aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--3rdParty/TinyXml/tinyxml2.cpp2101
-rw-r--r--3rdParty/TinyXml/tinyxml2.h1911
-rw-r--r--LibOVR/Include/OVR.h33
-rw-r--r--LibOVR/Include/OVRVersion.h22
-rw-r--r--LibOVR/Projects/Win32/LibOVR_Msvc2010.sln20
-rw-r--r--LibOVR/Projects/Win32/LibOVR_Msvc2010.vcxproj275
-rw-r--r--LibOVR/Projects/Win32/LibOVR_Msvc2010.vcxproj.filters186
-rw-r--r--LibOVR/Projects/libovr.txt83
-rw-r--r--LibOVR/Src/Kernel/OVR_Alg.cpp46
-rw-r--r--LibOVR/Src/Kernel/OVR_Alg.h953
-rw-r--r--LibOVR/Src/Kernel/OVR_Allocator.cpp84
-rw-r--r--LibOVR/Src/Kernel/OVR_Allocator.h336
-rw-r--r--LibOVR/Src/Kernel/OVR_Array.h793
-rw-r--r--LibOVR/Src/Kernel/OVR_Atomic.cpp82
-rw-r--r--LibOVR/Src/Kernel/OVR_Atomic.h859
-rw-r--r--LibOVR/Src/Kernel/OVR_Color.h55
-rw-r--r--LibOVR/Src/Kernel/OVR_ContainerAllocator.h256
-rw-r--r--LibOVR/Src/Kernel/OVR_File.cpp571
-rw-r--r--LibOVR/Src/Kernel/OVR_File.h518
-rw-r--r--LibOVR/Src/Kernel/OVR_FileFILE.cpp583
-rw-r--r--LibOVR/Src/Kernel/OVR_Hash.h1291
-rw-r--r--LibOVR/Src/Kernel/OVR_KeyCodes.h240
-rw-r--r--LibOVR/Src/Kernel/OVR_List.h325
-rw-r--r--LibOVR/Src/Kernel/OVR_Log.cpp173
-rw-r--r--LibOVR/Src/Kernel/OVR_Log.h193
-rw-r--r--LibOVR/Src/Kernel/OVR_Math.cpp153
-rw-r--r--LibOVR/Src/Kernel/OVR_Math.h1070
-rw-r--r--LibOVR/Src/Kernel/OVR_RefCount.cpp100
-rw-r--r--LibOVR/Src/Kernel/OVR_RefCount.h522
-rw-r--r--LibOVR/Src/Kernel/OVR_Std.cpp1025
-rw-r--r--LibOVR/Src/Kernel/OVR_Std.h503
-rw-r--r--LibOVR/Src/Kernel/OVR_String.cpp752
-rw-r--r--LibOVR/Src/Kernel/OVR_String.h645
-rw-r--r--LibOVR/Src/Kernel/OVR_StringHash.h89
-rw-r--r--LibOVR/Src/Kernel/OVR_String_FormatUtil.cpp42
-rw-r--r--LibOVR/Src/Kernel/OVR_String_PathUtil.cpp200
-rw-r--r--LibOVR/Src/Kernel/OVR_SysFile.cpp125
-rw-r--r--LibOVR/Src/Kernel/OVR_SysFile.h93
-rw-r--r--LibOVR/Src/Kernel/OVR_System.cpp70
-rw-r--r--LibOVR/Src/Kernel/OVR_System.h67
-rw-r--r--LibOVR/Src/Kernel/OVR_Threads.h396
-rw-r--r--LibOVR/Src/Kernel/OVR_ThreadsPthread.cpp795
-rw-r--r--LibOVR/Src/Kernel/OVR_ThreadsWinAPI.cpp994
-rw-r--r--LibOVR/Src/Kernel/OVR_Timer.cpp156
-rw-r--r--LibOVR/Src/Kernel/OVR_Timer.h100
-rw-r--r--LibOVR/Src/Kernel/OVR_Types.h456
-rw-r--r--LibOVR/Src/Kernel/OVR_UTF8Util.cpp545
-rw-r--r--LibOVR/Src/Kernel/OVR_UTF8Util.h88
-rw-r--r--LibOVR/Src/OVR_Device.h627
-rw-r--r--LibOVR/Src/OVR_DeviceConstants.h38
-rw-r--r--LibOVR/Src/OVR_DeviceHandle.cpp174
-rw-r--r--LibOVR/Src/OVR_DeviceHandle.h97
-rw-r--r--LibOVR/Src/OVR_DeviceImpl.cpp784
-rw-r--r--LibOVR/Src/OVR_DeviceImpl.h425
-rw-r--r--LibOVR/Src/OVR_DeviceMessages.h162
-rw-r--r--LibOVR/Src/OVR_HIDDevice.h143
-rw-r--r--LibOVR/Src/OVR_HIDDeviceBase.h40
-rw-r--r--LibOVR/Src/OVR_HIDDeviceImpl.h203
-rw-r--r--LibOVR/Src/OVR_LatencyTestImpl.cpp798
-rw-r--r--LibOVR/Src/OVR_LatencyTestImpl.h135
-rw-r--r--LibOVR/Src/OVR_OSX_DeviceManager.cpp349
-rw-r--r--LibOVR/Src/OVR_OSX_DeviceManager.h119
-rw-r--r--LibOVR/Src/OVR_OSX_HIDDevice.cpp899
-rw-r--r--LibOVR/Src/OVR_OSX_HIDDevice.h149
-rw-r--r--LibOVR/Src/OVR_OSX_HMDDevice.cpp326
-rw-r--r--LibOVR/Src/OVR_OSX_HMDDevice.h136
-rw-r--r--LibOVR/Src/OVR_OSX_SensorDevice.cpp45
-rw-r--r--LibOVR/Src/OVR_SensorFilter.cpp201
-rw-r--r--LibOVR/Src/OVR_SensorFilter.h99
-rw-r--r--LibOVR/Src/OVR_SensorFusion.cpp378
-rw-r--r--LibOVR/Src/OVR_SensorFusion.h268
-rw-r--r--LibOVR/Src/OVR_SensorImpl.cpp882
-rw-r--r--LibOVR/Src/OVR_SensorImpl.h208
-rw-r--r--LibOVR/Src/OVR_ThreadCommandQueue.cpp370
-rw-r--r--LibOVR/Src/OVR_ThreadCommandQueue.h308
-rw-r--r--LibOVR/Src/OVR_Win32_DeviceManager.cpp423
-rw-r--r--LibOVR/Src/OVR_Win32_DeviceManager.h146
-rw-r--r--LibOVR/Src/OVR_Win32_DeviceStatus.cpp350
-rw-r--r--LibOVR/Src/OVR_Win32_DeviceStatus.h101
-rw-r--r--LibOVR/Src/OVR_Win32_HIDDevice.cpp637
-rw-r--r--LibOVR/Src/OVR_Win32_HIDDevice.h194
-rw-r--r--LibOVR/Src/OVR_Win32_HMDDevice.cpp418
-rw-r--r--LibOVR/Src/OVR_Win32_HMDDevice.h133
-rw-r--r--LibOVR/Src/OVR_Win32_SensorDevice.cpp47
-rw-r--r--LibOVR/Src/OVR_Win32_SensorDevice.h24
-rw-r--r--LibOVR/Src/Util/Util_LatencyTest.cpp560
-rw-r--r--LibOVR/Src/Util/Util_LatencyTest.h160
-rw-r--r--LibOVR/Src/Util/Util_MagCalibration.cpp180
-rw-r--r--LibOVR/Src/Util/Util_MagCalibration.h115
-rw-r--r--LibOVR/Src/Util/Util_Render_Stereo.cpp311
-rw-r--r--LibOVR/Src/Util/Util_Render_Stereo.h299
-rw-r--r--Mac_OculusWorldDemo.app/Contents/Info.plist50
-rw-r--r--Mac_OculusWorldDemo.app/Contents/MacOS/OculusWorldDemobin0 -> 881004 bytes
-rw-r--r--Mac_OculusWorldDemo.app/Contents/PkgInfo1
-rw-r--r--Samples/CommonSrc/Makefile7
-rw-r--r--Samples/CommonSrc/Oculus.icobin0 -> 40177 bytes
-rw-r--r--Samples/CommonSrc/Platform/Gamepad.h102
-rw-r--r--Samples/CommonSrc/Platform/OSX_Gamepad.cpp424
-rw-r--r--Samples/CommonSrc/Platform/OSX_Gamepad.h67
-rw-r--r--Samples/CommonSrc/Platform/OSX_Platform.h80
-rw-r--r--Samples/CommonSrc/Platform/OSX_Platform.mm514
-rw-r--r--Samples/CommonSrc/Platform/OSX_PlatformObjc.h31
-rw-r--r--Samples/CommonSrc/Platform/OSX_WavPlayer.cpp242
-rw-r--r--Samples/CommonSrc/Platform/OSX_WavPlayer.h64
-rw-r--r--Samples/CommonSrc/Platform/Platform.cpp75
-rw-r--r--Samples/CommonSrc/Platform/Platform.h190
-rw-r--r--Samples/CommonSrc/Platform/Platform_Default.h59
-rw-r--r--Samples/CommonSrc/Platform/Win32_Gamepad.cpp101
-rw-r--r--Samples/CommonSrc/Platform/Win32_Gamepad.h54
-rw-r--r--Samples/CommonSrc/Platform/Win32_Platform.cpp600
-rw-r--r--Samples/CommonSrc/Platform/Win32_Platform.h101
-rw-r--r--Samples/CommonSrc/Render/Render_D3D10_Device.cpp26
-rw-r--r--Samples/CommonSrc/Render/Render_D3D10_Device.h26
-rw-r--r--Samples/CommonSrc/Render/Render_D3D11_Device.cpp26
-rw-r--r--Samples/CommonSrc/Render/Render_D3D11_Device.h26
-rw-r--r--Samples/CommonSrc/Render/Render_D3D1X_Device.cpp1831
-rw-r--r--Samples/CommonSrc/Render/Render_D3D1X_Device.h360
-rw-r--r--Samples/CommonSrc/Render/Render_Device.cpp1033
-rw-r--r--Samples/CommonSrc/Render/Render_Device.h897
-rw-r--r--Samples/CommonSrc/Render/Render_Font.h51
-rw-r--r--Samples/CommonSrc/Render/Render_FontEmbed_DejaVu48.h9463
-rw-r--r--Samples/CommonSrc/Render/Render_GL_Device.cpp849
-rw-r--r--Samples/CommonSrc/Render/Render_GL_Device.h230
-rw-r--r--Samples/CommonSrc/Render/Render_GL_Win32_Device.cpp90
-rw-r--r--Samples/CommonSrc/Render/Render_GL_Win32_Device.h59
-rw-r--r--Samples/CommonSrc/Render/Render_LoadTextureDDS.cpp121
-rw-r--r--Samples/CommonSrc/Render/Render_LoadTextureTGA.cpp102
-rw-r--r--Samples/CommonSrc/Render/Render_XmlSceneLoader.cpp390
-rw-r--r--Samples/CommonSrc/Render/Render_XmlSceneLoader.h74
-rw-r--r--Samples/LibOVR_Samples_Msvc2010.sln43
-rw-r--r--Samples/LibOVR_With_Samples.xcodeproj/OculusRoomTiny-Info.plist34
-rw-r--r--Samples/LibOVR_With_Samples.xcodeproj/OculusWorldDemo-Info.plist34
-rw-r--r--Samples/LibOVR_With_Samples.xcodeproj/SensorBoxTest-Info.plist34
-rw-r--r--Samples/LibOVR_With_Samples.xcodeproj/project.pbxproj1092
-rw-r--r--Samples/LibOVR_With_Samples_Msvc2010.sln67
-rw-r--r--Samples/Oculus.icnsbin0 -> 144958 bytes
-rw-r--r--Samples/OculusRoomTiny/OSX_OculusRoomTiny.h223
-rw-r--r--Samples/OculusRoomTiny/OSX_OculusRoomTiny.mm861
-rw-r--r--Samples/OculusRoomTiny/OculusRoomModel.cpp260
-rw-r--r--Samples/OculusRoomTiny/OculusRoomTiny.rcbin0 -> 144 bytes
-rw-r--r--Samples/OculusRoomTiny/OculusRoomTiny_Msvc2010.vcxproj187
-rw-r--r--Samples/OculusRoomTiny/OculusRoomTiny_Msvc2010.vcxproj.filters17
-rw-r--r--Samples/OculusRoomTiny/RenderTiny_D3D1X_Device.cpp1311
-rw-r--r--Samples/OculusRoomTiny/RenderTiny_D3D1X_Device.h273
-rw-r--r--Samples/OculusRoomTiny/RenderTiny_Device.cpp442
-rw-r--r--Samples/OculusRoomTiny/RenderTiny_Device.h724
-rw-r--r--Samples/OculusRoomTiny/RenderTiny_GL_Device.cpp784
-rw-r--r--Samples/OculusRoomTiny/RenderTiny_GL_Device.h228
-rw-r--r--Samples/OculusRoomTiny/Win32_OculusRoomTiny.cpp712
-rw-r--r--Samples/OculusRoomTiny/Win32_OculusRoomTiny.h189
-rw-r--r--Samples/OculusWorldDemo/Makefile5
-rw-r--r--Samples/OculusWorldDemo/OculusWorldDemo.cpp1836
-rw-r--r--Samples/OculusWorldDemo/OculusWorldDemo.rcbin0 -> 144 bytes
-rw-r--r--Samples/OculusWorldDemo/OculusWorldDemo_Msvc2010.vcxproj211
-rw-r--r--Samples/OculusWorldDemo/OculusWorldDemo_Msvc2010.vcxproj.filters89
-rw-r--r--Samples/OculusWorldDemo/Player.cpp166
-rw-r--r--Samples/OculusWorldDemo/Player.h79
-rw-r--r--Samples/SensorBox/SensorBoxTest.cpp506
-rw-r--r--Samples/SensorBox/SensorBoxTest.rcbin0 -> 144 bytes
-rw-r--r--Samples/SensorBox/SensorBoxTest_Msvc2010.vcxproj126
-rw-r--r--Samples/SensorBox/SensorBoxTest_Msvc2010.vcxproj.filters73
-rw-r--r--Win_OculusWorldDemo.lnkbin0 -> 2349 bytes
-rw-r--r--readme.txt6
163 files changed, 62464 insertions, 0 deletions
diff --git a/3rdParty/TinyXml/tinyxml2.cpp b/3rdParty/TinyXml/tinyxml2.cpp
new file mode 100644
index 0000000..647901b
--- /dev/null
+++ b/3rdParty/TinyXml/tinyxml2.cpp
@@ -0,0 +1,2101 @@
+/*
+Original code by Lee Thomason (www.grinninglizard.com)
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any
+damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any
+purpose, including commercial applications, and to alter it and
+redistribute it freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must
+not claim that you wrote the original software. If you use this
+software in a product, an acknowledgment in the product documentation
+would be appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and
+must not be misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source
+distribution.
+*/
+
+#include "tinyxml2.h"
+
+#include <new> // yes, this one new style header, is in the Android SDK.
+# ifdef ANDROID_NDK
+# include <stddef.h>
+#else
+# include <cstddef>
+#endif
+
+static const char LINE_FEED = (char)0x0a; // all line endings are normalized to LF
+static const char LF = LINE_FEED;
+static const char CARRIAGE_RETURN = (char)0x0d; // CR gets filtered out
+static const char CR = CARRIAGE_RETURN;
+static const char SINGLE_QUOTE = '\'';
+static const char DOUBLE_QUOTE = '\"';
+
+// Bunch of unicode info at:
+// http://www.unicode.org/faq/utf_bom.html
+// ef bb bf (Microsoft "lead bytes") - designates UTF-8
+
+static const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
+static const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
+static const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
+
+
+#define DELETE_NODE( node ) { \
+ if ( node ) { \
+ MemPool* pool = node->_memPool; \
+ node->~XMLNode(); \
+ pool->Free( node ); \
+ } \
+ }
+#define DELETE_ATTRIBUTE( attrib ) { \
+ if ( attrib ) { \
+ MemPool* pool = attrib->_memPool; \
+ attrib->~XMLAttribute(); \
+ pool->Free( attrib ); \
+ } \
+ }
+
+namespace tinyxml2
+{
+
+struct Entity {
+ const char* pattern;
+ int length;
+ char value;
+};
+
+static const int NUM_ENTITIES = 5;
+static const Entity entities[NUM_ENTITIES] = {
+ { "quot", 4, DOUBLE_QUOTE },
+ { "amp", 3, '&' },
+ { "apos", 4, SINGLE_QUOTE },
+ { "lt", 2, '<' },
+ { "gt", 2, '>' }
+};
+
+
+StrPair::~StrPair()
+{
+ Reset();
+}
+
+
+void StrPair::Reset()
+{
+ if ( _flags & NEEDS_DELETE ) {
+ delete [] _start;
+ }
+ _flags = 0;
+ _start = 0;
+ _end = 0;
+}
+
+
+void StrPair::SetStr( const char* str, int flags )
+{
+ Reset();
+ size_t len = strlen( str );
+ _start = new char[ len+1 ];
+ memcpy( _start, str, len+1 );
+ _end = _start + len;
+ _flags = flags | NEEDS_DELETE;
+}
+
+
+char* StrPair::ParseText( char* p, const char* endTag, int strFlags )
+{
+ TIXMLASSERT( endTag && *endTag );
+
+ char* start = p; // fixme: hides a member
+ char endChar = *endTag;
+ size_t length = strlen( endTag );
+
+ // Inner loop of text parsing.
+ while ( *p ) {
+ if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) {
+ Set( start, p, strFlags );
+ return p + length;
+ }
+ ++p;
+ }
+ return 0;
+}
+
+
+char* StrPair::ParseName( char* p )
+{
+ char* start = p;
+
+ if ( !start || !(*start) ) {
+ return 0;
+ }
+
+ while( *p && (
+ XMLUtil::IsAlphaNum( (unsigned char) *p )
+ || *p == '_'
+ || *p == ':'
+ || (*p == '-' && p>start ) // can be in a name, but not lead it.
+ || (*p == '.' && p>start ) )) { // can be in a name, but not lead it.
+ ++p;
+ }
+
+ if ( p > start ) {
+ Set( start, p, 0 );
+ return p;
+ }
+ return 0;
+}
+
+
+void StrPair::CollapseWhitespace()
+{
+ // Trim leading space.
+ _start = XMLUtil::SkipWhiteSpace( _start );
+
+ if ( _start && *_start ) {
+ char* p = _start; // the read pointer
+ char* q = _start; // the write pointer
+
+ while( *p ) {
+ if ( XMLUtil::IsWhiteSpace( *p )) {
+ p = XMLUtil::SkipWhiteSpace( p );
+ if ( *p == 0 ) {
+ break; // don't write to q; this trims the trailing space.
+ }
+ *q = ' ';
+ ++q;
+ }
+ *q = *p;
+ ++q;
+ ++p;
+ }
+ *q = 0;
+ }
+}
+
+
+const char* StrPair::GetStr()
+{
+ if ( _flags & NEEDS_FLUSH ) {
+ *_end = 0;
+ _flags ^= NEEDS_FLUSH;
+
+ if ( _flags ) {
+ char* p = _start; // the read pointer
+ char* q = _start; // the write pointer
+
+ while( p < _end ) {
+ if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) {
+ // CR-LF pair becomes LF
+ // CR alone becomes LF
+ // LF-CR becomes LF
+ if ( *(p+1) == LF ) {
+ p += 2;
+ }
+ else {
+ ++p;
+ }
+ *q++ = LF;
+ }
+ else if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) {
+ if ( *(p+1) == CR ) {
+ p += 2;
+ }
+ else {
+ ++p;
+ }
+ *q++ = LF;
+ }
+ else if ( (_flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) {
+ // Entities handled by tinyXML2:
+ // - special entities in the entity table [in/out]
+ // - numeric character reference [in]
+ // &#20013; or &#x4e2d;
+
+ if ( *(p+1) == '#' ) {
+ char buf[10] = { 0 };
+ int len;
+ p = const_cast<char*>( XMLUtil::GetCharacterRef( p, buf, &len ) );
+ for( int i=0; i<len; ++i ) {
+ *q++ = buf[i];
+ }
+ TIXMLASSERT( q <= p );
+ }
+ else {
+ int i=0;
+ for(; i<NUM_ENTITIES; ++i ) {
+ if ( strncmp( p+1, entities[i].pattern, entities[i].length ) == 0
+ && *(p+entities[i].length+1) == ';' ) {
+ // Found an entity convert;
+ *q = entities[i].value;
+ ++q;
+ p += entities[i].length + 2;
+ break;
+ }
+ }
+ if ( i == NUM_ENTITIES ) {
+ // fixme: treat as error?
+ ++p;
+ ++q;
+ }
+ }
+ }
+ else {
+ *q = *p;
+ ++p;
+ ++q;
+ }
+ }
+ *q = 0;
+ }
+ // The loop below has plenty going on, and this
+ // is a less useful mode. Break it out.
+ if ( _flags & COLLAPSE_WHITESPACE ) {
+ CollapseWhitespace();
+ }
+ _flags = (_flags & NEEDS_DELETE);
+ }
+ return _start;
+}
+
+
+
+
+// --------- XMLUtil ----------- //
+
+const char* XMLUtil::ReadBOM( const char* p, bool* bom )
+{
+ *bom = false;
+ const unsigned char* pu = reinterpret_cast<const unsigned char*>(p);
+ // Check for BOM:
+ if ( *(pu+0) == TIXML_UTF_LEAD_0
+ && *(pu+1) == TIXML_UTF_LEAD_1
+ && *(pu+2) == TIXML_UTF_LEAD_2 ) {
+ *bom = true;
+ p += 3;
+ }
+ return p;
+}
+
+
+void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length )
+{
+ const unsigned long BYTE_MASK = 0xBF;
+ const unsigned long BYTE_MARK = 0x80;
+ const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
+
+ if (input < 0x80) {
+ *length = 1;
+ }
+ else if ( input < 0x800 ) {
+ *length = 2;
+ }
+ else if ( input < 0x10000 ) {
+ *length = 3;
+ }
+ else if ( input < 0x200000 ) {
+ *length = 4;
+ }
+ else {
+ *length = 0; // This code won't covert this correctly anyway.
+ return;
+ }
+
+ output += *length;
+
+ // Scary scary fall throughs.
+ switch (*length) {
+ case 4:
+ --output;
+ *output = (char)((input | BYTE_MARK) & BYTE_MASK);
+ input >>= 6;
+ case 3:
+ --output;
+ *output = (char)((input | BYTE_MARK) & BYTE_MASK);
+ input >>= 6;
+ case 2:
+ --output;
+ *output = (char)((input | BYTE_MARK) & BYTE_MASK);
+ input >>= 6;
+ case 1:
+ --output;
+ *output = (char)(input | FIRST_BYTE_MARK[*length]);
+ default:
+ break;
+ }
+}
+
+
+const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length )
+{
+ // Presume an entity, and pull it out.
+ *length = 0;
+
+ if ( *(p+1) == '#' && *(p+2) ) {
+ unsigned long ucs = 0;
+ ptrdiff_t delta = 0;
+ unsigned mult = 1;
+
+ if ( *(p+2) == 'x' ) {
+ // Hexadecimal.
+ if ( !*(p+3) ) {
+ return 0;
+ }
+
+ const char* q = p+3;
+ q = strchr( q, ';' );
+
+ if ( !q || !*q ) {
+ return 0;
+ }
+
+ delta = q-p;
+ --q;
+
+ while ( *q != 'x' ) {
+ if ( *q >= '0' && *q <= '9' ) {
+ ucs += mult * (*q - '0');
+ }
+ else if ( *q >= 'a' && *q <= 'f' ) {
+ ucs += mult * (*q - 'a' + 10);
+ }
+ else if ( *q >= 'A' && *q <= 'F' ) {
+ ucs += mult * (*q - 'A' + 10 );
+ }
+ else {
+ return 0;
+ }
+ mult *= 16;
+ --q;
+ }
+ }
+ else {
+ // Decimal.
+ if ( !*(p+2) ) {
+ return 0;
+ }
+
+ const char* q = p+2;
+ q = strchr( q, ';' );
+
+ if ( !q || !*q ) {
+ return 0;
+ }
+
+ delta = q-p;
+ --q;
+
+ while ( *q != '#' ) {
+ if ( *q >= '0' && *q <= '9' ) {
+ ucs += mult * (*q - '0');
+ }
+ else {
+ return 0;
+ }
+ mult *= 10;
+ --q;
+ }
+ }
+ // convert the UCS to UTF-8
+ ConvertUTF32ToUTF8( ucs, value, length );
+ return p + delta + 1;
+ }
+ return p+1;
+}
+
+
+void XMLUtil::ToStr( int v, char* buffer, int bufferSize )
+{
+ TIXML_SNPRINTF( buffer, bufferSize, "%d", v );
+}
+
+
+void XMLUtil::ToStr( unsigned v, char* buffer, int bufferSize )
+{
+ TIXML_SNPRINTF( buffer, bufferSize, "%u", v );
+}
+
+
+void XMLUtil::ToStr( bool v, char* buffer, int bufferSize )
+{
+ TIXML_SNPRINTF( buffer, bufferSize, "%d", v ? 1 : 0 );
+}
+
+
+void XMLUtil::ToStr( float v, char* buffer, int bufferSize )
+{
+ TIXML_SNPRINTF( buffer, bufferSize, "%g", v );
+}
+
+
+void XMLUtil::ToStr( double v, char* buffer, int bufferSize )
+{
+ TIXML_SNPRINTF( buffer, bufferSize, "%g", v );
+}
+
+
+bool XMLUtil::ToInt( const char* str, int* value )
+{
+ if ( TIXML_SSCANF( str, "%d", value ) == 1 ) {
+ return true;
+ }
+ return false;
+}
+
+bool XMLUtil::ToUnsigned( const char* str, unsigned *value )
+{
+ if ( TIXML_SSCANF( str, "%u", value ) == 1 ) {
+ return true;
+ }
+ return false;
+}
+
+bool XMLUtil::ToBool( const char* str, bool* value )
+{
+ int ival = 0;
+ if ( ToInt( str, &ival )) {
+ *value = (ival==0) ? false : true;
+ return true;
+ }
+ if ( StringEqual( str, "true" ) ) {
+ *value = true;
+ return true;
+ }
+ else if ( StringEqual( str, "false" ) ) {
+ *value = false;
+ return true;
+ }
+ return false;
+}
+
+
+bool XMLUtil::ToFloat( const char* str, float* value )
+{
+ if ( TIXML_SSCANF( str, "%f", value ) == 1 ) {
+ return true;
+ }
+ return false;
+}
+
+bool XMLUtil::ToDouble( const char* str, double* value )
+{
+ if ( TIXML_SSCANF( str, "%lf", value ) == 1 ) {
+ return true;
+ }
+ return false;
+}
+
+
+char* XMLDocument::Identify( char* p, XMLNode** node )
+{
+ XMLNode* returnNode = 0;
+ char* start = p;
+ p = XMLUtil::SkipWhiteSpace( p );
+ if( !p || !*p ) {
+ return p;
+ }
+
+ // What is this thing?
+ // - Elements start with a letter or underscore, but xml is reserved.
+ // - Comments: <!--
+ // - Decleration: <?
+ // - Everthing else is unknown to tinyxml.
+ //
+
+ static const char* xmlHeader = { "<?" };
+ static const char* commentHeader = { "<!--" };
+ static const char* dtdHeader = { "<!" };
+ static const char* cdataHeader = { "<![CDATA[" };
+ static const char* elementHeader = { "<" }; // and a header for everything else; check last.
+
+ static const int xmlHeaderLen = 2;
+ static const int commentHeaderLen = 4;
+ static const int dtdHeaderLen = 2;
+ static const int cdataHeaderLen = 9;
+ static const int elementHeaderLen = 1;
+
+#if defined(_MSC_VER)
+#pragma warning ( push )
+#pragma warning ( disable : 4127 )
+#endif
+ TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLUnknown ) ); // use same memory pool
+ TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLDeclaration ) ); // use same memory pool
+#if defined(_MSC_VER)
+#pragma warning (pop)
+#endif
+ if ( XMLUtil::StringEqual( p, xmlHeader, xmlHeaderLen ) ) {
+ returnNode = new (_commentPool.Alloc()) XMLDeclaration( this );
+ returnNode->_memPool = &_commentPool;
+ p += xmlHeaderLen;
+ }
+ else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) {
+ returnNode = new (_commentPool.Alloc()) XMLComment( this );
+ returnNode->_memPool = &_commentPool;
+ p += commentHeaderLen;
+ }
+ else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) {
+ XMLText* text = new (_textPool.Alloc()) XMLText( this );
+ returnNode = text;
+ returnNode->_memPool = &_textPool;
+ p += cdataHeaderLen;
+ text->SetCData( true );
+ }
+ else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) {
+ returnNode = new (_commentPool.Alloc()) XMLUnknown( this );
+ returnNode->_memPool = &_commentPool;
+ p += dtdHeaderLen;
+ }
+ else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) {
+ returnNode = new (_elementPool.Alloc()) XMLElement( this );
+ returnNode->_memPool = &_elementPool;
+ p += elementHeaderLen;
+ }
+ else {
+ returnNode = new (_textPool.Alloc()) XMLText( this );
+ returnNode->_memPool = &_textPool;
+ p = start; // Back it up, all the text counts.
+ }
+
+ *node = returnNode;
+ return p;
+}
+
+
+bool XMLDocument::Accept( XMLVisitor* visitor ) const
+{
+ if ( visitor->VisitEnter( *this ) ) {
+ for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {
+ if ( !node->Accept( visitor ) ) {
+ break;
+ }
+ }
+ }
+ return visitor->VisitExit( *this );
+}
+
+
+// --------- XMLNode ----------- //
+
+XMLNode::XMLNode( XMLDocument* doc ) :
+ _document( doc ),
+ _parent( 0 ),
+ _firstChild( 0 ), _lastChild( 0 ),
+ _prev( 0 ), _next( 0 )
+{
+}
+
+
+XMLNode::~XMLNode()
+{
+ DeleteChildren();
+ if ( _parent ) {
+ _parent->Unlink( this );
+ }
+}
+
+
+void XMLNode::SetValue( const char* str, bool staticMem )
+{
+ if ( staticMem ) {
+ _value.SetInternedStr( str );
+ }
+ else {
+ _value.SetStr( str );
+ }
+}
+
+
+void XMLNode::DeleteChildren()
+{
+ while( _firstChild ) {
+ XMLNode* node = _firstChild;
+ Unlink( node );
+
+ DELETE_NODE( node );
+ }
+ _firstChild = _lastChild = 0;
+}
+
+
+void XMLNode::Unlink( XMLNode* child )
+{
+ TIXMLASSERT( child->_parent == this );
+ if ( child == _firstChild ) {
+ _firstChild = _firstChild->_next;
+ }
+ if ( child == _lastChild ) {
+ _lastChild = _lastChild->_prev;
+ }
+
+ if ( child->_prev ) {
+ child->_prev->_next = child->_next;
+ }
+ if ( child->_next ) {
+ child->_next->_prev = child->_prev;
+ }
+ child->_parent = 0;
+}
+
+
+void XMLNode::DeleteChild( XMLNode* node )
+{
+ TIXMLASSERT( node->_parent == this );
+ DELETE_NODE( node );
+}
+
+
+XMLNode* XMLNode::InsertEndChild( XMLNode* addThis )
+{
+ if ( _lastChild ) {
+ TIXMLASSERT( _firstChild );
+ TIXMLASSERT( _lastChild->_next == 0 );
+ _lastChild->_next = addThis;
+ addThis->_prev = _lastChild;
+ _lastChild = addThis;
+
+ addThis->_next = 0;
+ }
+ else {
+ TIXMLASSERT( _firstChild == 0 );
+ _firstChild = _lastChild = addThis;
+
+ addThis->_prev = 0;
+ addThis->_next = 0;
+ }
+ addThis->_parent = this;
+ addThis->_memPool->SetTracked();
+ return addThis;
+}
+
+
+XMLNode* XMLNode::InsertFirstChild( XMLNode* addThis )
+{
+ if ( _firstChild ) {
+ TIXMLASSERT( _lastChild );
+ TIXMLASSERT( _firstChild->_prev == 0 );
+
+ _firstChild->_prev = addThis;
+ addThis->_next = _firstChild;
+ _firstChild = addThis;
+
+ addThis->_prev = 0;
+ }
+ else {
+ TIXMLASSERT( _lastChild == 0 );
+ _firstChild = _lastChild = addThis;
+
+ addThis->_prev = 0;
+ addThis->_next = 0;
+ }
+ addThis->_parent = this;
+ addThis->_memPool->SetTracked();
+ return addThis;
+}
+
+
+XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis )
+{
+ TIXMLASSERT( afterThis->_parent == this );
+ if ( afterThis->_parent != this ) {
+ return 0;
+ }
+
+ if ( afterThis->_next == 0 ) {
+ // The last node or the only node.
+ return InsertEndChild( addThis );
+ }
+ addThis->_prev = afterThis;
+ addThis->_next = afterThis->_next;
+ afterThis->_next->_prev = addThis;
+ afterThis->_next = addThis;
+ addThis->_parent = this;
+ addThis->_memPool->SetTracked();
+ return addThis;
+}
+
+
+
+
+const XMLElement* XMLNode::FirstChildElement( const char* value ) const
+{
+ for( XMLNode* node=_firstChild; node; node=node->_next ) {
+ XMLElement* element = node->ToElement();
+ if ( element ) {
+ if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) {
+ return element;
+ }
+ }
+ }
+ return 0;
+}
+
+
+const XMLElement* XMLNode::LastChildElement( const char* value ) const
+{
+ for( XMLNode* node=_lastChild; node; node=node->_prev ) {
+ XMLElement* element = node->ToElement();
+ if ( element ) {
+ if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) {
+ return element;
+ }
+ }
+ }
+ return 0;
+}
+
+
+const XMLElement* XMLNode::NextSiblingElement( const char* value ) const
+{
+ for( XMLNode* element=this->_next; element; element = element->_next ) {
+ if ( element->ToElement()
+ && (!value || XMLUtil::StringEqual( value, element->Value() ))) {
+ return element->ToElement();
+ }
+ }
+ return 0;
+}
+
+
+const XMLElement* XMLNode::PreviousSiblingElement( const char* value ) const
+{
+ for( XMLNode* element=_prev; element; element = element->_prev ) {
+ if ( element->ToElement()
+ && (!value || XMLUtil::StringEqual( value, element->Value() ))) {
+ return element->ToElement();
+ }
+ }
+ return 0;
+}
+
+
+char* XMLNode::ParseDeep( char* p, StrPair* parentEnd )
+{
+ // This is a recursive method, but thinking about it "at the current level"
+ // it is a pretty simple flat list:
+ // <foo/>
+ // <!-- comment -->
+ //
+ // With a special case:
+ // <foo>
+ // </foo>
+ // <!-- comment -->
+ //
+ // Where the closing element (/foo) *must* be the next thing after the opening
+ // element, and the names must match. BUT the tricky bit is that the closing
+ // element will be read by the child.
+ //
+ // 'endTag' is the end tag for this node, it is returned by a call to a child.
+ // 'parentEnd' is the end tag for the parent, which is filled in and returned.
+
+ while( p && *p ) {
+ XMLNode* node = 0;
+
+ p = _document->Identify( p, &node );
+ if ( p == 0 || node == 0 ) {
+ break;
+ }
+
+ StrPair endTag;
+ p = node->ParseDeep( p, &endTag );
+ if ( !p ) {
+ DELETE_NODE( node );
+ node = 0;
+ if ( !_document->Error() ) {
+ _document->SetError( XML_ERROR_PARSING, 0, 0 );
+ }
+ break;
+ }
+
+ // We read the end tag. Return it to the parent.
+ if ( node->ToElement() && node->ToElement()->ClosingType() == XMLElement::CLOSING ) {
+ if ( parentEnd ) {
+ *parentEnd = static_cast<XMLElement*>(node)->_value;
+ }
+ node->_memPool->SetTracked(); // created and then immediately deleted.
+ DELETE_NODE( node );
+ return p;
+ }
+
+ // Handle an end tag returned to this level.
+ // And handle a bunch of annoying errors.
+ XMLElement* ele = node->ToElement();
+ if ( ele ) {
+ if ( endTag.Empty() && ele->ClosingType() == XMLElement::OPEN ) {
+ _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 );
+ p = 0;
+ }
+ else if ( !endTag.Empty() && ele->ClosingType() != XMLElement::OPEN ) {
+ _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 );
+ p = 0;
+ }
+ else if ( !endTag.Empty() ) {
+ if ( !XMLUtil::StringEqual( endTag.GetStr(), node->Value() )) {
+ _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 );
+ p = 0;
+ }
+ }
+ }
+ if ( p == 0 ) {
+ DELETE_NODE( node );
+ node = 0;
+ }
+ if ( node ) {
+ this->InsertEndChild( node );
+ }
+ }
+ return 0;
+}
+
+// --------- XMLText ---------- //
+char* XMLText::ParseDeep( char* p, StrPair* )
+{
+ const char* start = p;
+ if ( this->CData() ) {
+ p = _value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION );
+ if ( !p ) {
+ _document->SetError( XML_ERROR_PARSING_CDATA, start, 0 );
+ }
+ return p;
+ }
+ else {
+ int flags = _document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES;
+ if ( _document->WhitespaceMode() == COLLAPSE_WHITESPACE ) {
+ flags |= StrPair::COLLAPSE_WHITESPACE;
+ }
+
+ p = _value.ParseText( p, "<", flags );
+ if ( !p ) {
+ _document->SetError( XML_ERROR_PARSING_TEXT, start, 0 );
+ }
+ if ( p && *p ) {
+ return p-1;
+ }
+ }
+ return 0;
+}
+
+
+XMLNode* XMLText::ShallowClone( XMLDocument* doc ) const
+{
+ if ( !doc ) {
+ doc = _document;
+ }
+ XMLText* text = doc->NewText( Value() ); // fixme: this will always allocate memory. Intern?
+ text->SetCData( this->CData() );
+ return text;
+}
+
+
+bool XMLText::ShallowEqual( const XMLNode* compare ) const
+{
+ return ( compare->ToText() && XMLUtil::StringEqual( compare->ToText()->Value(), Value() ));
+}
+
+
+bool XMLText::Accept( XMLVisitor* visitor ) const
+{
+ return visitor->Visit( *this );
+}
+
+
+// --------- XMLComment ---------- //
+
+XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc )
+{
+}
+
+
+XMLComment::~XMLComment()
+{
+}
+
+
+char* XMLComment::ParseDeep( char* p, StrPair* )
+{
+ // Comment parses as text.
+ const char* start = p;
+ p = _value.ParseText( p, "-->", StrPair::COMMENT );
+ if ( p == 0 ) {
+ _document->SetError( XML_ERROR_PARSING_COMMENT, start, 0 );
+ }
+ return p;
+}
+
+
+XMLNode* XMLComment::ShallowClone( XMLDocument* doc ) const
+{
+ if ( !doc ) {
+ doc = _document;
+ }
+ XMLComment* comment = doc->NewComment( Value() ); // fixme: this will always allocate memory. Intern?
+ return comment;
+}
+
+
+bool XMLComment::ShallowEqual( const XMLNode* compare ) const
+{
+ return ( compare->ToComment() && XMLUtil::StringEqual( compare->ToComment()->Value(), Value() ));
+}
+
+
+bool XMLComment::Accept( XMLVisitor* visitor ) const
+{
+ return visitor->Visit( *this );
+}
+
+
+// --------- XMLDeclaration ---------- //
+
+XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc )
+{
+}
+
+
+XMLDeclaration::~XMLDeclaration()
+{
+ //printf( "~XMLDeclaration\n" );
+}
+
+
+char* XMLDeclaration::ParseDeep( char* p, StrPair* )
+{
+ // Declaration parses as text.
+ const char* start = p;
+ p = _value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION );
+ if ( p == 0 ) {
+ _document->SetError( XML_ERROR_PARSING_DECLARATION, start, 0 );
+ }
+ return p;
+}
+
+
+XMLNode* XMLDeclaration::ShallowClone( XMLDocument* doc ) const
+{
+ if ( !doc ) {
+ doc = _document;
+ }
+ XMLDeclaration* dec = doc->NewDeclaration( Value() ); // fixme: this will always allocate memory. Intern?
+ return dec;
+}
+
+
+bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const
+{
+ return ( compare->ToDeclaration() && XMLUtil::StringEqual( compare->ToDeclaration()->Value(), Value() ));
+}
+
+
+
+bool XMLDeclaration::Accept( XMLVisitor* visitor ) const
+{
+ return visitor->Visit( *this );
+}
+
+// --------- XMLUnknown ---------- //
+
+XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc )
+{
+}
+
+
+XMLUnknown::~XMLUnknown()
+{
+}
+
+
+char* XMLUnknown::ParseDeep( char* p, StrPair* )
+{
+ // Unknown parses as text.
+ const char* start = p;
+
+ p = _value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION );
+ if ( !p ) {
+ _document->SetError( XML_ERROR_PARSING_UNKNOWN, start, 0 );
+ }
+ return p;
+}
+
+
+XMLNode* XMLUnknown::ShallowClone( XMLDocument* doc ) const
+{
+ if ( !doc ) {
+ doc = _document;
+ }
+ XMLUnknown* text = doc->NewUnknown( Value() ); // fixme: this will always allocate memory. Intern?
+ return text;
+}
+
+
+bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const
+{
+ return ( compare->ToUnknown() && XMLUtil::StringEqual( compare->ToUnknown()->Value(), Value() ));
+}
+
+
+bool XMLUnknown::Accept( XMLVisitor* visitor ) const
+{
+ return visitor->Visit( *this );
+}
+
+// --------- XMLAttribute ---------- //
+char* XMLAttribute::ParseDeep( char* p, bool processEntities )
+{
+ // Parse using the name rules: bug fix, was using ParseText before
+ p = _name.ParseName( p );
+ if ( !p || !*p ) {
+ return 0;
+ }
+
+ // Skip white space before =
+ p = XMLUtil::SkipWhiteSpace( p );
+ if ( !p || *p != '=' ) {
+ return 0;
+ }
+
+ ++p; // move up to opening quote
+ p = XMLUtil::SkipWhiteSpace( p );
+ if ( *p != '\"' && *p != '\'' ) {
+ return 0;
+ }
+
+ char endTag[2] = { *p, 0 };
+ ++p; // move past opening quote
+
+ p = _value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES );
+ return p;
+}
+
+
+void XMLAttribute::SetName( const char* n )
+{
+ _name.SetStr( n );
+}
+
+
+XMLError XMLAttribute::QueryIntValue( int* value ) const
+{
+ if ( XMLUtil::ToInt( Value(), value )) {
+ return XML_NO_ERROR;
+ }
+ return XML_WRONG_ATTRIBUTE_TYPE;
+}
+
+
+XMLError XMLAttribute::QueryUnsignedValue( unsigned int* value ) const
+{
+ if ( XMLUtil::ToUnsigned( Value(), value )) {
+ return XML_NO_ERROR;
+ }
+ return XML_WRONG_ATTRIBUTE_TYPE;
+}
+
+
+XMLError XMLAttribute::QueryBoolValue( bool* value ) const
+{
+ if ( XMLUtil::ToBool( Value(), value )) {
+ return XML_NO_ERROR;
+ }
+ return XML_WRONG_ATTRIBUTE_TYPE;
+}
+
+
+XMLError XMLAttribute::QueryFloatValue( float* value ) const
+{
+ if ( XMLUtil::ToFloat( Value(), value )) {
+ return XML_NO_ERROR;
+ }
+ return XML_WRONG_ATTRIBUTE_TYPE;
+}
+
+
+XMLError XMLAttribute::QueryDoubleValue( double* value ) const
+{
+ if ( XMLUtil::ToDouble( Value(), value )) {
+ return XML_NO_ERROR;
+ }
+ return XML_WRONG_ATTRIBUTE_TYPE;
+}
+
+
+void XMLAttribute::SetAttribute( const char* v )
+{
+ _value.SetStr( v );
+}
+
+
+void XMLAttribute::SetAttribute( int v )
+{
+ char buf[BUF_SIZE];
+ XMLUtil::ToStr( v, buf, BUF_SIZE );
+ _value.SetStr( buf );
+}
+
+
+void XMLAttribute::SetAttribute( unsigned v )
+{
+ char buf[BUF_SIZE];
+ XMLUtil::ToStr( v, buf, BUF_SIZE );
+ _value.SetStr( buf );
+}
+
+
+void XMLAttribute::SetAttribute( bool v )
+{
+ char buf[BUF_SIZE];
+ XMLUtil::ToStr( v, buf, BUF_SIZE );
+ _value.SetStr( buf );
+}
+
+void XMLAttribute::SetAttribute( double v )
+{
+ char buf[BUF_SIZE];
+ XMLUtil::ToStr( v, buf, BUF_SIZE );
+ _value.SetStr( buf );
+}
+
+void XMLAttribute::SetAttribute( float v )
+{
+ char buf[BUF_SIZE];
+ XMLUtil::ToStr( v, buf, BUF_SIZE );
+ _value.SetStr( buf );
+}
+
+
+// --------- XMLElement ---------- //
+XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ),
+ _closingType( 0 ),
+ _rootAttribute( 0 )
+{
+}
+
+
+XMLElement::~XMLElement()
+{
+ while( _rootAttribute ) {
+ XMLAttribute* next = _rootAttribute->_next;
+ DELETE_ATTRIBUTE( _rootAttribute );
+ _rootAttribute = next;
+ }
+}
+
+
+XMLAttribute* XMLElement::FindAttribute( const char* name )
+{
+ XMLAttribute* a = 0;
+ for( a=_rootAttribute; a; a = a->_next ) {
+ if ( XMLUtil::StringEqual( a->Name(), name ) ) {
+ return a;
+ }
+ }
+ return 0;
+}
+
+
+const XMLAttribute* XMLElement::FindAttribute( const char* name ) const
+{
+ XMLAttribute* a = 0;
+ for( a=_rootAttribute; a; a = a->_next ) {
+ if ( XMLUtil::StringEqual( a->Name(), name ) ) {
+ return a;
+ }
+ }
+ return 0;
+}
+
+
+const char* XMLElement::Attribute( const char* name, const char* value ) const
+{
+ const XMLAttribute* a = FindAttribute( name );
+ if ( !a ) {
+ return 0;
+ }
+ if ( !value || XMLUtil::StringEqual( a->Value(), value )) {
+ return a->Value();
+ }
+ return 0;
+}
+
+
+const char* XMLElement::GetText() const
+{
+ if ( FirstChild() && FirstChild()->ToText() ) {
+ return FirstChild()->ToText()->Value();
+ }
+ return 0;
+}
+
+
+XMLError XMLElement::QueryIntText( int* ival ) const
+{
+ if ( FirstChild() && FirstChild()->ToText() ) {
+ const char* t = FirstChild()->ToText()->Value();
+ if ( XMLUtil::ToInt( t, ival ) ) {
+ return XML_SUCCESS;
+ }
+ return XML_CAN_NOT_CONVERT_TEXT;
+ }
+ return XML_NO_TEXT_NODE;
+}
+
+
+XMLError XMLElement::QueryUnsignedText( unsigned* uval ) const
+{
+ if ( FirstChild() && FirstChild()->ToText() ) {
+ const char* t = FirstChild()->ToText()->Value();
+ if ( XMLUtil::ToUnsigned( t, uval ) ) {
+ return XML_SUCCESS;
+ }
+ return XML_CAN_NOT_CONVERT_TEXT;
+ }
+ return XML_NO_TEXT_NODE;
+}
+
+
+XMLError XMLElement::QueryBoolText( bool* bval ) const
+{
+ if ( FirstChild() && FirstChild()->ToText() ) {
+ const char* t = FirstChild()->ToText()->Value();
+ if ( XMLUtil::ToBool( t, bval ) ) {
+ return XML_SUCCESS;
+ }
+ return XML_CAN_NOT_CONVERT_TEXT;
+ }
+ return XML_NO_TEXT_NODE;
+}
+
+
+XMLError XMLElement::QueryDoubleText( double* dval ) const
+{
+ if ( FirstChild() && FirstChild()->ToText() ) {
+ const char* t = FirstChild()->ToText()->Value();
+ if ( XMLUtil::ToDouble( t, dval ) ) {
+ return XML_SUCCESS;
+ }
+ return XML_CAN_NOT_CONVERT_TEXT;
+ }
+ return XML_NO_TEXT_NODE;
+}
+
+
+XMLError XMLElement::QueryFloatText( float* fval ) const
+{
+ if ( FirstChild() && FirstChild()->ToText() ) {
+ const char* t = FirstChild()->ToText()->Value();
+ if ( XMLUtil::ToFloat( t, fval ) ) {
+ return XML_SUCCESS;
+ }
+ return XML_CAN_NOT_CONVERT_TEXT;
+ }
+ return XML_NO_TEXT_NODE;
+}
+
+
+
+XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name )
+{
+ XMLAttribute* last = 0;
+ XMLAttribute* attrib = 0;
+ for( attrib = _rootAttribute;
+ attrib;
+ last = attrib, attrib = attrib->_next ) {
+ if ( XMLUtil::StringEqual( attrib->Name(), name ) ) {
+ break;
+ }
+ }
+ if ( !attrib ) {
+ attrib = new (_document->_attributePool.Alloc() ) XMLAttribute();
+ attrib->_memPool = &_document->_attributePool;
+ if ( last ) {
+ last->_next = attrib;
+ }
+ else {
+ _rootAttribute = attrib;
+ }
+ attrib->SetName( name );
+ attrib->_memPool->SetTracked(); // always created and linked.
+ }
+ return attrib;
+}
+
+
+void XMLElement::DeleteAttribute( const char* name )
+{
+ XMLAttribute* prev = 0;
+ for( XMLAttribute* a=_rootAttribute; a; a=a->_next ) {
+ if ( XMLUtil::StringEqual( name, a->Name() ) ) {
+ if ( prev ) {
+ prev->_next = a->_next;
+ }
+ else {
+ _rootAttribute = a->_next;
+ }
+ DELETE_ATTRIBUTE( a );
+ break;
+ }
+ prev = a;
+ }
+}
+
+
+char* XMLElement::ParseAttributes( char* p )
+{
+ const char* start = p;
+ XMLAttribute* prevAttribute = 0;
+
+ // Read the attributes.
+ while( p ) {
+ p = XMLUtil::SkipWhiteSpace( p );
+ if ( !p || !(*p) ) {
+ _document->SetError( XML_ERROR_PARSING_ELEMENT, start, Name() );
+ return 0;
+ }
+
+ // attribute.
+ if ( XMLUtil::IsAlpha( *p ) ) {
+ XMLAttribute* attrib = new (_document->_attributePool.Alloc() ) XMLAttribute();
+ attrib->_memPool = &_document->_attributePool;
+ attrib->_memPool->SetTracked();
+
+ p = attrib->ParseDeep( p, _document->ProcessEntities() );
+ if ( !p || Attribute( attrib->Name() ) ) {
+ DELETE_ATTRIBUTE( attrib );
+ _document->SetError( XML_ERROR_PARSING_ATTRIBUTE, start, p );
+ return 0;
+ }
+ // There is a minor bug here: if the attribute in the source xml
+ // document is duplicated, it will not be detected and the
+ // attribute will be doubly added. However, tracking the 'prevAttribute'
+ // avoids re-scanning the attribute list. Preferring performance for
+ // now, may reconsider in the future.
+ if ( prevAttribute ) {
+ prevAttribute->_next = attrib;
+ }
+ else {
+ _rootAttribute = attrib;
+ }
+ prevAttribute = attrib;
+ }
+ // end of the tag
+ else if ( *p == '/' && *(p+1) == '>' ) {
+ _closingType = CLOSED;
+ return p+2; // done; sealed element.
+ }
+ // end of the tag
+ else if ( *p == '>' ) {
+ ++p;
+ break;
+ }
+ else {
+ _document->SetError( XML_ERROR_PARSING_ELEMENT, start, p );
+ return 0;
+ }
+ }
+ return p;
+}
+
+
+//
+// <ele></ele>
+// <ele>foo<b>bar</b></ele>
+//
+char* XMLElement::ParseDeep( char* p, StrPair* strPair )
+{
+ // Read the element name.
+ p = XMLUtil::SkipWhiteSpace( p );
+ if ( !p ) {
+ return 0;
+ }
+
+ // The closing element is the </element> form. It is
+ // parsed just like a regular element then deleted from
+ // the DOM.
+ if ( *p == '/' ) {
+ _closingType = CLOSING;
+ ++p;
+ }
+
+ p = _value.ParseName( p );
+ if ( _value.Empty() ) {
+ return 0;
+ }
+
+ p = ParseAttributes( p );
+ if ( !p || !*p || _closingType ) {
+ return p;
+ }
+
+ p = XMLNode::ParseDeep( p, strPair );
+ return p;
+}
+
+
+
+XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const
+{
+ if ( !doc ) {
+ doc = _document;
+ }
+ XMLElement* element = doc->NewElement( Value() ); // fixme: this will always allocate memory. Intern?
+ for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) {
+ element->SetAttribute( a->Name(), a->Value() ); // fixme: this will always allocate memory. Intern?
+ }
+ return element;
+}
+
+
+bool XMLElement::ShallowEqual( const XMLNode* compare ) const
+{
+ const XMLElement* other = compare->ToElement();
+ if ( other && XMLUtil::StringEqual( other->Value(), Value() )) {
+
+ const XMLAttribute* a=FirstAttribute();
+ const XMLAttribute* b=other->FirstAttribute();
+
+ while ( a && b ) {
+ if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) {
+ return false;
+ }
+ a = a->Next();
+ b = b->Next();
+ }
+ if ( a || b ) {
+ // different count
+ return false;
+ }
+ return true;
+ }
+ return false;
+}
+
+
+bool XMLElement::Accept( XMLVisitor* visitor ) const
+{
+ if ( visitor->VisitEnter( *this, _rootAttribute ) ) {
+ for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {
+ if ( !node->Accept( visitor ) ) {
+ break;
+ }
+ }
+ }
+ return visitor->VisitExit( *this );
+}
+
+
+// --------- XMLDocument ----------- //
+XMLDocument::XMLDocument( bool processEntities, Whitespace whitespace ) :
+ XMLNode( 0 ),
+ _writeBOM( false ),
+ _processEntities( processEntities ),
+ _errorID( XML_NO_ERROR ),
+ _whitespace( whitespace ),
+ _errorStr1( 0 ),
+ _errorStr2( 0 ),
+ _charBuffer( 0 )
+{
+ _document = this; // avoid warning about 'this' in initializer list
+}
+
+
+XMLDocument::~XMLDocument()
+{
+ DeleteChildren();
+ delete [] _charBuffer;
+
+#if 0
+ textPool.Trace( "text" );
+ elementPool.Trace( "element" );
+ commentPool.Trace( "comment" );
+ attributePool.Trace( "attribute" );
+#endif
+
+#ifdef DEBUG
+ if ( Error() == false ) {
+ TIXMLASSERT( _elementPool.CurrentAllocs() == _elementPool.Untracked() );
+ TIXMLASSERT( _attributePool.CurrentAllocs() == _attributePool.Untracked() );
+ TIXMLASSERT( _textPool.CurrentAllocs() == _textPool.Untracked() );
+ TIXMLASSERT( _commentPool.CurrentAllocs() == _commentPool.Untracked() );
+ }
+#endif
+}
+
+
+void XMLDocument::InitDocument()
+{
+ _errorID = XML_NO_ERROR;
+ _errorStr1 = 0;
+ _errorStr2 = 0;
+
+ delete [] _charBuffer;
+ _charBuffer = 0;
+}
+
+
+XMLElement* XMLDocument::NewElement( const char* name )
+{
+ XMLElement* ele = new (_elementPool.Alloc()) XMLElement( this );
+ ele->_memPool = &_elementPool;
+ ele->SetName( name );
+ return ele;
+}
+
+
+XMLComment* XMLDocument::NewComment( const char* str )
+{
+ XMLComment* comment = new (_commentPool.Alloc()) XMLComment( this );
+ comment->_memPool = &_commentPool;
+ comment->SetValue( str );
+ return comment;
+}
+
+
+XMLText* XMLDocument::NewText( const char* str )
+{
+ XMLText* text = new (_textPool.Alloc()) XMLText( this );
+ text->_memPool = &_textPool;
+ text->SetValue( str );
+ return text;
+}
+
+
+XMLDeclaration* XMLDocument::NewDeclaration( const char* str )
+{
+ XMLDeclaration* dec = new (_commentPool.Alloc()) XMLDeclaration( this );
+ dec->_memPool = &_commentPool;
+ dec->SetValue( str ? str : "xml version=\"1.0\" encoding=\"UTF-8\"" );
+ return dec;
+}
+
+
+XMLUnknown* XMLDocument::NewUnknown( const char* str )
+{
+ XMLUnknown* unk = new (_commentPool.Alloc()) XMLUnknown( this );
+ unk->_memPool = &_commentPool;
+ unk->SetValue( str );
+ return unk;
+}
+
+
+XMLError XMLDocument::LoadFile( const char* filename )
+{
+ DeleteChildren();
+ InitDocument();
+ FILE* fp = 0;
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1400 )
+ errno_t err = fopen_s(&fp, filename, "rb" );
+ if ( !fp || err) {
+#else
+ fp = fopen( filename, "rb" );
+ if ( !fp) {
+#endif
+ SetError( XML_ERROR_FILE_NOT_FOUND, filename, 0 );
+ return _errorID;
+ }
+ LoadFile( fp );
+ fclose( fp );
+ return _errorID;
+}
+
+
+XMLError XMLDocument::LoadFile( FILE* fp )
+{
+ DeleteChildren();
+ InitDocument();
+
+ fseek( fp, 0, SEEK_END );
+ size_t size = ftell( fp );
+ fseek( fp, 0, SEEK_SET );
+
+ if ( size == 0 ) {
+ return _errorID;
+ }
+
+ _charBuffer = new char[size+1];
+ size_t read = fread( _charBuffer, 1, size, fp );
+ if ( read != size ) {
+ SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
+ return _errorID;
+ }
+
+ _charBuffer[size] = 0;
+
+ const char* p = _charBuffer;
+ p = XMLUtil::SkipWhiteSpace( p );
+ p = XMLUtil::ReadBOM( p, &_writeBOM );
+ if ( !p || !*p ) {
+ SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
+ return _errorID;
+ }
+
+ ParseDeep( _charBuffer + (p-_charBuffer), 0 );
+ return _errorID;
+}
+
+
+XMLError XMLDocument::SaveFile( const char* filename, bool compact )
+{
+ FILE* fp = 0;
+#if defined(_MSC_VER) && (_MSC_VER >= 1400 )
+ errno_t err = fopen_s(&fp, filename, "w" );
+ if ( !fp || err) {
+#else
+ fp = fopen( filename, "w" );
+ if ( !fp) {
+#endif
+ SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, filename, 0 );
+ return _errorID;
+ }
+ SaveFile(fp, compact);
+ fclose( fp );
+ return _errorID;
+}
+
+
+XMLError XMLDocument::SaveFile( FILE* fp, bool compact )
+{
+ XMLPrinter stream( fp, compact );
+ Print( &stream );
+ return _errorID;
+}
+
+
+XMLError XMLDocument::Parse( const char* p, size_t len )
+{
+ DeleteChildren();
+ InitDocument();
+
+ if ( !p || !*p ) {
+ SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
+ return _errorID;
+ }
+ if ( len == (size_t)(-1) ) {
+ len = strlen( p );
+ }
+ _charBuffer = new char[ len+1 ];
+ memcpy( _charBuffer, p, len );
+ _charBuffer[len] = 0;
+
+ p = XMLUtil::SkipWhiteSpace( p );
+ p = XMLUtil::ReadBOM( p, &_writeBOM );
+ if ( !p || !*p ) {
+ SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
+ return _errorID;
+ }
+
+ ParseDeep( _charBuffer, 0 );
+ return _errorID;
+}
+
+
+void XMLDocument::Print( XMLPrinter* streamer )
+{
+ XMLPrinter stdStreamer( stdout );
+ if ( !streamer ) {
+ streamer = &stdStreamer;
+ }
+ Accept( streamer );
+}
+
+
+void XMLDocument::SetError( XMLError error, const char* str1, const char* str2 )
+{
+ _errorID = error;
+ _errorStr1 = str1;
+ _errorStr2 = str2;
+}
+
+
+void XMLDocument::PrintError() const
+{
+ if ( _errorID ) {
+ static const int LEN = 20;
+ char buf1[LEN] = { 0 };
+ char buf2[LEN] = { 0 };
+
+ if ( _errorStr1 ) {
+ TIXML_SNPRINTF( buf1, LEN, "%s", _errorStr1 );
+ }
+ if ( _errorStr2 ) {
+ TIXML_SNPRINTF( buf2, LEN, "%s", _errorStr2 );
+ }
+
+ printf( "XMLDocument error id=%d str1=%s str2=%s\n",
+ _errorID, buf1, buf2 );
+ }
+}
+
+
+XMLPrinter::XMLPrinter( FILE* file, bool compact ) :
+ _elementJustOpened( false ),
+ _firstElement( true ),
+ _fp( file ),
+ _depth( 0 ),
+ _textDepth( -1 ),
+ _processEntities( true ),
+ _compactMode( compact )
+{
+ for( int i=0; i<ENTITY_RANGE; ++i ) {
+ _entityFlag[i] = false;
+ _restrictedEntityFlag[i] = false;
+ }
+ for( int i=0; i<NUM_ENTITIES; ++i ) {
+ TIXMLASSERT( entities[i].value < ENTITY_RANGE );
+ if ( entities[i].value < ENTITY_RANGE ) {
+ _entityFlag[ (int)entities[i].value ] = true;
+ }
+ }
+ _restrictedEntityFlag[(int)'&'] = true;
+ _restrictedEntityFlag[(int)'<'] = true;
+ _restrictedEntityFlag[(int)'>'] = true; // not required, but consistency is nice
+ _buffer.Push( 0 );
+}
+
+
+void XMLPrinter::Print( const char* format, ... )
+{
+ va_list va;
+ va_start( va, format );
+
+ if ( _fp ) {
+ vfprintf( _fp, format, va );
+ }
+ else {
+ // This seems brutally complex. Haven't figured out a better
+ // way on windows.
+#ifdef _MSC_VER
+ int len = -1;
+ int expand = 1000;
+ while ( len < 0 ) {
+ len = vsnprintf_s( _accumulator.Mem(), _accumulator.Capacity(), _TRUNCATE, format, va );
+ if ( len < 0 ) {
+ expand *= 3/2;
+ _accumulator.PushArr( expand );
+ }
+ }
+ char* p = _buffer.PushArr( len ) - 1;
+ memcpy( p, _accumulator.Mem(), len+1 );
+#else
+ int len = vsnprintf( 0, 0, format, va );
+ // Close out and re-start the va-args
+ va_end( va );
+ va_start( va, format );
+ char* p = _buffer.PushArr( len ) - 1;
+ vsnprintf( p, len+1, format, va );
+#endif
+ }
+ va_end( va );
+}
+
+
+void XMLPrinter::PrintSpace( int depth )
+{
+ for( int i=0; i<depth; ++i ) {
+ Print( " " );
+ }
+}
+
+
+void XMLPrinter::PrintString( const char* p, bool restricted )
+{
+ // Look for runs of bytes between entities to print.
+ const char* q = p;
+ const bool* flag = restricted ? _restrictedEntityFlag : _entityFlag;
+
+ if ( _processEntities ) {
+ while ( *q ) {
+ // Remember, char is sometimes signed. (How many times has that bitten me?)
+ if ( *q > 0 && *q < ENTITY_RANGE ) {
+ // Check for entities. If one is found, flush
+ // the stream up until the entity, write the
+ // entity, and keep looking.
+ if ( flag[(unsigned)(*q)] ) {
+ while ( p < q ) {
+ Print( "%c", *p );
+ ++p;
+ }
+ for( int i=0; i<NUM_ENTITIES; ++i ) {
+ if ( entities[i].value == *q ) {
+ Print( "&%s;", entities[i].pattern );
+ break;
+ }
+ }
+ ++p;
+ }
+ }
+ ++q;
+ }
+ }
+ // Flush the remaining string. This will be the entire
+ // string if an entity wasn't found.
+ if ( !_processEntities || (q-p > 0) ) {
+ Print( "%s", p );
+ }
+}
+
+
+void XMLPrinter::PushHeader( bool writeBOM, bool writeDec )
+{
+ static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 };
+ if ( writeBOM ) {
+ Print( "%s", bom );
+ }
+ if ( writeDec ) {
+ PushDeclaration( "xml version=\"1.0\"" );
+ }
+}
+
+
+void XMLPrinter::OpenElement( const char* name )
+{
+ if ( _elementJustOpened ) {
+ SealElement();
+ }
+ _stack.Push( name );
+
+ if ( _textDepth < 0 && !_firstElement && !_compactMode ) {
+ Print( "\n" );
+ PrintSpace( _depth );
+ }
+
+ Print( "<%s", name );
+ _elementJustOpened = true;
+ _firstElement = false;
+ ++_depth;
+}
+
+
+void XMLPrinter::PushAttribute( const char* name, const char* value )
+{
+ TIXMLASSERT( _elementJustOpened );
+ Print( " %s=\"", name );
+ PrintString( value, false );
+ Print( "\"" );
+}
+
+
+void XMLPrinter::PushAttribute( const char* name, int v )
+{
+ char buf[BUF_SIZE];
+ XMLUtil::ToStr( v, buf, BUF_SIZE );
+ PushAttribute( name, buf );
+}
+
+
+void XMLPrinter::PushAttribute( const char* name, unsigned v )
+{
+ char buf[BUF_SIZE];
+ XMLUtil::ToStr( v, buf, BUF_SIZE );
+ PushAttribute( name, buf );
+}
+
+
+void XMLPrinter::PushAttribute( const char* name, bool v )
+{
+ char buf[BUF_SIZE];
+ XMLUtil::ToStr( v, buf, BUF_SIZE );
+ PushAttribute( name, buf );
+}
+
+
+void XMLPrinter::PushAttribute( const char* name, double v )
+{
+ char buf[BUF_SIZE];
+ XMLUtil::ToStr( v, buf, BUF_SIZE );
+ PushAttribute( name, buf );
+}
+
+
+void XMLPrinter::CloseElement()
+{
+ --_depth;
+ const char* name = _stack.Pop();
+
+ if ( _elementJustOpened ) {
+ Print( "/>" );
+ }
+ else {
+ if ( _textDepth < 0 && !_compactMode) {
+ Print( "\n" );
+ PrintSpace( _depth );
+ }
+ Print( "</%s>", name );
+ }
+
+ if ( _textDepth == _depth ) {
+ _textDepth = -1;
+ }
+ if ( _depth == 0 && !_compactMode) {
+ Print( "\n" );
+ }
+ _elementJustOpened = false;
+}
+
+
+void XMLPrinter::SealElement()
+{
+ _elementJustOpened = false;
+ Print( ">" );
+}
+
+
+void XMLPrinter::PushText( const char* text, bool cdata )
+{
+ _textDepth = _depth-1;
+
+ if ( _elementJustOpened ) {
+ SealElement();
+ }
+ if ( cdata ) {
+ Print( "<![CDATA[" );
+ Print( "%s", text );
+ Print( "]]>" );
+ }
+ else {
+ PrintString( text, true );
+ }
+}
+
+void XMLPrinter::PushText( int value )
+{
+ char buf[BUF_SIZE];
+ XMLUtil::ToStr( value, buf, BUF_SIZE );
+ PushText( buf, false );
+}
+
+
+void XMLPrinter::PushText( unsigned value )
+{
+ char buf[BUF_SIZE];
+ XMLUtil::ToStr( value, buf, BUF_SIZE );
+ PushText( buf, false );
+}
+
+
+void XMLPrinter::PushText( bool value )
+{
+ char buf[BUF_SIZE];
+ XMLUtil::ToStr( value, buf, BUF_SIZE );
+ PushText( buf, false );
+}
+
+
+void XMLPrinter::PushText( float value )
+{
+ char buf[BUF_SIZE];
+ XMLUtil::ToStr( value, buf, BUF_SIZE );
+ PushText( buf, false );
+}
+
+
+void XMLPrinter::PushText( double value )
+{
+ char buf[BUF_SIZE];
+ XMLUtil::ToStr( value, buf, BUF_SIZE );
+ PushText( buf, false );
+}
+
+
+void XMLPrinter::PushComment( const char* comment )
+{
+ if ( _elementJustOpened ) {
+ SealElement();
+ }
+ if ( _textDepth < 0 && !_firstElement && !_compactMode) {
+ Print( "\n" );
+ PrintSpace( _depth );
+ }
+ _firstElement = false;
+ Print( "<!--%s-->", comment );
+}
+
+
+void XMLPrinter::PushDeclaration( const char* value )
+{
+ if ( _elementJustOpened ) {
+ SealElement();
+ }
+ if ( _textDepth < 0 && !_firstElement && !_compactMode) {
+ Print( "\n" );
+ PrintSpace( _depth );
+ }
+ _firstElement = false;
+ Print( "<?%s?>", value );
+}
+
+
+void XMLPrinter::PushUnknown( const char* value )
+{
+ if ( _elementJustOpened ) {
+ SealElement();
+ }
+ if ( _textDepth < 0 && !_firstElement && !_compactMode) {
+ Print( "\n" );
+ PrintSpace( _depth );
+ }
+ _firstElement = false;
+ Print( "<!%s>", value );
+}
+
+
+bool XMLPrinter::VisitEnter( const XMLDocument& doc )
+{
+ _processEntities = doc.ProcessEntities();
+ if ( doc.HasBOM() ) {
+ PushHeader( true, false );
+ }
+ return true;
+}
+
+
+bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute )
+{
+ OpenElement( element.Name() );
+ while ( attribute ) {
+ PushAttribute( attribute->Name(), attribute->Value() );
+ attribute = attribute->Next();
+ }
+ return true;
+}
+
+
+bool XMLPrinter::VisitExit( const XMLElement& )
+{
+ CloseElement();
+ return true;
+}
+
+
+bool XMLPrinter::Visit( const XMLText& text )
+{
+ PushText( text.Value(), text.CData() );
+ return true;
+}
+
+
+bool XMLPrinter::Visit( const XMLComment& comment )
+{
+ PushComment( comment.Value() );
+ return true;
+}
+
+bool XMLPrinter::Visit( const XMLDeclaration& declaration )
+{
+ PushDeclaration( declaration.Value() );
+ return true;
+}
+
+
+bool XMLPrinter::Visit( const XMLUnknown& unknown )
+{
+ PushUnknown( unknown.Value() );
+ return true;
+}
+
+} // namespace tinyxml2
+
diff --git a/3rdParty/TinyXml/tinyxml2.h b/3rdParty/TinyXml/tinyxml2.h
new file mode 100644
index 0000000..0f8e9ad
--- /dev/null
+++ b/3rdParty/TinyXml/tinyxml2.h
@@ -0,0 +1,1911 @@
+/*
+Original code by Lee Thomason (www.grinninglizard.com)
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any
+damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any
+purpose, including commercial applications, and to alter it and
+redistribute it freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must
+not claim that you wrote the original software. If you use this
+software in a product, an acknowledgment in the product documentation
+would be appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and
+must not be misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source
+distribution.
+*/
+
+#ifndef TINYXML2_INCLUDED
+#define TINYXML2_INCLUDED
+
+#if defined(ANDROID_NDK) || defined(__BORLANDC__)
+# include <ctype.h>
+# include <limits.h>
+# include <stdio.h>
+# include <stdlib.h>
+# include <string.h>
+# include <stdarg.h>
+#else
+# include <cctype>
+# include <climits>
+# include <cstdio>
+# include <cstdlib>
+# include <cstring>
+# include <cstdarg>
+#endif
+
+/*
+ TODO: intern strings instead of allocation.
+*/
+/*
+ gcc:
+ g++ -Wall -DDEBUG tinyxml2.cpp xmltest.cpp -o gccxmltest.exe
+
+ Formatting, Artistic Style:
+ AStyle.exe --style=1tbs --indent-switches --break-closing-brackets --indent-preprocessor tinyxml2.cpp tinyxml2.h
+*/
+
+#if defined( _DEBUG ) || defined( DEBUG ) || defined (__DEBUG__)
+# ifndef DEBUG
+# define DEBUG
+# endif
+#endif
+
+
+#if defined(DEBUG)
+# if defined(_MSC_VER)
+# define TIXMLASSERT( x ) if ( !(x)) { __debugbreak(); } //if ( !(x)) WinDebugBreak()
+# elif defined (ANDROID_NDK)
+# include <android/log.h>
+# define TIXMLASSERT( x ) if ( !(x)) { __android_log_assert( "assert", "grinliz", "ASSERT in '%s' at %d.", __FILE__, __LINE__ ); }
+# else
+# include <assert.h>
+# define TIXMLASSERT assert
+# endif
+# else
+# define TIXMLASSERT( x ) {}
+#endif
+
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1400 )
+// Microsoft visual studio, version 2005 and higher.
+/*int _snprintf_s(
+ char *buffer,
+ size_t sizeOfBuffer,
+ size_t count,
+ const char *format [,
+ argument] ...
+);*/
+inline int TIXML_SNPRINTF( char* buffer, size_t size, const char* format, ... )
+{
+ va_list va;
+ va_start( va, format );
+ int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va );
+ va_end( va );
+ return result;
+}
+#define TIXML_SSCANF sscanf_s
+#else
+// GCC version 3 and higher
+//#warning( "Using sn* functions." )
+#define TIXML_SNPRINTF snprintf
+#define TIXML_SSCANF sscanf
+#endif
+
+static const int TIXML2_MAJOR_VERSION = 1;
+static const int TIXML2_MINOR_VERSION = 0;
+static const int TIXML2_PATCH_VERSION = 9;
+
+namespace tinyxml2
+{
+class XMLDocument;
+class XMLElement;
+class XMLAttribute;
+class XMLComment;
+class XMLNode;
+class XMLText;
+class XMLDeclaration;
+class XMLUnknown;
+
+class XMLPrinter;
+
+/*
+ A class that wraps strings. Normally stores the start and end
+ pointers into the XML file itself, and will apply normalization
+ and entity translation if actually read. Can also store (and memory
+ manage) a traditional char[]
+*/
+class StrPair
+{
+public:
+ enum {
+ NEEDS_ENTITY_PROCESSING = 0x01,
+ NEEDS_NEWLINE_NORMALIZATION = 0x02,
+ COLLAPSE_WHITESPACE = 0x04,
+
+ TEXT_ELEMENT = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION,
+ TEXT_ELEMENT_LEAVE_ENTITIES = NEEDS_NEWLINE_NORMALIZATION,
+ ATTRIBUTE_NAME = 0,
+ ATTRIBUTE_VALUE = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION,
+ ATTRIBUTE_VALUE_LEAVE_ENTITIES = NEEDS_NEWLINE_NORMALIZATION,
+ COMMENT = NEEDS_NEWLINE_NORMALIZATION
+ };
+
+ StrPair() : _flags( 0 ), _start( 0 ), _end( 0 ) {}
+ ~StrPair();
+
+ void Set( char* start, char* end, int flags ) {
+ Reset();
+ _start = start;
+ _end = end;
+ _flags = flags | NEEDS_FLUSH;
+ }
+
+ const char* GetStr();
+
+ bool Empty() const {
+ return _start == _end;
+ }
+
+ void SetInternedStr( const char* str ) {
+ Reset();
+ _start = const_cast<char*>(str);
+ }
+
+ void SetStr( const char* str, int flags=0 );
+
+ char* ParseText( char* in, const char* endTag, int strFlags );
+ char* ParseName( char* in );
+
+private:
+ void Reset();
+ void CollapseWhitespace();
+
+ enum {
+ NEEDS_FLUSH = 0x100,
+ NEEDS_DELETE = 0x200
+ };
+
+ // After parsing, if *end != 0, it can be set to zero.
+ int _flags;
+ char* _start;
+ char* _end;
+};
+
+
+/*
+ A dynamic array of Plain Old Data. Doesn't support constructors, etc.
+ Has a small initial memory pool, so that low or no usage will not
+ cause a call to new/delete
+*/
+template <class T, int INIT>
+class DynArray
+{
+public:
+ DynArray< T, INIT >() {
+ _mem = _pool;
+ _allocated = INIT;
+ _size = 0;
+ }
+
+ ~DynArray() {
+ if ( _mem != _pool ) {
+ delete [] _mem;
+ }
+ }
+
+ void Push( T t ) {
+ EnsureCapacity( _size+1 );
+ _mem[_size++] = t;
+ }
+
+ T* PushArr( int count ) {
+ EnsureCapacity( _size+count );
+ T* ret = &_mem[_size];
+ _size += count;
+ return ret;
+ }
+
+ T Pop() {
+ return _mem[--_size];
+ }
+
+ void PopArr( int count ) {
+ TIXMLASSERT( _size >= count );
+ _size -= count;
+ }
+
+ bool Empty() const {
+ return _size == 0;
+ }
+
+ T& operator[](int i) {
+ TIXMLASSERT( i>= 0 && i < _size );
+ return _mem[i];
+ }
+
+ const T& operator[](int i) const {
+ TIXMLASSERT( i>= 0 && i < _size );
+ return _mem[i];
+ }
+
+ int Size() const {
+ return _size;
+ }
+
+ int Capacity() const {
+ return _allocated;
+ }
+
+ const T* Mem() const {
+ return _mem;
+ }
+
+ T* Mem() {
+ return _mem;
+ }
+
+private:
+ void EnsureCapacity( int cap ) {
+ if ( cap > _allocated ) {
+ int newAllocated = cap * 2;
+ T* newMem = new T[newAllocated];
+ memcpy( newMem, _mem, sizeof(T)*_size ); // warning: not using constructors, only works for PODs
+ if ( _mem != _pool ) {
+ delete [] _mem;
+ }
+ _mem = newMem;
+ _allocated = newAllocated;
+ }
+ }
+
+ T* _mem;
+ T _pool[INIT];
+ int _allocated; // objects allocated
+ int _size; // number objects in use
+};
+
+
+/*
+ Parent virtual class of a pool for fast allocation
+ and deallocation of objects.
+*/
+class MemPool
+{
+public:
+ MemPool() {}
+ virtual ~MemPool() {}
+
+ virtual int ItemSize() const = 0;
+ virtual void* Alloc() = 0;
+ virtual void Free( void* ) = 0;
+ virtual void SetTracked() = 0;
+};
+
+
+/*
+ Template child class to create pools of the correct type.
+*/
+template< int SIZE >
+class MemPoolT : public MemPool
+{
+public:
+ MemPoolT() : _root(0), _currentAllocs(0), _nAllocs(0), _maxAllocs(0), _nUntracked(0) {}
+ ~MemPoolT() {
+ // Delete the blocks.
+ for( int i=0; i<_blockPtrs.Size(); ++i ) {
+ delete _blockPtrs[i];
+ }
+ }
+
+ virtual int ItemSize() const {
+ return SIZE;
+ }
+ int CurrentAllocs() const {
+ return _currentAllocs;
+ }
+
+ virtual void* Alloc() {
+ if ( !_root ) {
+ // Need a new block.
+ Block* block = new Block();
+ _blockPtrs.Push( block );
+
+ for( int i=0; i<COUNT-1; ++i ) {
+ block->chunk[i].next = &block->chunk[i+1];
+ }
+ block->chunk[COUNT-1].next = 0;
+ _root = block->chunk;
+ }
+ void* result = _root;
+ _root = _root->next;
+
+ ++_currentAllocs;
+ if ( _currentAllocs > _maxAllocs ) {
+ _maxAllocs = _currentAllocs;
+ }
+ _nAllocs++;
+ _nUntracked++;
+ return result;
+ }
+ virtual void Free( void* mem ) {
+ if ( !mem ) {
+ return;
+ }
+ --_currentAllocs;
+ Chunk* chunk = (Chunk*)mem;
+#ifdef DEBUG
+ memset( chunk, 0xfe, sizeof(Chunk) );
+#endif
+ chunk->next = _root;
+ _root = chunk;
+ }
+ void Trace( const char* name ) {
+ printf( "Mempool %s watermark=%d [%dk] current=%d size=%d nAlloc=%d blocks=%d\n",
+ name, _maxAllocs, _maxAllocs*SIZE/1024, _currentAllocs, SIZE, _nAllocs, _blockPtrs.Size() );
+ }
+
+ void SetTracked() {
+ _nUntracked--;
+ }
+
+ int Untracked() const {
+ return _nUntracked;
+ }
+
+ enum { COUNT = 1024/SIZE }; // Some compilers do not accept to use COUNT in private part if COUNT is private
+
+private:
+ union Chunk {
+ Chunk* next;
+ char mem[SIZE];
+ };
+ struct Block {
+ Chunk chunk[COUNT];
+ };
+ DynArray< Block*, 10 > _blockPtrs;
+ Chunk* _root;
+
+ int _currentAllocs;
+ int _nAllocs;
+ int _maxAllocs;
+ int _nUntracked;
+};
+
+
+
+/**
+ Implements the interface to the "Visitor pattern" (see the Accept() method.)
+ If you call the Accept() method, it requires being passed a XMLVisitor
+ class to handle callbacks. For nodes that contain other nodes (Document, Element)
+ you will get called with a VisitEnter/VisitExit pair. Nodes that are always leafs
+ are simply called with Visit().
+
+ If you return 'true' from a Visit method, recursive parsing will continue. If you return
+ false, <b>no children of this node or its sibilings</b> will be visited.
+
+ All flavors of Visit methods have a default implementation that returns 'true' (continue
+ visiting). You need to only override methods that are interesting to you.
+
+ Generally Accept() is called on the TiXmlDocument, although all nodes support visiting.
+
+ You should never change the document from a callback.
+
+ @sa XMLNode::Accept()
+*/
+class XMLVisitor
+{
+public:
+ virtual ~XMLVisitor() {}
+
+ /// Visit a document.
+ virtual bool VisitEnter( const XMLDocument& /*doc*/ ) {
+ return true;
+ }
+ /// Visit a document.
+ virtual bool VisitExit( const XMLDocument& /*doc*/ ) {
+ return true;
+ }
+
+ /// Visit an element.
+ virtual bool VisitEnter( const XMLElement& /*element*/, const XMLAttribute* /*firstAttribute*/ ) {
+ return true;
+ }
+ /// Visit an element.
+ virtual bool VisitExit( const XMLElement& /*element*/ ) {
+ return true;
+ }
+
+ /// Visit a declaration.
+ virtual bool Visit( const XMLDeclaration& /*declaration*/ ) {
+ return true;
+ }
+ /// Visit a text node.
+ virtual bool Visit( const XMLText& /*text*/ ) {
+ return true;
+ }
+ /// Visit a comment node.
+ virtual bool Visit( const XMLComment& /*comment*/ ) {
+ return true;
+ }
+ /// Visit an unknown node.
+ virtual bool Visit( const XMLUnknown& /*unknown*/ ) {
+ return true;
+ }
+};
+
+
+/*
+ Utility functionality.
+*/
+class XMLUtil
+{
+public:
+ // Anything in the high order range of UTF-8 is assumed to not be whitespace. This isn't
+ // correct, but simple, and usually works.
+ static const char* SkipWhiteSpace( const char* p ) {
+ while( !IsUTF8Continuation(*p) && isspace( *reinterpret_cast<const unsigned char*>(p) ) ) {
+ ++p;
+ }
+ return p;
+ }
+ static char* SkipWhiteSpace( char* p ) {
+ while( !IsUTF8Continuation(*p) && isspace( *reinterpret_cast<unsigned char*>(p) ) ) {
+ ++p;
+ }
+ return p;
+ }
+ static bool IsWhiteSpace( char p ) {
+ return !IsUTF8Continuation(p) && isspace( static_cast<unsigned char>(p) );
+ }
+
+ inline static bool StringEqual( const char* p, const char* q, int nChar=INT_MAX ) {
+ int n = 0;
+ if ( p == q ) {
+ return true;
+ }
+ while( *p && *q && *p == *q && n<nChar ) {
+ ++p;
+ ++q;
+ ++n;
+ }
+ if ( (n == nChar) || ( *p == 0 && *q == 0 ) ) {
+ return true;
+ }
+ return false;
+ }
+ inline static int IsUTF8Continuation( const char p ) {
+ return p & 0x80;
+ }
+ inline static int IsAlphaNum( unsigned char anyByte ) {
+ return ( anyByte < 128 ) ? isalnum( anyByte ) : 1;
+ }
+ inline static int IsAlpha( unsigned char anyByte ) {
+ return ( anyByte < 128 ) ? isalpha( anyByte ) : 1;
+ }
+
+ static const char* ReadBOM( const char* p, bool* hasBOM );
+ // p is the starting location,
+ // the UTF-8 value of the entity will be placed in value, and length filled in.
+ static const char* GetCharacterRef( const char* p, char* value, int* length );
+ static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length );
+
+ // converts primitive types to strings
+ static void ToStr( int v, char* buffer, int bufferSize );
+ static void ToStr( unsigned v, char* buffer, int bufferSize );
+ static void ToStr( bool v, char* buffer, int bufferSize );
+ static void ToStr( float v, char* buffer, int bufferSize );
+ static void ToStr( double v, char* buffer, int bufferSize );
+
+ // converts strings to primitive types
+ static bool ToInt( const char* str, int* value );
+ static bool ToUnsigned( const char* str, unsigned* value );
+ static bool ToBool( const char* str, bool* value );
+ static bool ToFloat( const char* str, float* value );
+ static bool ToDouble( const char* str, double* value );
+};
+
+
+/** XMLNode is a base class for every object that is in the
+ XML Document Object Model (DOM), except XMLAttributes.
+ Nodes have siblings, a parent, and children which can
+ be navigated. A node is always in a XMLDocument.
+ The type of a XMLNode can be queried, and it can
+ be cast to its more defined type.
+
+ A XMLDocument allocates memory for all its Nodes.
+ When the XMLDocument gets deleted, all its Nodes
+ will also be deleted.
+
+ @verbatim
+ A Document can contain: Element (container or leaf)
+ Comment (leaf)
+ Unknown (leaf)
+ Declaration( leaf )
+
+ An Element can contain: Element (container or leaf)
+ Text (leaf)
+ Attributes (not on tree)
+ Comment (leaf)
+ Unknown (leaf)
+
+ @endverbatim
+*/
+class XMLNode
+{
+ friend class XMLDocument;
+ friend class XMLElement;
+public:
+
+ /// Get the XMLDocument that owns this XMLNode.
+ const XMLDocument* GetDocument() const {
+ return _document;
+ }
+ /// Get the XMLDocument that owns this XMLNode.
+ XMLDocument* GetDocument() {
+ return _document;
+ }
+
+ /// Safely cast to an Element, or null.
+ virtual XMLElement* ToElement() {
+ return 0;
+ }
+ /// Safely cast to Text, or null.
+ virtual XMLText* ToText() {
+ return 0;
+ }
+ /// Safely cast to a Comment, or null.
+ virtual XMLComment* ToComment() {
+ return 0;
+ }
+ /// Safely cast to a Document, or null.
+ virtual XMLDocument* ToDocument() {
+ return 0;
+ }
+ /// Safely cast to a Declaration, or null.
+ virtual XMLDeclaration* ToDeclaration() {
+ return 0;
+ }
+ /// Safely cast to an Unknown, or null.
+ virtual XMLUnknown* ToUnknown() {
+ return 0;
+ }
+
+ virtual const XMLElement* ToElement() const {
+ return 0;
+ }
+ virtual const XMLText* ToText() const {
+ return 0;
+ }
+ virtual const XMLComment* ToComment() const {
+ return 0;
+ }
+ virtual const XMLDocument* ToDocument() const {
+ return 0;
+ }
+ virtual const XMLDeclaration* ToDeclaration() const {
+ return 0;
+ }
+ virtual const XMLUnknown* ToUnknown() const {
+ return 0;
+ }
+
+ /** The meaning of 'value' changes for the specific type.
+ @verbatim
+ Document: empty
+ Element: name of the element
+ Comment: the comment text
+ Unknown: the tag contents
+ Text: the text string
+ @endverbatim
+ */
+ const char* Value() const {
+ return _value.GetStr();
+ }
+
+ /** Set the Value of an XML node.
+ @sa Value()
+ */
+ void SetValue( const char* val, bool staticMem=false );
+
+ /// Get the parent of this node on the DOM.
+ const XMLNode* Parent() const {
+ return _parent;
+ }
+
+ XMLNode* Parent() {
+ return _parent;
+ }
+
+ /// Returns true if this node has no children.
+ bool NoChildren() const {
+ return !_firstChild;
+ }
+
+ /// Get the first child node, or null if none exists.
+ const XMLNode* FirstChild() const {
+ return _firstChild;
+ }
+
+ XMLNode* FirstChild() {
+ return _firstChild;
+ }
+
+ /** Get the first child element, or optionally the first child
+ element with the specified name.
+ */
+ const XMLElement* FirstChildElement( const char* value=0 ) const;
+
+ XMLElement* FirstChildElement( const char* value=0 ) {
+ return const_cast<XMLElement*>(const_cast<const XMLNode*>(this)->FirstChildElement( value ));
+ }
+
+ /// Get the last child node, or null if none exists.
+ const XMLNode* LastChild() const {
+ return _lastChild;
+ }
+
+ XMLNode* LastChild() {
+ return const_cast<XMLNode*>(const_cast<const XMLNode*>(this)->LastChild() );
+ }
+
+ /** Get the last child element or optionally the last child
+ element with the specified name.
+ */
+ const XMLElement* LastChildElement( const char* value=0 ) const;
+
+ XMLElement* LastChildElement( const char* value=0 ) {
+ return const_cast<XMLElement*>(const_cast<const XMLNode*>(this)->LastChildElement(value) );
+ }
+
+ /// Get the previous (left) sibling node of this node.
+ const XMLNode* PreviousSibling() const {
+ return _prev;
+ }
+
+ XMLNode* PreviousSibling() {
+ return _prev;
+ }
+
+ /// Get the previous (left) sibling element of this node, with an opitionally supplied name.
+ const XMLElement* PreviousSiblingElement( const char* value=0 ) const ;
+
+ XMLElement* PreviousSiblingElement( const char* value=0 ) {
+ return const_cast<XMLElement*>(const_cast<const XMLNode*>(this)->PreviousSiblingElement( value ) );
+ }
+
+ /// Get the next (right) sibling node of this node.
+ const XMLNode* NextSibling() const {
+ return _next;
+ }
+
+ XMLNode* NextSibling() {
+ return _next;
+ }
+
+ /// Get the next (right) sibling element of this node, with an opitionally supplied name.
+ const XMLElement* NextSiblingElement( const char* value=0 ) const;
+
+ XMLElement* NextSiblingElement( const char* value=0 ) {
+ return const_cast<XMLElement*>(const_cast<const XMLNode*>(this)->NextSiblingElement( value ) );
+ }
+
+ /**
+ Add a child node as the last (right) child.
+ */
+ XMLNode* InsertEndChild( XMLNode* addThis );
+
+ XMLNode* LinkEndChild( XMLNode* addThis ) {
+ return InsertEndChild( addThis );
+ }
+ /**
+ Add a child node as the first (left) child.
+ */
+ XMLNode* InsertFirstChild( XMLNode* addThis );
+ /**
+ Add a node after the specified child node.
+ */
+ XMLNode* InsertAfterChild( XMLNode* afterThis, XMLNode* addThis );
+
+ /**
+ Delete all the children of this node.
+ */
+ void DeleteChildren();
+
+ /**
+ Delete a child of this node.
+ */
+ void DeleteChild( XMLNode* node );
+
+ /**
+ Make a copy of this node, but not its children.
+ You may pass in a Document pointer that will be
+ the owner of the new Node. If the 'document' is
+ null, then the node returned will be allocated
+ from the current Document. (this->GetDocument())
+
+ Note: if called on a XMLDocument, this will return null.
+ */
+ virtual XMLNode* ShallowClone( XMLDocument* document ) const = 0;
+
+ /**
+ Test if 2 nodes are the same, but don't test children.
+ The 2 nodes do not need to be in the same Document.
+
+ Note: if called on a XMLDocument, this will return false.
+ */
+ virtual bool ShallowEqual( const XMLNode* compare ) const = 0;
+
+ /** Accept a hierarchical visit of the nodes in the TinyXML DOM. Every node in the
+ XML tree will be conditionally visited and the host will be called back
+ via the TiXmlVisitor interface.
+
+ This is essentially a SAX interface for TinyXML. (Note however it doesn't re-parse
+ the XML for the callbacks, so the performance of TinyXML is unchanged by using this
+ interface versus any other.)
+
+ The interface has been based on ideas from:
+
+ - http://www.saxproject.org/
+ - http://c2.com/cgi/wiki?HierarchicalVisitorPattern
+
+ Which are both good references for "visiting".
+
+ An example of using Accept():
+ @verbatim
+ TiXmlPrinter printer;
+ tinyxmlDoc.Accept( &printer );
+ const char* xmlcstr = printer.CStr();
+ @endverbatim
+ */
+ virtual bool Accept( XMLVisitor* visitor ) const = 0;
+
+ // internal
+ virtual char* ParseDeep( char*, StrPair* );
+
+protected:
+ XMLNode( XMLDocument* );
+ virtual ~XMLNode();
+ XMLNode( const XMLNode& ); // not supported
+ XMLNode& operator=( const XMLNode& ); // not supported
+
+ XMLDocument* _document;
+ XMLNode* _parent;
+ mutable StrPair _value;
+
+ XMLNode* _firstChild;
+ XMLNode* _lastChild;
+
+ XMLNode* _prev;
+ XMLNode* _next;
+
+private:
+ MemPool* _memPool;
+ void Unlink( XMLNode* child );
+};
+
+
+/** XML text.
+
+ Note that a text node can have child element nodes, for example:
+ @verbatim
+ <root>This is <b>bold</b></root>
+ @endverbatim
+
+ A text node can have 2 ways to output the next. "normal" output
+ and CDATA. It will default to the mode it was parsed from the XML file and
+ you generally want to leave it alone, but you can change the output mode with
+ SetCDATA() and query it with CDATA().
+*/
+class XMLText : public XMLNode
+{
+ friend class XMLBase;
+ friend class XMLDocument;
+public:
+ virtual bool Accept( XMLVisitor* visitor ) const;
+
+ virtual XMLText* ToText() {
+ return this;
+ }
+ virtual const XMLText* ToText() const {
+ return this;
+ }
+
+ /// Declare whether this should be CDATA or standard text.
+ void SetCData( bool isCData ) {
+ _isCData = isCData;
+ }
+ /// Returns true if this is a CDATA text element.
+ bool CData() const {
+ return _isCData;
+ }
+
+ char* ParseDeep( char*, StrPair* endTag );
+ virtual XMLNode* ShallowClone( XMLDocument* document ) const;
+ virtual bool ShallowEqual( const XMLNode* compare ) const;
+
+protected:
+ XMLText( XMLDocument* doc ) : XMLNode( doc ), _isCData( false ) {}
+ virtual ~XMLText() {}
+ XMLText( const XMLText& ); // not supported
+ XMLText& operator=( const XMLText& ); // not supported
+
+private:
+ bool _isCData;
+};
+
+
+/** An XML Comment. */
+class XMLComment : public XMLNode
+{
+ friend class XMLDocument;
+public:
+ virtual XMLComment* ToComment() {
+ return this;
+ }
+ virtual const XMLComment* ToComment() const {
+ return this;
+ }
+
+ virtual bool Accept( XMLVisitor* visitor ) const;
+
+ char* ParseDeep( char*, StrPair* endTag );
+ virtual XMLNode* ShallowClone( XMLDocument* document ) const;
+ virtual bool ShallowEqual( const XMLNode* compare ) const;
+
+protected:
+ XMLComment( XMLDocument* doc );
+ virtual ~XMLComment();
+ XMLComment( const XMLComment& ); // not supported
+ XMLComment& operator=( const XMLComment& ); // not supported
+
+private:
+};
+
+
+/** In correct XML the declaration is the first entry in the file.
+ @verbatim
+ <?xml version="1.0" standalone="yes"?>
+ @endverbatim
+
+ TinyXML2 will happily read or write files without a declaration,
+ however.
+
+ The text of the declaration isn't interpreted. It is parsed
+ and written as a string.
+*/
+class XMLDeclaration : public XMLNode
+{
+ friend class XMLDocument;
+public:
+ virtual XMLDeclaration* ToDeclaration() {
+ return this;
+ }
+ virtual const XMLDeclaration* ToDeclaration() const {
+ return this;
+ }
+
+ virtual bool Accept( XMLVisitor* visitor ) const;
+
+ char* ParseDeep( char*, StrPair* endTag );
+ virtual XMLNode* ShallowClone( XMLDocument* document ) const;
+ virtual bool ShallowEqual( const XMLNode* compare ) const;
+
+protected:
+ XMLDeclaration( XMLDocument* doc );
+ virtual ~XMLDeclaration();
+ XMLDeclaration( const XMLDeclaration& ); // not supported
+ XMLDeclaration& operator=( const XMLDeclaration& ); // not supported
+};
+
+
+/** Any tag that tinyXml doesn't recognize is saved as an
+ unknown. It is a tag of text, but should not be modified.
+ It will be written back to the XML, unchanged, when the file
+ is saved.
+
+ DTD tags get thrown into TiXmlUnknowns.
+*/
+class XMLUnknown : public XMLNode
+{
+ friend class XMLDocument;
+public:
+ virtual XMLUnknown* ToUnknown() {
+ return this;
+ }
+ virtual const XMLUnknown* ToUnknown() const {
+ return this;
+ }
+
+ virtual bool Accept( XMLVisitor* visitor ) const;
+
+ char* ParseDeep( char*, StrPair* endTag );
+ virtual XMLNode* ShallowClone( XMLDocument* document ) const;
+ virtual bool ShallowEqual( const XMLNode* compare ) const;
+
+protected:
+ XMLUnknown( XMLDocument* doc );
+ virtual ~XMLUnknown();
+ XMLUnknown( const XMLUnknown& ); // not supported
+ XMLUnknown& operator=( const XMLUnknown& ); // not supported
+};
+
+
+enum XMLError {
+ XML_NO_ERROR = 0,
+ XML_SUCCESS = 0,
+
+ XML_NO_ATTRIBUTE,
+ XML_WRONG_ATTRIBUTE_TYPE,
+
+ XML_ERROR_FILE_NOT_FOUND,
+ XML_ERROR_FILE_COULD_NOT_BE_OPENED,
+ XML_ERROR_FILE_READ_ERROR,
+ XML_ERROR_ELEMENT_MISMATCH,
+ XML_ERROR_PARSING_ELEMENT,
+ XML_ERROR_PARSING_ATTRIBUTE,
+ XML_ERROR_IDENTIFYING_TAG,
+ XML_ERROR_PARSING_TEXT,
+ XML_ERROR_PARSING_CDATA,
+ XML_ERROR_PARSING_COMMENT,
+ XML_ERROR_PARSING_DECLARATION,
+ XML_ERROR_PARSING_UNKNOWN,
+ XML_ERROR_EMPTY_DOCUMENT,
+ XML_ERROR_MISMATCHED_ELEMENT,
+ XML_ERROR_PARSING,
+
+ XML_CAN_NOT_CONVERT_TEXT,
+ XML_NO_TEXT_NODE
+};
+
+
+/** An attribute is a name-value pair. Elements have an arbitrary
+ number of attributes, each with a unique name.
+
+ @note The attributes are not XMLNodes. You may only query the
+ Next() attribute in a list.
+*/
+class XMLAttribute
+{
+ friend class XMLElement;
+public:
+ /// The name of the attribute.
+ const char* Name() const {
+ return _name.GetStr();
+ }
+ /// The value of the attribute.
+ const char* Value() const {
+ return _value.GetStr();
+ }
+ /// The next attribute in the list.
+ const XMLAttribute* Next() const {
+ return _next;
+ }
+
+ /** IntAttribute interprets the attribute as an integer, and returns the value.
+ If the value isn't an integer, 0 will be returned. There is no error checking;
+ use QueryIntAttribute() if you need error checking.
+ */
+ int IntValue() const {
+ int i=0;
+ QueryIntValue( &i );
+ return i;
+ }
+ /// Query as an unsigned integer. See IntAttribute()
+ unsigned UnsignedValue() const {
+ unsigned i=0;
+ QueryUnsignedValue( &i );
+ return i;
+ }
+ /// Query as a boolean. See IntAttribute()
+ bool BoolValue() const {
+ bool b=false;
+ QueryBoolValue( &b );
+ return b;
+ }
+ /// Query as a double. See IntAttribute()
+ double DoubleValue() const {
+ double d=0;
+ QueryDoubleValue( &d );
+ return d;
+ }
+ /// Query as a float. See IntAttribute()
+ float FloatValue() const {
+ float f=0;
+ QueryFloatValue( &f );
+ return f;
+ }
+
+ /** QueryIntAttribute interprets the attribute as an integer, and returns the value
+ in the provided paremeter. The function will return XML_NO_ERROR on success,
+ and XML_WRONG_ATTRIBUTE_TYPE if the conversion is not successful.
+ */
+ XMLError QueryIntValue( int* value ) const;
+ /// See QueryIntAttribute
+ XMLError QueryUnsignedValue( unsigned int* value ) const;
+ /// See QueryIntAttribute
+ XMLError QueryBoolValue( bool* value ) const;
+ /// See QueryIntAttribute
+ XMLError QueryDoubleValue( double* value ) const;
+ /// See QueryIntAttribute
+ XMLError QueryFloatValue( float* value ) const;
+
+ /// Set the attribute to a string value.
+ void SetAttribute( const char* value );
+ /// Set the attribute to value.
+ void SetAttribute( int value );
+ /// Set the attribute to value.
+ void SetAttribute( unsigned value );
+ /// Set the attribute to value.
+ void SetAttribute( bool value );
+ /// Set the attribute to value.
+ void SetAttribute( double value );
+ /// Set the attribute to value.
+ void SetAttribute( float value );
+
+private:
+ enum { BUF_SIZE = 200 };
+
+ XMLAttribute() : _next( 0 ) {}
+ virtual ~XMLAttribute() {}
+
+ XMLAttribute( const XMLAttribute& ); // not supported
+ void operator=( const XMLAttribute& ); // not supported
+ void SetName( const char* name );
+
+ char* ParseDeep( char* p, bool processEntities );
+
+ mutable StrPair _name;
+ mutable StrPair _value;
+ XMLAttribute* _next;
+ MemPool* _memPool;
+};
+
+
+/** The element is a container class. It has a value, the element name,
+ and can contain other elements, text, comments, and unknowns.
+ Elements also contain an arbitrary number of attributes.
+*/
+class XMLElement : public XMLNode
+{
+ friend class XMLBase;
+ friend class XMLDocument;
+public:
+ /// Get the name of an element (which is the Value() of the node.)
+ const char* Name() const {
+ return Value();
+ }
+ /// Set the name of the element.
+ void SetName( const char* str, bool staticMem=false ) {
+ SetValue( str, staticMem );
+ }
+
+ virtual XMLElement* ToElement() {
+ return this;
+ }
+ virtual const XMLElement* ToElement() const {
+ return this;
+ }
+ virtual bool Accept( XMLVisitor* visitor ) const;
+
+ /** Given an attribute name, Attribute() returns the value
+ for the attribute of that name, or null if none
+ exists. For example:
+
+ @verbatim
+ const char* value = ele->Attribute( "foo" );
+ @endverbatim
+
+ The 'value' parameter is normally null. However, if specified,
+ the attribute will only be returned if the 'name' and 'value'
+ match. This allow you to write code:
+
+ @verbatim
+ if ( ele->Attribute( "foo", "bar" ) ) callFooIsBar();
+ @endverbatim
+
+ rather than:
+ @verbatim
+ if ( ele->Attribute( "foo" ) ) {
+ if ( strcmp( ele->Attribute( "foo" ), "bar" ) == 0 ) callFooIsBar();
+ }
+ @endverbatim
+ */
+ const char* Attribute( const char* name, const char* value=0 ) const;
+
+ /** Given an attribute name, IntAttribute() returns the value
+ of the attribute interpreted as an integer. 0 will be
+ returned if there is an error. For a method with error
+ checking, see QueryIntAttribute()
+ */
+ int IntAttribute( const char* name ) const {
+ int i=0;
+ QueryIntAttribute( name, &i );
+ return i;
+ }
+ /// See IntAttribute()
+ unsigned UnsignedAttribute( const char* name ) const {
+ unsigned i=0;
+ QueryUnsignedAttribute( name, &i );
+ return i;
+ }
+ /// See IntAttribute()
+ bool BoolAttribute( const char* name ) const {
+ bool b=false;
+ QueryBoolAttribute( name, &b );
+ return b;
+ }
+ /// See IntAttribute()
+ double DoubleAttribute( const char* name ) const {
+ double d=0;
+ QueryDoubleAttribute( name, &d );
+ return d;
+ }
+ /// See IntAttribute()
+ float FloatAttribute( const char* name ) const {
+ float f=0;
+ QueryFloatAttribute( name, &f );
+ return f;
+ }
+
+ /** Given an attribute name, QueryIntAttribute() returns
+ XML_NO_ERROR, XML_WRONG_ATTRIBUTE_TYPE if the conversion
+ can't be performed, or XML_NO_ATTRIBUTE if the attribute
+ doesn't exist. If successful, the result of the conversion
+ will be written to 'value'. If not successful, nothing will
+ be written to 'value'. This allows you to provide default
+ value:
+
+ @verbatim
+ int value = 10;
+ QueryIntAttribute( "foo", &value ); // if "foo" isn't found, value will still be 10
+ @endverbatim
+ */
+ XMLError QueryIntAttribute( const char* name, int* value ) const {
+ const XMLAttribute* a = FindAttribute( name );
+ if ( !a ) {
+ return XML_NO_ATTRIBUTE;
+ }
+ return a->QueryIntValue( value );
+ }
+ /// See QueryIntAttribute()
+ XMLError QueryUnsignedAttribute( const char* name, unsigned int* value ) const {
+ const XMLAttribute* a = FindAttribute( name );
+ if ( !a ) {
+ return XML_NO_ATTRIBUTE;
+ }
+ return a->QueryUnsignedValue( value );
+ }
+ /// See QueryIntAttribute()
+ XMLError QueryBoolAttribute( const char* name, bool* value ) const {
+ const XMLAttribute* a = FindAttribute( name );
+ if ( !a ) {
+ return XML_NO_ATTRIBUTE;
+ }
+ return a->QueryBoolValue( value );
+ }
+ /// See QueryIntAttribute()
+ XMLError QueryDoubleAttribute( const char* name, double* value ) const {
+ const XMLAttribute* a = FindAttribute( name );
+ if ( !a ) {
+ return XML_NO_ATTRIBUTE;
+ }
+ return a->QueryDoubleValue( value );
+ }
+ /// See QueryIntAttribute()
+ XMLError QueryFloatAttribute( const char* name, float* value ) const {
+ const XMLAttribute* a = FindAttribute( name );
+ if ( !a ) {
+ return XML_NO_ATTRIBUTE;
+ }
+ return a->QueryFloatValue( value );
+ }
+
+ /// Sets the named attribute to value.
+ void SetAttribute( const char* name, const char* value ) {
+ XMLAttribute* a = FindOrCreateAttribute( name );
+ a->SetAttribute( value );
+ }
+ /// Sets the named attribute to value.
+ void SetAttribute( const char* name, int value ) {
+ XMLAttribute* a = FindOrCreateAttribute( name );
+ a->SetAttribute( value );
+ }
+ /// Sets the named attribute to value.
+ void SetAttribute( const char* name, unsigned value ) {
+ XMLAttribute* a = FindOrCreateAttribute( name );
+ a->SetAttribute( value );
+ }
+ /// Sets the named attribute to value.
+ void SetAttribute( const char* name, bool value ) {
+ XMLAttribute* a = FindOrCreateAttribute( name );
+ a->SetAttribute( value );
+ }
+ /// Sets the named attribute to value.
+ void SetAttribute( const char* name, double value ) {
+ XMLAttribute* a = FindOrCreateAttribute( name );
+ a->SetAttribute( value );
+ }
+
+ /**
+ Delete an attribute.
+ */
+ void DeleteAttribute( const char* name );
+
+ /// Return the first attribute in the list.
+ const XMLAttribute* FirstAttribute() const {
+ return _rootAttribute;
+ }
+ /// Query a specific attribute in the list.
+ const XMLAttribute* FindAttribute( const char* name ) const;
+
+ /** Convenience function for easy access to the text inside an element. Although easy
+ and concise, GetText() is limited compared to getting the TiXmlText child
+ and accessing it directly.
+
+ If the first child of 'this' is a TiXmlText, the GetText()
+ returns the character string of the Text node, else null is returned.
+
+ This is a convenient method for getting the text of simple contained text:
+ @verbatim
+ <foo>This is text</foo>
+ const char* str = fooElement->GetText();
+ @endverbatim
+
+ 'str' will be a pointer to "This is text".
+
+ Note that this function can be misleading. If the element foo was created from
+ this XML:
+ @verbatim
+ <foo><b>This is text</b></foo>
+ @endverbatim
+
+ then the value of str would be null. The first child node isn't a text node, it is
+ another element. From this XML:
+ @verbatim
+ <foo>This is <b>text</b></foo>
+ @endverbatim
+ GetText() will return "This is ".
+ */
+ const char* GetText() const;
+
+ /**
+ Convenience method to query the value of a child text node. This is probably best
+ shown by example. Given you have a document is this form:
+ @verbatim
+ <point>
+ <x>1</x>
+ <y>1.4</y>
+ </point>
+ @endverbatim
+
+ The QueryIntText() and similar functions provide a safe and easier way to get to the
+ "value" of x and y.
+
+ @verbatim
+ int x = 0;
+ float y = 0; // types of x and y are contrived for example
+ const XMLElement* xElement = pointElement->FirstChildElement( "x" );
+ const XMLElement* yElement = pointElement->FirstChildElement( "y" );
+ xElement->QueryIntText( &x );
+ yElement->QueryFloatText( &y );
+ @endverbatim
+
+ @returns XML_SUCCESS (0) on success, XML_CAN_NOT_CONVERT_TEXT if the text cannot be converted
+ to the requested type, and XML_NO_TEXT_NODE if there is no child text to query.
+
+ */
+ XMLError QueryIntText( int* ival ) const;
+ /// See QueryIntText()
+ XMLError QueryUnsignedText( unsigned* uval ) const;
+ /// See QueryIntText()
+ XMLError QueryBoolText( bool* bval ) const;
+ /// See QueryIntText()
+ XMLError QueryDoubleText( double* dval ) const;
+ /// See QueryIntText()
+ XMLError QueryFloatText( float* fval ) const;
+
+ // internal:
+ enum {
+ OPEN, // <foo>
+ CLOSED, // <foo/>
+ CLOSING // </foo>
+ };
+ int ClosingType() const {
+ return _closingType;
+ }
+ char* ParseDeep( char* p, StrPair* endTag );
+ virtual XMLNode* ShallowClone( XMLDocument* document ) const;
+ virtual bool ShallowEqual( const XMLNode* compare ) const;
+
+private:
+ XMLElement( XMLDocument* doc );
+ virtual ~XMLElement();
+ XMLElement( const XMLElement& ); // not supported
+ void operator=( const XMLElement& ); // not supported
+
+ XMLAttribute* FindAttribute( const char* name );
+ XMLAttribute* FindOrCreateAttribute( const char* name );
+ //void LinkAttribute( XMLAttribute* attrib );
+ char* ParseAttributes( char* p );
+
+ int _closingType;
+ // The attribute list is ordered; there is no 'lastAttribute'
+ // because the list needs to be scanned for dupes before adding
+ // a new attribute.
+ XMLAttribute* _rootAttribute;
+};
+
+
+enum Whitespace {
+ PRESERVE_WHITESPACE,
+ COLLAPSE_WHITESPACE
+};
+
+
+/** A Document binds together all the functionality.
+ It can be saved, loaded, and printed to the screen.
+ All Nodes are connected and allocated to a Document.
+ If the Document is deleted, all its Nodes are also deleted.
+*/
+class XMLDocument : public XMLNode
+{
+ friend class XMLElement;
+public:
+ /// constructor
+ XMLDocument( bool processEntities = true, Whitespace = PRESERVE_WHITESPACE );
+ ~XMLDocument();
+
+ virtual XMLDocument* ToDocument() {
+ return this;
+ }
+ virtual const XMLDocument* ToDocument() const {
+ return this;
+ }
+
+ /**
+ Parse an XML file from a character string.
+ Returns XML_NO_ERROR (0) on success, or
+ an errorID.
+
+ You may optionally pass in the 'nBytes', which is
+ the number of bytes which will be parsed. If not
+ specified, TinyXML will assume 'xml' points to a
+ null terminated string.
+ */
+ XMLError Parse( const char* xml, size_t nBytes=(size_t)(-1) );
+
+ /**
+ Load an XML file from disk.
+ Returns XML_NO_ERROR (0) on success, or
+ an errorID.
+ */
+ XMLError LoadFile( const char* filename );
+
+ /**
+ Load an XML file from disk. You are responsible
+ for providing and closing the FILE*.
+
+ Returns XML_NO_ERROR (0) on success, or
+ an errorID.
+ */
+ XMLError LoadFile( FILE* );
+
+ /**
+ Save the XML file to disk.
+ Returns XML_NO_ERROR (0) on success, or
+ an errorID.
+ */
+ XMLError SaveFile( const char* filename, bool compact = false );
+
+ /**
+ Save the XML file to disk. You are responsible
+ for providing and closing the FILE*.
+
+ Returns XML_NO_ERROR (0) on success, or
+ an errorID.
+ */
+ XMLError SaveFile( FILE* fp, bool compact = false );
+
+ bool ProcessEntities() const {
+ return _processEntities;
+ }
+ Whitespace WhitespaceMode() const {
+ return _whitespace;
+ }
+
+ /**
+ Returns true if this document has a leading Byte Order Mark of UTF8.
+ */
+ bool HasBOM() const {
+ return _writeBOM;
+ }
+ /** Sets whether to write the BOM when writing the file.
+ */
+ void SetBOM( bool useBOM ) {
+ _writeBOM = useBOM;
+ }
+
+ /** Return the root element of DOM. Equivalent to FirstChildElement().
+ To get the first node, use FirstChild().
+ */
+ XMLElement* RootElement() {
+ return FirstChildElement();
+ }
+ const XMLElement* RootElement() const {
+ return FirstChildElement();
+ }
+
+ /** Print the Document. If the Printer is not provided, it will
+ print to stdout. If you provide Printer, this can print to a file:
+ @verbatim
+ XMLPrinter printer( fp );
+ doc.Print( &printer );
+ @endverbatim
+
+ Or you can use a printer to print to memory:
+ @verbatim
+ XMLPrinter printer;
+ doc->Print( &printer );
+ // printer.CStr() has a const char* to the XML
+ @endverbatim
+ */
+ void Print( XMLPrinter* streamer=0 );
+ virtual bool Accept( XMLVisitor* visitor ) const;
+
+ /**
+ Create a new Element associated with
+ this Document. The memory for the Element
+ is managed by the Document.
+ */
+ XMLElement* NewElement( const char* name );
+ /**
+ Create a new Comment associated with
+ this Document. The memory for the Comment
+ is managed by the Document.
+ */
+ XMLComment* NewComment( const char* comment );
+ /**
+ Create a new Text associated with
+ this Document. The memory for the Text
+ is managed by the Document.
+ */
+ XMLText* NewText( const char* text );
+ /**
+ Create a new Declaration associated with
+ this Document. The memory for the object
+ is managed by the Document.
+
+ If the 'text' param is null, the standard
+ declaration is used.:
+ @verbatim
+ <?xml version="1.0" encoding="UTF-8"?>
+ @endverbatim
+ */
+ XMLDeclaration* NewDeclaration( const char* text=0 );
+ /**
+ Create a new Unknown associated with
+ this Document. The memory forthe object
+ is managed by the Document.
+ */
+ XMLUnknown* NewUnknown( const char* text );
+
+ /**
+ Delete a node associated with this document.
+ It will be unlinked from the DOM.
+ */
+ void DeleteNode( XMLNode* node ) {
+ node->_parent->DeleteChild( node );
+ }
+
+ void SetError( XMLError error, const char* str1, const char* str2 );
+
+ /// Return true if there was an error parsing the document.
+ bool Error() const {
+ return _errorID != XML_NO_ERROR;
+ }
+ /// Return the errorID.
+ XMLError ErrorID() const {
+ return _errorID;
+ }
+ /// Return a possibly helpful diagnostic location or string.
+ const char* GetErrorStr1() const {
+ return _errorStr1;
+ }
+ /// Return a possibly helpful secondary diagnostic location or string.
+ const char* GetErrorStr2() const {
+ return _errorStr2;
+ }
+ /// If there is an error, print it to stdout.
+ void PrintError() const;
+
+ // internal
+ char* Identify( char* p, XMLNode** node );
+
+ virtual XMLNode* ShallowClone( XMLDocument* /*document*/ ) const {
+ return 0;
+ }
+ virtual bool ShallowEqual( const XMLNode* /*compare*/ ) const {
+ return false;
+ }
+
+private:
+ XMLDocument( const XMLDocument& ); // not supported
+ void operator=( const XMLDocument& ); // not supported
+ void InitDocument();
+
+ bool _writeBOM;
+ bool _processEntities;
+ XMLError _errorID;
+ Whitespace _whitespace;
+ const char* _errorStr1;
+ const char* _errorStr2;
+ char* _charBuffer;
+
+ MemPoolT< sizeof(XMLElement) > _elementPool;
+ MemPoolT< sizeof(XMLAttribute) > _attributePool;
+ MemPoolT< sizeof(XMLText) > _textPool;
+ MemPoolT< sizeof(XMLComment) > _commentPool;
+};
+
+
+/**
+ A XMLHandle is a class that wraps a node pointer with null checks; this is
+ an incredibly useful thing. Note that XMLHandle is not part of the TinyXML
+ DOM structure. It is a separate utility class.
+
+ Take an example:
+ @verbatim
+ <Document>
+ <Element attributeA = "valueA">
+ <Child attributeB = "value1" />
+ <Child attributeB = "value2" />
+ </Element>
+ </Document>
+ @endverbatim
+
+ Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very
+ easy to write a *lot* of code that looks like:
+
+ @verbatim
+ XMLElement* root = document.FirstChildElement( "Document" );
+ if ( root )
+ {
+ XMLElement* element = root->FirstChildElement( "Element" );
+ if ( element )
+ {
+ XMLElement* child = element->FirstChildElement( "Child" );
+ if ( child )
+ {
+ XMLElement* child2 = child->NextSiblingElement( "Child" );
+ if ( child2 )
+ {
+ // Finally do something useful.
+ @endverbatim
+
+ And that doesn't even cover "else" cases. XMLHandle addresses the verbosity
+ of such code. A XMLHandle checks for null pointers so it is perfectly safe
+ and correct to use:
+
+ @verbatim
+ XMLHandle docHandle( &document );
+ XMLElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).FirstChild().NextSibling().ToElement();
+ if ( child2 )
+ {
+ // do something useful
+ @endverbatim
+
+ Which is MUCH more concise and useful.
+
+ It is also safe to copy handles - internally they are nothing more than node pointers.
+ @verbatim
+ XMLHandle handleCopy = handle;
+ @endverbatim
+
+ See also XMLConstHandle, which is the same as XMLHandle, but operates on const objects.
+*/
+class XMLHandle
+{
+public:
+ /// Create a handle from any node (at any depth of the tree.) This can be a null pointer.
+ XMLHandle( XMLNode* node ) {
+ _node = node;
+ }
+ /// Create a handle from a node.
+ XMLHandle( XMLNode& node ) {
+ _node = &node;
+ }
+ /// Copy constructor
+ XMLHandle( const XMLHandle& ref ) {
+ _node = ref._node;
+ }
+ /// Assignment
+ XMLHandle& operator=( const XMLHandle& ref ) {
+ _node = ref._node;
+ return *this;
+ }
+
+ /// Get the first child of this handle.
+ XMLHandle FirstChild() {
+ return XMLHandle( _node ? _node->FirstChild() : 0 );
+ }
+ /// Get the first child element of this handle.
+ XMLHandle FirstChildElement( const char* value=0 ) {
+ return XMLHandle( _node ? _node->FirstChildElement( value ) : 0 );
+ }
+ /// Get the last child of this handle.
+ XMLHandle LastChild() {
+ return XMLHandle( _node ? _node->LastChild() : 0 );
+ }
+ /// Get the last child element of this handle.
+ XMLHandle LastChildElement( const char* _value=0 ) {
+ return XMLHandle( _node ? _node->LastChildElement( _value ) : 0 );
+ }
+ /// Get the previous sibling of this handle.
+ XMLHandle PreviousSibling() {
+ return XMLHandle( _node ? _node->PreviousSibling() : 0 );
+ }
+ /// Get the previous sibling element of this handle.
+ XMLHandle PreviousSiblingElement( const char* _value=0 ) {
+ return XMLHandle( _node ? _node->PreviousSiblingElement( _value ) : 0 );
+ }
+ /// Get the next sibling of this handle.
+ XMLHandle NextSibling() {
+ return XMLHandle( _node ? _node->NextSibling() : 0 );
+ }
+ /// Get the next sibling element of this handle.
+ XMLHandle NextSiblingElement( const char* _value=0 ) {
+ return XMLHandle( _node ? _node->NextSiblingElement( _value ) : 0 );
+ }
+
+ /// Safe cast to XMLNode. This can return null.
+ XMLNode* ToNode() {
+ return _node;
+ }
+ /// Safe cast to XMLElement. This can return null.
+ XMLElement* ToElement() {
+ return ( ( _node && _node->ToElement() ) ? _node->ToElement() : 0 );
+ }
+ /// Safe cast to XMLText. This can return null.
+ XMLText* ToText() {
+ return ( ( _node && _node->ToText() ) ? _node->ToText() : 0 );
+ }
+ /// Safe cast to XMLUnknown. This can return null.
+ XMLUnknown* ToUnknown() {
+ return ( ( _node && _node->ToUnknown() ) ? _node->ToUnknown() : 0 );
+ }
+ /// Safe cast to XMLDeclaration. This can return null.
+ XMLDeclaration* ToDeclaration() {
+ return ( ( _node && _node->ToDeclaration() ) ? _node->ToDeclaration() : 0 );
+ }
+
+private:
+ XMLNode* _node;
+};
+
+
+/**
+ A variant of the XMLHandle class for working with const XMLNodes and Documents. It is the
+ same in all regards, except for the 'const' qualifiers. See XMLHandle for API.
+*/
+class XMLConstHandle
+{
+public:
+ XMLConstHandle( const XMLNode* node ) {
+ _node = node;
+ }
+ XMLConstHandle( const XMLNode& node ) {
+ _node = &node;
+ }
+ XMLConstHandle( const XMLConstHandle& ref ) {
+ _node = ref._node;
+ }
+
+ XMLConstHandle& operator=( const XMLConstHandle& ref ) {
+ _node = ref._node;
+ return *this;
+ }
+
+ const XMLConstHandle FirstChild() const {
+ return XMLConstHandle( _node ? _node->FirstChild() : 0 );
+ }
+ const XMLConstHandle FirstChildElement( const char* value=0 ) const {
+ return XMLConstHandle( _node ? _node->FirstChildElement( value ) : 0 );
+ }
+ const XMLConstHandle LastChild() const {
+ return XMLConstHandle( _node ? _node->LastChild() : 0 );
+ }
+ const XMLConstHandle LastChildElement( const char* _value=0 ) const {
+ return XMLConstHandle( _node ? _node->LastChildElement( _value ) : 0 );
+ }
+ const XMLConstHandle PreviousSibling() const {
+ return XMLConstHandle( _node ? _node->PreviousSibling() : 0 );
+ }
+ const XMLConstHandle PreviousSiblingElement( const char* _value=0 ) const {
+ return XMLConstHandle( _node ? _node->PreviousSiblingElement( _value ) : 0 );
+ }
+ const XMLConstHandle NextSibling() const {
+ return XMLConstHandle( _node ? _node->NextSibling() : 0 );
+ }
+ const XMLConstHandle NextSiblingElement( const char* _value=0 ) const {
+ return XMLConstHandle( _node ? _node->NextSiblingElement( _value ) : 0 );
+ }
+
+
+ const XMLNode* ToNode() const {
+ return _node;
+ }
+ const XMLElement* ToElement() const {
+ return ( ( _node && _node->ToElement() ) ? _node->ToElement() : 0 );
+ }
+ const XMLText* ToText() const {
+ return ( ( _node && _node->ToText() ) ? _node->ToText() : 0 );
+ }
+ const XMLUnknown* ToUnknown() const {
+ return ( ( _node && _node->ToUnknown() ) ? _node->ToUnknown() : 0 );
+ }
+ const XMLDeclaration* ToDeclaration() const {
+ return ( ( _node && _node->ToDeclaration() ) ? _node->ToDeclaration() : 0 );
+ }
+
+private:
+ const XMLNode* _node;
+};
+
+
+/**
+ Printing functionality. The XMLPrinter gives you more
+ options than the XMLDocument::Print() method.
+
+ It can:
+ -# Print to memory.
+ -# Print to a file you provide.
+ -# Print XML without a XMLDocument.
+
+ Print to Memory
+
+ @verbatim
+ XMLPrinter printer;
+ doc->Print( &printer );
+ SomeFunction( printer.CStr() );
+ @endverbatim
+
+ Print to a File
+
+ You provide the file pointer.
+ @verbatim
+ XMLPrinter printer( fp );
+ doc.Print( &printer );
+ @endverbatim
+
+ Print without a XMLDocument
+
+ When loading, an XML parser is very useful. However, sometimes
+ when saving, it just gets in the way. The code is often set up
+ for streaming, and constructing the DOM is just overhead.
+
+ The Printer supports the streaming case. The following code
+ prints out a trivially simple XML file without ever creating
+ an XML document.
+
+ @verbatim
+ XMLPrinter printer( fp );
+ printer.OpenElement( "foo" );
+ printer.PushAttribute( "foo", "bar" );
+ printer.CloseElement();
+ @endverbatim
+*/
+class XMLPrinter : public XMLVisitor
+{
+public:
+ /** Construct the printer. If the FILE* is specified,
+ this will print to the FILE. Else it will print
+ to memory, and the result is available in CStr().
+ If 'compact' is set to true, then output is created
+ with only required whitespace and newlines.
+ */
+ XMLPrinter( FILE* file=0, bool compact = false );
+ ~XMLPrinter() {}
+
+ /** If streaming, write the BOM and declaration. */
+ void PushHeader( bool writeBOM, bool writeDeclaration );
+ /** If streaming, start writing an element.
+ The element must be closed with CloseElement()
+ */
+ void OpenElement( const char* name );
+ /// If streaming, add an attribute to an open element.
+ void PushAttribute( const char* name, const char* value );
+ void PushAttribute( const char* name, int value );
+ void PushAttribute( const char* name, unsigned value );
+ void PushAttribute( const char* name, bool value );
+ void PushAttribute( const char* name, double value );
+ /// If streaming, close the Element.
+ void CloseElement();
+
+ /// Add a text node.
+ void PushText( const char* text, bool cdata=false );
+ /// Add a text node from an integer.
+ void PushText( int value );
+ /// Add a text node from an unsigned.
+ void PushText( unsigned value );
+ /// Add a text node from a bool.
+ void PushText( bool value );
+ /// Add a text node from a float.
+ void PushText( float value );
+ /// Add a text node from a double.
+ void PushText( double value );
+
+ /// Add a comment
+ void PushComment( const char* comment );
+
+ void PushDeclaration( const char* value );
+ void PushUnknown( const char* value );
+
+ virtual bool VisitEnter( const XMLDocument& /*doc*/ );
+ virtual bool VisitExit( const XMLDocument& /*doc*/ ) {
+ return true;
+ }
+
+ virtual bool VisitEnter( const XMLElement& element, const XMLAttribute* attribute );
+ virtual bool VisitExit( const XMLElement& element );
+
+ virtual bool Visit( const XMLText& text );
+ virtual bool Visit( const XMLComment& comment );
+ virtual bool Visit( const XMLDeclaration& declaration );
+ virtual bool Visit( const XMLUnknown& unknown );
+
+ /**
+ If in print to memory mode, return a pointer to
+ the XML file in memory.
+ */
+ const char* CStr() const {
+ return _buffer.Mem();
+ }
+ /**
+ If in print to memory mode, return the size
+ of the XML file in memory. (Note the size returned
+ includes the terminating null.)
+ */
+ int CStrSize() const {
+ return _buffer.Size();
+ }
+
+private:
+ void SealElement();
+ void PrintSpace( int depth );
+ void PrintString( const char*, bool restrictedEntitySet ); // prints out, after detecting entities.
+ void Print( const char* format, ... );
+
+ bool _elementJustOpened;
+ bool _firstElement;
+ FILE* _fp;
+ int _depth;
+ int _textDepth;
+ bool _processEntities;
+ bool _compactMode;
+
+ enum {
+ ENTITY_RANGE = 64,
+ BUF_SIZE = 200
+ };
+ bool _entityFlag[ENTITY_RANGE];
+ bool _restrictedEntityFlag[ENTITY_RANGE];
+
+ DynArray< const char*, 10 > _stack;
+ DynArray< char, 20 > _buffer;
+#ifdef _MSC_VER
+ DynArray< char, 20 > _accumulator;
+#endif
+};
+
+
+} // tinyxml2
+
+
+#endif // TINYXML2_INCLUDED
diff --git a/LibOVR/Include/OVR.h b/LibOVR/Include/OVR.h
new file mode 100644
index 0000000..7c2b335
--- /dev/null
+++ b/LibOVR/Include/OVR.h
@@ -0,0 +1,33 @@
+/************************************************************************************
+
+Filename : OVR.h
+Content : This contains references to all OVR-specific headers in Src folder.
+ Should be generated automatically based on PublicHeader tags.
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_h
+#define OVR_h
+
+#include "../Src/Kernel/OVR_Allocator.h"
+#include "../Src/Kernel/OVR_Log.h"
+#include "../Src/Kernel/OVR_Math.h"
+#include "../Src/Kernel/OVR_System.h"
+#include "../Src/Kernel/OVR_Types.h"
+#include "../Src/OVR_Device.h"
+#include "../Src/OVR_DeviceConstants.h"
+#include "../Src/OVR_DeviceHandle.h"
+#include "../Src/OVR_DeviceMessages.h"
+#include "../Src/OVR_SensorFusion.h"
+#include "../Src/Util/Util_LatencyTest.h"
+#include "../Src/Util/Util_Render_Stereo.h"
+#include "../Src/Util/Util_MagCalibration.h"
+
+#endif
+
diff --git a/LibOVR/Include/OVRVersion.h b/LibOVR/Include/OVRVersion.h
new file mode 100644
index 0000000..ce4c30c
--- /dev/null
+++ b/LibOVR/Include/OVRVersion.h
@@ -0,0 +1,22 @@
+/************************************************************************************
+
+Filename : OVRVersion.h
+Content :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef _OVR_VERSION_H
+#define _OVR_VERSION_H
+
+#define OVR_MAJOR_VERSION 0
+#define OVR_MINOR_VERSION 2
+#define OVR_BUILD_VERSION 2
+#define OVR_VERSION_STRING "0.2.2"
+
+#endif
diff --git a/LibOVR/Projects/Win32/LibOVR_Msvc2010.sln b/LibOVR/Projects/Win32/LibOVR_Msvc2010.sln
new file mode 100644
index 0000000..305c332
--- /dev/null
+++ b/LibOVR/Projects/Win32/LibOVR_Msvc2010.sln
@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual Studio 2010
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LibOVR", "LibOVR_Msvc2010.vcxproj", "{934B40C7-F40A-4E4C-97A7-B9659BE0A441}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {934B40C7-F40A-4E4C-97A7-B9659BE0A441}.Debug|Win32.ActiveCfg = Debug|Win32
+ {934B40C7-F40A-4E4C-97A7-B9659BE0A441}.Debug|Win32.Build.0 = Debug|Win32
+ {934B40C7-F40A-4E4C-97A7-B9659BE0A441}.Release|Win32.ActiveCfg = Release|Win32
+ {934B40C7-F40A-4E4C-97A7-B9659BE0A441}.Release|Win32.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/LibOVR/Projects/Win32/LibOVR_Msvc2010.vcxproj b/LibOVR/Projects/Win32/LibOVR_Msvc2010.vcxproj
new file mode 100644
index 0000000..90f7bdd
--- /dev/null
+++ b/LibOVR/Projects/Win32/LibOVR_Msvc2010.vcxproj
@@ -0,0 +1,275 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\Include\OVR.h" />
+ <ClInclude Include="..\..\Src\Kernel\OVR_Alg.h" />
+ <ClInclude Include="..\..\Src\Kernel\OVR_Allocator.h" />
+ <ClInclude Include="..\..\Src\Kernel\OVR_Array.h" />
+ <ClInclude Include="..\..\Src\Kernel\OVR_Atomic.h" />
+ <ClInclude Include="..\..\Src\Kernel\OVR_Color.h" />
+ <ClInclude Include="..\..\Src\Kernel\OVR_ContainerAllocator.h" />
+ <ClInclude Include="..\..\Src\Kernel\OVR_File.h" />
+ <ClInclude Include="..\..\Src\Kernel\OVR_Hash.h" />
+ <ClInclude Include="..\..\Src\Kernel\OVR_KeyCodes.h" />
+ <ClInclude Include="..\..\Src\Kernel\OVR_List.h" />
+ <ClInclude Include="..\..\Src\Kernel\OVR_Log.h" />
+ <ClInclude Include="..\..\Src\Kernel\OVR_Math.h" />
+ <ClInclude Include="..\..\Src\Kernel\OVR_RefCount.h" />
+ <ClInclude Include="..\..\Src\Kernel\OVR_Std.h" />
+ <ClInclude Include="..\..\Src\Kernel\OVR_String.h" />
+ <ClInclude Include="..\..\Src\Kernel\OVR_StringHash.h" />
+ <ClInclude Include="..\..\Src\Kernel\OVR_SysFile.h" />
+ <ClInclude Include="..\..\Src\Kernel\OVR_System.h" />
+ <ClInclude Include="..\..\Src\Kernel\OVR_Threads.h" />
+ <ClInclude Include="..\..\Src\Kernel\OVR_Timer.h" />
+ <ClInclude Include="..\..\Src\Kernel\OVR_Types.h" />
+ <ClInclude Include="..\..\Src\Kernel\OVR_UTF8Util.h" />
+ <ClInclude Include="..\..\Src\OVR_Device.h" />
+ <ClInclude Include="..\..\Src\OVR_DeviceConstants.h" />
+ <ClInclude Include="..\..\Src\OVR_DeviceHandle.h" />
+ <ClInclude Include="..\..\Src\OVR_DeviceImpl.h" />
+ <ClInclude Include="..\..\Src\OVR_DeviceMessages.h" />
+ <ClInclude Include="..\..\Src\OVR_HIDDevice.h" />
+ <ClInclude Include="..\..\Src\OVR_HIDDeviceBase.h" />
+ <ClInclude Include="..\..\Src\OVR_HIDDeviceImpl.h" />
+ <ClInclude Include="..\..\Src\OVR_LatencyTestImpl.h" />
+ <ClInclude Include="..\..\Src\OVR_SensorFilter.h" />
+ <ClInclude Include="..\..\Src\Util\Util_LatencyTest.h" />
+ <ClInclude Include="..\..\Src\OVR_SensorFusion.h" />
+ <ClInclude Include="..\..\Src\OVR_SensorImpl.h" />
+ <ClInclude Include="..\..\Src\OVR_ThreadCommandQueue.h" />
+ <ClInclude Include="..\..\Src\OVR_Win32_DeviceManager.h" />
+ <ClInclude Include="..\..\Src\OVR_Win32_DeviceStatus.h" />
+ <ClInclude Include="..\..\Src\OVR_Win32_HIDDevice.h" />
+ <ClInclude Include="..\..\Src\OVR_Win32_HMDDevice.h" />
+ <ClInclude Include="..\..\Src\OVR_Win32_SensorDevice.h" />
+ <ClInclude Include="..\..\Src\Util\Util_MagCalibration.h" />
+ <ClInclude Include="..\..\Src\Util\Util_Render_Stereo.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\Src\Kernel\OVR_Alg.cpp" />
+ <ClCompile Include="..\..\Src\Kernel\OVR_Allocator.cpp" />
+ <ClCompile Include="..\..\Src\Kernel\OVR_Atomic.cpp" />
+ <ClCompile Include="..\..\Src\Kernel\OVR_File.cpp" />
+ <ClCompile Include="..\..\Src\Kernel\OVR_FileFILE.cpp" />
+ <ClCompile Include="..\..\Src\Kernel\OVR_Log.cpp" />
+ <ClCompile Include="..\..\Src\Kernel\OVR_Math.cpp" />
+ <ClCompile Include="..\..\Src\Kernel\OVR_RefCount.cpp" />
+ <ClCompile Include="..\..\Src\Kernel\OVR_Std.cpp" />
+ <ClCompile Include="..\..\Src\Kernel\OVR_String.cpp" />
+ <ClCompile Include="..\..\Src\Kernel\OVR_String_FormatUtil.cpp" />
+ <ClCompile Include="..\..\Src\Kernel\OVR_String_PathUtil.cpp" />
+ <ClCompile Include="..\..\Src\Kernel\OVR_SysFile.cpp" />
+ <ClCompile Include="..\..\Src\Kernel\OVR_System.cpp" />
+ <ClCompile Include="..\..\Src\Kernel\OVR_ThreadsWinAPI.cpp" />
+ <ClCompile Include="..\..\Src\Kernel\OVR_Timer.cpp" />
+ <ClCompile Include="..\..\Src\Kernel\OVR_UTF8Util.cpp" />
+ <ClCompile Include="..\..\Src\OVR_DeviceHandle.cpp" />
+ <ClCompile Include="..\..\Src\OVR_DeviceImpl.cpp" />
+ <ClCompile Include="..\..\Src\OVR_LatencyTestImpl.cpp" />
+ <ClCompile Include="..\..\Src\OVR_SensorFilter.cpp" />
+ <ClCompile Include="..\..\Src\OVR_SensorFusion.cpp" />
+ <ClCompile Include="..\..\Src\OVR_SensorImpl.cpp" />
+ <ClCompile Include="..\..\Src\OVR_ThreadCommandQueue.cpp" />
+ <ClCompile Include="..\..\Src\OVR_Win32_DeviceManager.cpp" />
+ <ClCompile Include="..\..\Src\OVR_Win32_DeviceStatus.cpp" />
+ <ClCompile Include="..\..\Src\OVR_Win32_HIDDevice.cpp" />
+ <ClCompile Include="..\..\Src\OVR_Win32_HMDDevice.cpp" />
+ <ClCompile Include="..\..\Src\Util\Util_LatencyTest.cpp" />
+ <ClCompile Include="..\..\Src\OVR_Win32_SensorDevice.cpp" />
+ <ClCompile Include="..\..\Src\Util\Util_MagCalibration.cpp" />
+ <ClCompile Include="..\..\Src\Util\Util_Render_Stereo.cpp" />
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{934B40C7-F40A-4E4C-97A7-B9659BE0A441}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>LibOVR</RootNamespace>
+ <ProjectName>LibOVR</ProjectName>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <OutDir>../../Lib/$(Platform)/</OutDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <OutDir>../../Lib/$(Platform)/</OutDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <IntDir>../../Obj/$(Platform)/$(Configuration)/</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <IntDir>../../Obj/$(Platform)/$(Configuration)/</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <TargetName>libovrd</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <TargetName>libovr64d</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <OutDir>../../Lib/$(Platform)/</OutDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <OutDir>../../Lib/$(Platform)/</OutDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <IntDir>../../Obj/$(Platform)/$(Configuration)/</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <IntDir>../../Obj/$(Platform)/$(Configuration)/</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <TargetName>libovr</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <TargetName>libovr64</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level4</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>OVR_BUILD_DEBUG;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <DebugInformationFormat>OldStyle</DebugInformationFormat>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <MinimalRebuild>false</MinimalRebuild>
+ <OmitDefaultLibName>true</OmitDefaultLibName>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ <Lib>
+ <AdditionalDependencies>Setupapi.lib</AdditionalDependencies>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level4</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>OVR_BUILD_DEBUG;WIN32;_WIN64;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <DebugInformationFormat>OldStyle</DebugInformationFormat>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <MinimalRebuild>false</MinimalRebuild>
+ <OmitDefaultLibName>true</OmitDefaultLibName>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ <Lib>
+ <AdditionalDependencies>Setupapi.lib</AdditionalDependencies>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level4</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <OmitDefaultLibName>true</OmitDefaultLibName>
+ <DebugInformationFormat>OldStyle</DebugInformationFormat>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ <Lib>
+ <AdditionalDependencies>Setupapi.lib</AdditionalDependencies>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level4</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_WIN64;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <OmitDefaultLibName>true</OmitDefaultLibName>
+ <DebugInformationFormat>OldStyle</DebugInformationFormat>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ <Lib>
+ <AdditionalDependencies>Setupapi.lib</AdditionalDependencies>
+ </Lib>
+ </ItemDefinitionGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/LibOVR/Projects/Win32/LibOVR_Msvc2010.vcxproj.filters b/LibOVR/Projects/Win32/LibOVR_Msvc2010.vcxproj.filters
new file mode 100644
index 0000000..23f1686
--- /dev/null
+++ b/LibOVR/Projects/Win32/LibOVR_Msvc2010.vcxproj.filters
@@ -0,0 +1,186 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClCompile Include="..\..\Src\OVR_SensorFusion.cpp" />
+ <ClCompile Include="..\..\Src\OVR_ThreadCommandQueue.cpp" />
+ <ClCompile Include="..\..\Src\OVR_Win32_DeviceManager.cpp" />
+ <ClCompile Include="..\..\Src\OVR_Win32_HMDDevice.cpp" />
+ <ClCompile Include="..\..\Src\Kernel\OVR_Alg.cpp">
+ <Filter>Kernel</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\Src\Kernel\OVR_Allocator.cpp">
+ <Filter>Kernel</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\Src\Kernel\OVR_Atomic.cpp">
+ <Filter>Kernel</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\Src\Kernel\OVR_File.cpp">
+ <Filter>Kernel</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\Src\Kernel\OVR_FileFILE.cpp">
+ <Filter>Kernel</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\Src\Kernel\OVR_Log.cpp">
+ <Filter>Kernel</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\Src\Kernel\OVR_Math.cpp">
+ <Filter>Kernel</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\Src\Kernel\OVR_RefCount.cpp">
+ <Filter>Kernel</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\Src\Kernel\OVR_Std.cpp">
+ <Filter>Kernel</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\Src\Kernel\OVR_String.cpp">
+ <Filter>Kernel</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\Src\Kernel\OVR_String_PathUtil.cpp">
+ <Filter>Kernel</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\Src\Kernel\OVR_SysFile.cpp">
+ <Filter>Kernel</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\Src\Kernel\OVR_System.cpp">
+ <Filter>Kernel</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\Src\Kernel\OVR_ThreadsWinAPI.cpp">
+ <Filter>Kernel</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\Src\Kernel\OVR_Timer.cpp">
+ <Filter>Kernel</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\Src\Kernel\OVR_UTF8Util.cpp">
+ <Filter>Kernel</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\Src\OVR_DeviceImpl.cpp" />
+ <ClCompile Include="..\..\Src\OVR_DeviceHandle.cpp" />
+ <ClCompile Include="..\..\Src\OVR_Win32_DeviceStatus.cpp" />
+ <ClCompile Include="..\..\Src\Kernel\OVR_String_FormatUtil.cpp">
+ <Filter>Kernel</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\Src\Util\Util_Render_Stereo.cpp">
+ <Filter>Util</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\Src\Util\Util_LatencyTest.cpp">
+ <Filter>Util</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\Src\OVR_Win32_HIDDevice.cpp" />
+ <ClCompile Include="..\..\Src\OVR_LatencyTestImpl.cpp" />
+ <ClCompile Include="..\..\Src\OVR_SensorImpl.cpp" />
+ <ClCompile Include="..\..\Src\OVR_Win32_SensorDevice.cpp" />
+ <ClCompile Include="..\..\Src\Util\Util_MagCalibration.cpp">
+ <Filter>Util</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\Src\OVR_SensorFilter.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\Src\OVR_DeviceImpl.h" />
+ <ClInclude Include="..\..\Src\OVR_SensorFusion.h" />
+ <ClInclude Include="..\..\Src\OVR_ThreadCommandQueue.h" />
+ <ClInclude Include="..\..\Src\OVR_Win32_DeviceManager.h" />
+ <ClInclude Include="..\..\Src\OVR_Win32_HMDDevice.h" />
+ <ClInclude Include="..\..\Src\Kernel\OVR_Alg.h">
+ <Filter>Kernel</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\Src\Kernel\OVR_Allocator.h">
+ <Filter>Kernel</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\Src\Kernel\OVR_Array.h">
+ <Filter>Kernel</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\Src\Kernel\OVR_Atomic.h">
+ <Filter>Kernel</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\Src\Kernel\OVR_ContainerAllocator.h">
+ <Filter>Kernel</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\Src\Kernel\OVR_Math.h">
+ <Filter>Kernel</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\Src\Kernel\OVR_File.h">
+ <Filter>Kernel</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\Src\Kernel\OVR_Hash.h">
+ <Filter>Kernel</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\Src\Kernel\OVR_KeyCodes.h">
+ <Filter>Kernel</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\Src\Kernel\OVR_List.h">
+ <Filter>Kernel</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\Src\Kernel\OVR_Log.h">
+ <Filter>Kernel</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\Src\Kernel\OVR_RefCount.h">
+ <Filter>Kernel</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\Src\Kernel\OVR_System.h">
+ <Filter>Kernel</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\Src\Kernel\OVR_Std.h">
+ <Filter>Kernel</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\Src\Kernel\OVR_String.h">
+ <Filter>Kernel</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\Src\Kernel\OVR_StringHash.h">
+ <Filter>Kernel</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\Src\Kernel\OVR_SysFile.h">
+ <Filter>Kernel</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\Src\Kernel\OVR_Threads.h">
+ <Filter>Kernel</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\Src\Kernel\OVR_Timer.h">
+ <Filter>Kernel</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\Src\Kernel\OVR_Types.h">
+ <Filter>Kernel</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\Src\Kernel\OVR_UTF8Util.h">
+ <Filter>Kernel</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\Include\OVR.h">
+ <Filter>Include</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\Src\OVR_Device.h" />
+ <ClInclude Include="..\..\Src\OVR_DeviceConstants.h" />
+ <ClInclude Include="..\..\Src\OVR_DeviceMessages.h" />
+ <ClInclude Include="..\..\Src\OVR_DeviceHandle.h" />
+ <ClInclude Include="..\..\Src\OVR_Win32_DeviceStatus.h" />
+ <ClInclude Include="..\..\Src\Kernel\OVR_Color.h">
+ <Filter>Kernel</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\Src\Util\Util_Render_Stereo.h">
+ <Filter>Util</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\Src\Util\Util_LatencyTest.h">
+ <Filter>Util</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\Src\OVR_HIDDevice.h" />
+ <ClInclude Include="..\..\Src\OVR_Win32_HIDDevice.h" />
+ <ClInclude Include="..\..\Src\OVR_HIDDeviceImpl.h" />
+ <ClInclude Include="..\..\Src\OVR_LatencyTestImpl.h" />
+ <ClInclude Include="..\..\Src\OVR_SensorImpl.h" />
+ <ClInclude Include="..\..\Src\OVR_HIDDeviceBase.h" />
+ <ClInclude Include="..\..\Src\OVR_Win32_SensorDevice.h" />
+ <ClInclude Include="..\..\Src\Util\Util_MagCalibration.h">
+ <Filter>Util</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\Src\OVR_SensorFilter.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <Filter Include="Kernel">
+ <UniqueIdentifier>{ccc06f04-d013-483f-8471-bd25332e38bb}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Include">
+ <UniqueIdentifier>{53c19267-8aec-48ad-a1a3-7ffd9090f075}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Util">
+ <UniqueIdentifier>{6d4ac63a-dea8-4fdb-ad20-6768c33376d9}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/LibOVR/Projects/libovr.txt b/LibOVR/Projects/libovr.txt
new file mode 100644
index 0000000..633db8f
--- /dev/null
+++ b/LibOVR/Projects/libovr.txt
@@ -0,0 +1,83 @@
+LibOVR/Src/Kernel/OVR_Alg.cpp
+LibOVR/Src/Kernel/OVR_Alg.h
+LibOVR/Src/Kernel/OVR_Allocator.cpp
+LibOVR/Src/Kernel/OVR_Allocator.h
+LibOVR/Src/Kernel/OVR_Array.h
+LibOVR/Src/Kernel/OVR_Atomic.cpp
+LibOVR/Src/Kernel/OVR_Atomic.h
+LibOVR/Src/Kernel/OVR_Color.h
+LibOVR/Src/Kernel/OVR_ContainerAllocator.h
+LibOVR/Src/Kernel/OVR_File.cpp
+LibOVR/Src/Kernel/OVR_File.h
+LibOVR/Src/Kernel/OVR_FileFILE.cpp
+LibOVR/Src/Kernel/OVR_Hash.h
+LibOVR/Src/Kernel/OVR_KeyCodes.h
+LibOVR/Src/Kernel/OVR_List.h
+LibOVR/Src/Kernel/OVR_Log.cpp
+LibOVR/Src/Kernel/OVR_Log.h
+LibOVR/Src/Kernel/OVR_Math.cpp
+LibOVR/Src/Kernel/OVR_Math.h
+LibOVR/Src/Kernel/OVR_RefCount.cpp
+LibOVR/Src/Kernel/OVR_RefCount.h
+LibOVR/Src/Kernel/OVR_Std.cpp
+LibOVR/Src/Kernel/OVR_Std.h
+LibOVR/Src/Kernel/OVR_String.cpp
+LibOVR/Src/Kernel/OVR_String.h
+LibOVR/Src/Kernel/OVR_String_FormatUtil.cpp
+LibOVR/Src/Kernel/OVR_String_PathUtil.cpp
+LibOVR/Src/Kernel/OVR_StringHash.h
+LibOVR/Src/Kernel/OVR_SysFile.cpp
+LibOVR/Src/Kernel/OVR_SysFile.h
+LibOVR/Src/Kernel/OVR_System.cpp
+LibOVR/Src/Kernel/OVR_System.h
+LibOVR/Src/Kernel/OVR_Threads.h
+LibOVR/Src/Kernel/OVR_Timer.cpp
+LibOVR/Src/Kernel/OVR_Timer.h
+LibOVR/Src/Kernel/OVR_Types.h
+LibOVR/Src/Kernel/OVR_UTF8Util.cpp
+LibOVR/Src/Kernel/OVR_UTF8Util.h
+LibOVR/Src/OVR_Device.h
+LibOVR/Src/OVR_DeviceConstants.h
+LibOVR/Src/OVR_DeviceHandle.cpp
+LibOVR/Src/OVR_DeviceHandle.h
+LibOVR/Src/OVR_DeviceImpl.cpp
+LibOVR/Src/OVR_DeviceImpl.h
+LibOVR/Src/OVR_DeviceMessages.h
+LibOVR/Src/OVR_HIDDevice.h
+LibOVR/Src/OVR_HIDDeviceBase.h
+LibOVR/Src/OVR_HIDDeviceImpl.h
+LibOVR/Src/OVR_LatencyTestImpl.cpp
+LibOVR/Src/OVR_LatencyTestImpl.h
+LibOVR/Src/OVR_SensorFusion.cpp
+LibOVR/Src/OVR_SensorFusion.h
+LibOVR/Src/OVR_SensorImpl.cpp
+LibOVR/Src/OVR_SensorImpl.h
+LibOVR/Src/OVR_ThreadCommandQueue.cpp
+LibOVR/Src/OVR_ThreadCommandQueue.h
+LibOVR/Src/Util/Util_LatencyTest.cpp
+LibOVR/Src/Util/Util_LatencyTest.h
+LibOVR/Src/Util/Util_Render_Stereo.cpp
+LibOVR/Src/Util/Util_Render_Stereo.h
+
+[MacOS]
+LibOVR/Src/Kernel/OVR_ThreadsPthread.cpp
+LibOVR/Src/OVR_OSX_DeviceManager.cpp
+LibOVR/Src/OVR_OSX_DeviceManager.h
+LibOVR/Src/OVR_OSX_HIDDevice.cpp
+LibOVR/Src/OVR_OSX_HIDDevice.h
+LibOVR/Src/OVR_OSX_HMDDevice.cpp
+LibOVR/Src/OVR_OSX_HMDDevice.h
+LibOVR/Src/OVR_OSX_SensorDevice.cpp
+
+[Win32]
+LibOVR/Src/Kernel/OVR_ThreadsWinAPI.cpp
+LibOVR/Src/OVR_Win32_DeviceManager.cpp
+LibOVR/Src/OVR_Win32_DeviceManager.h
+LibOVR/Src/OVR_Win32_DeviceStatus.cpp
+LibOVR/Src/OVR_Win32_DeviceStatus.h
+LibOVR/Src/OVR_Win32_HIDDevice.cpp
+LibOVR/Src/OVR_Win32_HIDDevice.h
+LibOVR/Src/OVR_Win32_HMDDevice.cpp
+LibOVR/Src/OVR_Win32_HMDDevice.h
+LibOVR/Src/OVR_Win32_SensorDevice.cpp
+LibOVR/Src/OVR_Win32_SensorDevice.h
diff --git a/LibOVR/Src/Kernel/OVR_Alg.cpp b/LibOVR/Src/Kernel/OVR_Alg.cpp
new file mode 100644
index 0000000..ea82f2a
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_Alg.cpp
@@ -0,0 +1,46 @@
+/************************************************************************************
+
+Filename : OVR_Alg.cpp
+Content : Static lookup tables for Alg functions
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#include "OVR_Types.h"
+
+namespace OVR { namespace Alg {
+
+//------------------------------------------------------------------------
+extern const UByte UpperBitTable[256] =
+{
+ 0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7
+};
+
+extern const UByte LowerBitTable[256] =
+{
+ 8,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 7,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0
+};
+
+
+}} // OVE::Alg
diff --git a/LibOVR/Src/Kernel/OVR_Alg.h b/LibOVR/Src/Kernel/OVR_Alg.h
new file mode 100644
index 0000000..841e8f0
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_Alg.h
@@ -0,0 +1,953 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_Alg.h
+Content : Simple general purpose algorithms: Sort, Binary Search, etc.
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#ifndef OVR_Alg_h
+#define OVR_Alg_h
+
+#include "OVR_Types.h"
+#include <string.h>
+
+namespace OVR { namespace Alg {
+
+
+//-----------------------------------------------------------------------------------
+// ***** Operator extensions
+
+template <typename T> OVR_FORCE_INLINE void Swap(T &a, T &b)
+{ T temp(a); a = b; b = temp; }
+
+
+// ***** min/max are not implemented in Visual Studio 6 standard STL
+
+template <typename T> OVR_FORCE_INLINE const T Min(const T a, const T b)
+{ return (a < b) ? a : b; }
+
+template <typename T> OVR_FORCE_INLINE const T Max(const T a, const T b)
+{ return (b < a) ? a : b; }
+
+template <typename T> OVR_FORCE_INLINE const T Clamp(const T v, const T minVal, const T maxVal)
+{ return Max<T>(minVal, Min<T>(v, maxVal)); }
+
+template <typename T> OVR_FORCE_INLINE int Chop(T f)
+{ return (int)f; }
+
+template <typename T> OVR_FORCE_INLINE T Lerp(T a, T b, T f)
+{ return (b - a) * f + a; }
+
+
+// These functions stand to fix a stupid VC++ warning (with /Wp64 on):
+// "warning C4267: 'argument' : conversion from 'size_t' to 'const unsigned', possible loss of data"
+// Use these functions instead of gmin/gmax if the argument has size
+// of the pointer to avoid the warning. Though, functionally they are
+// absolutelly the same as regular gmin/gmax.
+template <typename T> OVR_FORCE_INLINE const T PMin(const T a, const T b)
+{
+ OVR_COMPILER_ASSERT(sizeof(T) == sizeof(UPInt));
+ return (a < b) ? a : b;
+}
+template <typename T> OVR_FORCE_INLINE const T PMax(const T a, const T b)
+{
+ OVR_COMPILER_ASSERT(sizeof(T) == sizeof(UPInt));
+ return (b < a) ? a : b;
+}
+
+
+template <typename T> OVR_FORCE_INLINE const T Abs(const T v)
+{ return (v>=0) ? v : -v; }
+
+
+//-----------------------------------------------------------------------------------
+// ***** OperatorLess
+//
+template<class T> struct OperatorLess
+{
+ static bool Compare(const T& a, const T& b)
+ {
+ return a < b;
+ }
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** QuickSortSliced
+//
+// Sort any part of any array: plain, Array, ArrayPaged, ArrayUnsafe.
+// The range is specified with start, end, where "end" is exclusive!
+// The comparison predicate must be specified.
+template<class Array, class Less>
+void QuickSortSliced(Array& arr, UPInt start, UPInt end, Less less)
+{
+ enum
+ {
+ Threshold = 9
+ };
+
+ if(end - start < 2) return;
+
+ SPInt stack[80];
+ SPInt* top = stack;
+ SPInt base = (SPInt)start;
+ SPInt limit = (SPInt)end;
+
+ for(;;)
+ {
+ SPInt len = limit - base;
+ SPInt i, j, pivot;
+
+ if(len > Threshold)
+ {
+ // we use base + len/2 as the pivot
+ pivot = base + len / 2;
+ Swap(arr[base], arr[pivot]);
+
+ i = base + 1;
+ j = limit - 1;
+
+ // now ensure that *i <= *base <= *j
+ if(less(arr[j], arr[i])) Swap(arr[j], arr[i]);
+ if(less(arr[base], arr[i])) Swap(arr[base], arr[i]);
+ if(less(arr[j], arr[base])) Swap(arr[j], arr[base]);
+
+ for(;;)
+ {
+ do i++; while( less(arr[i], arr[base]) );
+ do j--; while( less(arr[base], arr[j]) );
+
+ if( i > j )
+ {
+ break;
+ }
+
+ Swap(arr[i], arr[j]);
+ }
+
+ Swap(arr[base], arr[j]);
+
+ // now, push the largest sub-array
+ if(j - base > limit - i)
+ {
+ top[0] = base;
+ top[1] = j;
+ base = i;
+ }
+ else
+ {
+ top[0] = i;
+ top[1] = limit;
+ limit = j;
+ }
+ top += 2;
+ }
+ else
+ {
+ // the sub-array is small, perform insertion sort
+ j = base;
+ i = j + 1;
+
+ for(; i < limit; j = i, i++)
+ {
+ for(; less(arr[j + 1], arr[j]); j--)
+ {
+ Swap(arr[j + 1], arr[j]);
+ if(j == base)
+ {
+ break;
+ }
+ }
+ }
+ if(top > stack)
+ {
+ top -= 2;
+ base = top[0];
+ limit = top[1];
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------------
+// ***** QuickSortSliced
+//
+// Sort any part of any array: plain, Array, ArrayPaged, ArrayUnsafe.
+// The range is specified with start, end, where "end" is exclusive!
+// The data type must have a defined "<" operator.
+template<class Array>
+void QuickSortSliced(Array& arr, UPInt start, UPInt end)
+{
+ typedef typename Array::ValueType ValueType;
+ QuickSortSliced(arr, start, end, OperatorLess<ValueType>::Compare);
+}
+
+// Same as corresponding G_QuickSortSliced but with checking array limits to avoid
+// crash in the case of wrong comparator functor.
+template<class Array, class Less>
+bool QuickSortSlicedSafe(Array& arr, UPInt start, UPInt end, Less less)
+{
+ enum
+ {
+ Threshold = 9
+ };
+
+ if(end - start < 2) return true;
+
+ SPInt stack[80];
+ SPInt* top = stack;
+ SPInt base = (SPInt)start;
+ SPInt limit = (SPInt)end;
+
+ for(;;)
+ {
+ SPInt len = limit - base;
+ SPInt i, j, pivot;
+
+ if(len > Threshold)
+ {
+ // we use base + len/2 as the pivot
+ pivot = base + len / 2;
+ Swap(arr[base], arr[pivot]);
+
+ i = base + 1;
+ j = limit - 1;
+
+ // now ensure that *i <= *base <= *j
+ if(less(arr[j], arr[i])) Swap(arr[j], arr[i]);
+ if(less(arr[base], arr[i])) Swap(arr[base], arr[i]);
+ if(less(arr[j], arr[base])) Swap(arr[j], arr[base]);
+
+ for(;;)
+ {
+ do
+ {
+ i++;
+ if (i >= limit)
+ return false;
+ } while( less(arr[i], arr[base]) );
+ do
+ {
+ j--;
+ if (j < 0)
+ return false;
+ } while( less(arr[base], arr[j]) );
+
+ if( i > j )
+ {
+ break;
+ }
+
+ Swap(arr[i], arr[j]);
+ }
+
+ Swap(arr[base], arr[j]);
+
+ // now, push the largest sub-array
+ if(j - base > limit - i)
+ {
+ top[0] = base;
+ top[1] = j;
+ base = i;
+ }
+ else
+ {
+ top[0] = i;
+ top[1] = limit;
+ limit = j;
+ }
+ top += 2;
+ }
+ else
+ {
+ // the sub-array is small, perform insertion sort
+ j = base;
+ i = j + 1;
+
+ for(; i < limit; j = i, i++)
+ {
+ for(; less(arr[j + 1], arr[j]); j--)
+ {
+ Swap(arr[j + 1], arr[j]);
+ if(j == base)
+ {
+ break;
+ }
+ }
+ }
+ if(top > stack)
+ {
+ top -= 2;
+ base = top[0];
+ limit = top[1];
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+ return true;
+}
+
+template<class Array>
+bool QuickSortSlicedSafe(Array& arr, UPInt start, UPInt end)
+{
+ typedef typename Array::ValueType ValueType;
+ return QuickSortSlicedSafe(arr, start, end, OperatorLess<ValueType>::Compare);
+}
+
+//-----------------------------------------------------------------------------------
+// ***** QuickSort
+//
+// Sort an array Array, ArrayPaged, ArrayUnsafe.
+// The array must have GetSize() function.
+// The comparison predicate must be specified.
+template<class Array, class Less>
+void QuickSort(Array& arr, Less less)
+{
+ QuickSortSliced(arr, 0, arr.GetSize(), less);
+}
+
+// checks for boundaries
+template<class Array, class Less>
+bool QuickSortSafe(Array& arr, Less less)
+{
+ return QuickSortSlicedSafe(arr, 0, arr.GetSize(), less);
+}
+
+
+//-----------------------------------------------------------------------------------
+// ***** QuickSort
+//
+// Sort an array Array, ArrayPaged, ArrayUnsafe.
+// The array must have GetSize() function.
+// The data type must have a defined "<" operator.
+template<class Array>
+void QuickSort(Array& arr)
+{
+ typedef typename Array::ValueType ValueType;
+ QuickSortSliced(arr, 0, arr.GetSize(), OperatorLess<ValueType>::Compare);
+}
+
+template<class Array>
+bool QuickSortSafe(Array& arr)
+{
+ typedef typename Array::ValueType ValueType;
+ return QuickSortSlicedSafe(arr, 0, arr.GetSize(), OperatorLess<ValueType>::Compare);
+}
+
+//-----------------------------------------------------------------------------------
+// ***** InsertionSortSliced
+//
+// Sort any part of any array: plain, Array, ArrayPaged, ArrayUnsafe.
+// The range is specified with start, end, where "end" is exclusive!
+// The comparison predicate must be specified.
+// Unlike Quick Sort, the Insertion Sort works much slower in average,
+// but may be much faster on almost sorted arrays. Besides, it guarantees
+// that the elements will not be swapped if not necessary. For example,
+// an array with all equal elements will remain "untouched", while
+// Quick Sort will considerably shuffle the elements in this case.
+template<class Array, class Less>
+void InsertionSortSliced(Array& arr, UPInt start, UPInt end, Less less)
+{
+ UPInt j = start;
+ UPInt i = j + 1;
+ UPInt limit = end;
+
+ for(; i < limit; j = i, i++)
+ {
+ for(; less(arr[j + 1], arr[j]); j--)
+ {
+ Swap(arr[j + 1], arr[j]);
+ if(j <= start)
+ {
+ break;
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------------
+// ***** InsertionSortSliced
+//
+// Sort any part of any array: plain, Array, ArrayPaged, ArrayUnsafe.
+// The range is specified with start, end, where "end" is exclusive!
+// The data type must have a defined "<" operator.
+template<class Array>
+void InsertionSortSliced(Array& arr, UPInt start, UPInt end)
+{
+ typedef typename Array::ValueType ValueType;
+ InsertionSortSliced(arr, start, end, OperatorLess<ValueType>::Compare);
+}
+
+//-----------------------------------------------------------------------------------
+// ***** InsertionSort
+//
+// Sort an array Array, ArrayPaged, ArrayUnsafe.
+// The array must have GetSize() function.
+// The comparison predicate must be specified.
+
+template<class Array, class Less>
+void InsertionSort(Array& arr, Less less)
+{
+ InsertionSortSliced(arr, 0, arr.GetSize(), less);
+}
+
+//-----------------------------------------------------------------------------------
+// ***** InsertionSort
+//
+// Sort an array Array, ArrayPaged, ArrayUnsafe.
+// The array must have GetSize() function.
+// The data type must have a defined "<" operator.
+template<class Array>
+void InsertionSort(Array& arr)
+{
+ typedef typename Array::ValueType ValueType;
+ InsertionSortSliced(arr, 0, arr.GetSize(), OperatorLess<ValueType>::Compare);
+}
+
+
+
+//-----------------------------------------------------------------------------------
+// ***** LowerBoundSliced
+//
+template<class Array, class Value, class Less>
+UPInt LowerBoundSliced(const Array& arr, UPInt start, UPInt end, const Value& val, Less less)
+{
+ SPInt first = (SPInt)start;
+ SPInt len = (SPInt)(end - start);
+ SPInt half;
+ SPInt middle;
+
+ while(len > 0)
+ {
+ half = len >> 1;
+ middle = first + half;
+ if(less(arr[middle], val))
+ {
+ first = middle + 1;
+ len = len - half - 1;
+ }
+ else
+ {
+ len = half;
+ }
+ }
+ return (UPInt)first;
+}
+
+
+//-----------------------------------------------------------------------------------
+// ***** LowerBoundSliced
+//
+template<class Array, class Value>
+UPInt LowerBoundSliced(const Array& arr, UPInt start, UPInt end, const Value& val)
+{
+ return LowerBoundSliced(arr, start, end, val, OperatorLess<Value>::Compare);
+}
+
+//-----------------------------------------------------------------------------------
+// ***** LowerBoundSized
+//
+template<class Array, class Value>
+UPInt LowerBoundSized(const Array& arr, UPInt size, const Value& val)
+{
+ return LowerBoundSliced(arr, 0, size, val, OperatorLess<Value>::Compare);
+}
+
+//-----------------------------------------------------------------------------------
+// ***** LowerBound
+//
+template<class Array, class Value, class Less>
+UPInt LowerBound(const Array& arr, const Value& val, Less less)
+{
+ return LowerBoundSliced(arr, 0, arr.GetSize(), val, less);
+}
+
+
+//-----------------------------------------------------------------------------------
+// ***** LowerBound
+//
+template<class Array, class Value>
+UPInt LowerBound(const Array& arr, const Value& val)
+{
+ return LowerBoundSliced(arr, 0, arr.GetSize(), val, OperatorLess<Value>::Compare);
+}
+
+
+
+//-----------------------------------------------------------------------------------
+// ***** UpperBoundSliced
+//
+template<class Array, class Value, class Less>
+UPInt UpperBoundSliced(const Array& arr, UPInt start, UPInt end, const Value& val, Less less)
+{
+ SPInt first = (SPInt)start;
+ SPInt len = (SPInt)(end - start);
+ SPInt half;
+ SPInt middle;
+
+ while(len > 0)
+ {
+ half = len >> 1;
+ middle = first + half;
+ if(less(val, arr[middle]))
+ {
+ len = half;
+ }
+ else
+ {
+ first = middle + 1;
+ len = len - half - 1;
+ }
+ }
+ return (UPInt)first;
+}
+
+
+//-----------------------------------------------------------------------------------
+// ***** UpperBoundSliced
+//
+template<class Array, class Value>
+UPInt UpperBoundSliced(const Array& arr, UPInt start, UPInt end, const Value& val)
+{
+ return UpperBoundSliced(arr, start, end, val, OperatorLess<Value>::Compare);
+}
+
+
+//-----------------------------------------------------------------------------------
+// ***** UpperBoundSized
+//
+template<class Array, class Value>
+UPInt UpperBoundSized(const Array& arr, UPInt size, const Value& val)
+{
+ return UpperBoundSliced(arr, 0, size, val, OperatorLess<Value>::Compare);
+}
+
+
+//-----------------------------------------------------------------------------------
+// ***** UpperBound
+//
+template<class Array, class Value, class Less>
+UPInt UpperBound(const Array& arr, const Value& val, Less less)
+{
+ return UpperBoundSliced(arr, 0, arr.GetSize(), val, less);
+}
+
+
+//-----------------------------------------------------------------------------------
+// ***** UpperBound
+//
+template<class Array, class Value>
+UPInt UpperBound(const Array& arr, const Value& val)
+{
+ return UpperBoundSliced(arr, 0, arr.GetSize(), val, OperatorLess<Value>::Compare);
+}
+
+
+//-----------------------------------------------------------------------------------
+// ***** ReverseArray
+//
+template<class Array> void ReverseArray(Array& arr)
+{
+ SPInt from = 0;
+ SPInt to = arr.GetSize() - 1;
+ while(from < to)
+ {
+ Swap(arr[from], arr[to]);
+ ++from;
+ --to;
+ }
+}
+
+
+// ***** AppendArray
+//
+template<class CDst, class CSrc>
+void AppendArray(CDst& dst, const CSrc& src)
+{
+ UPInt i;
+ for(i = 0; i < src.GetSize(); i++)
+ dst.PushBack(src[i]);
+}
+
+//-----------------------------------------------------------------------------------
+// ***** ArrayAdaptor
+//
+// A simple adapter that provides the GetSize() method and overloads
+// operator []. Used to wrap plain arrays in QuickSort and such.
+template<class T> class ArrayAdaptor
+{
+public:
+ typedef T ValueType;
+ ArrayAdaptor() : Data(0), Size(0) {}
+ ArrayAdaptor(T* ptr, UPInt size) : Data(ptr), Size(size) {}
+ UPInt GetSize() const { return Size; }
+ const T& operator [] (UPInt i) const { return Data[i]; }
+ T& operator [] (UPInt i) { return Data[i]; }
+private:
+ T* Data;
+ UPInt Size;
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** GConstArrayAdaptor
+//
+// A simple const adapter that provides the GetSize() method and overloads
+// operator []. Used to wrap plain arrays in LowerBound and such.
+template<class T> class ConstArrayAdaptor
+{
+public:
+ typedef T ValueType;
+ ConstArrayAdaptor() : Data(0), Size(0) {}
+ ConstArrayAdaptor(const T* ptr, UPInt size) : Data(ptr), Size(size) {}
+ UPInt GetSize() const { return Size; }
+ const T& operator [] (UPInt i) const { return Data[i]; }
+private:
+ const T* Data;
+ UPInt Size;
+};
+
+
+
+//-----------------------------------------------------------------------------------
+extern const UByte UpperBitTable[256];
+extern const UByte LowerBitTable[256];
+
+
+
+//-----------------------------------------------------------------------------------
+inline UByte UpperBit(UPInt val)
+{
+#ifndef OVR_64BIT_POINTERS
+
+ if (val & 0xFFFF0000)
+ {
+ return (val & 0xFF000000) ?
+ UpperBitTable[(val >> 24) ] + 24:
+ UpperBitTable[(val >> 16) & 0xFF] + 16;
+ }
+ return (val & 0xFF00) ?
+ UpperBitTable[(val >> 8) & 0xFF] + 8:
+ UpperBitTable[(val ) & 0xFF];
+
+#else
+
+ if (val & 0xFFFFFFFF00000000)
+ {
+ if (val & 0xFFFF000000000000)
+ {
+ return (val & 0xFF00000000000000) ?
+ UpperBitTable[(val >> 56) ] + 56:
+ UpperBitTable[(val >> 48) & 0xFF] + 48;
+ }
+ return (val & 0xFF0000000000) ?
+ UpperBitTable[(val >> 40) & 0xFF] + 40:
+ UpperBitTable[(val >> 32) & 0xFF] + 32;
+ }
+ else
+ {
+ if (val & 0xFFFF0000)
+ {
+ return (val & 0xFF000000) ?
+ UpperBitTable[(val >> 24) ] + 24:
+ UpperBitTable[(val >> 16) & 0xFF] + 16;
+ }
+ return (val & 0xFF00) ?
+ UpperBitTable[(val >> 8) & 0xFF] + 8:
+ UpperBitTable[(val ) & 0xFF];
+ }
+
+#endif
+}
+
+//-----------------------------------------------------------------------------------
+inline UByte LowerBit(UPInt val)
+{
+#ifndef OVR_64BIT_POINTERS
+
+ if (val & 0xFFFF)
+ {
+ return (val & 0xFF) ?
+ LowerBitTable[ val & 0xFF]:
+ LowerBitTable[(val >> 8) & 0xFF] + 8;
+ }
+ return (val & 0xFF0000) ?
+ LowerBitTable[(val >> 16) & 0xFF] + 16:
+ LowerBitTable[(val >> 24) & 0xFF] + 24;
+
+#else
+
+ if (val & 0xFFFFFFFF)
+ {
+ if (val & 0xFFFF)
+ {
+ return (val & 0xFF) ?
+ LowerBitTable[ val & 0xFF]:
+ LowerBitTable[(val >> 8) & 0xFF] + 8;
+ }
+ return (val & 0xFF0000) ?
+ LowerBitTable[(val >> 16) & 0xFF] + 16:
+ LowerBitTable[(val >> 24) & 0xFF] + 24;
+ }
+ else
+ {
+ if (val & 0xFFFF00000000)
+ {
+ return (val & 0xFF00000000) ?
+ LowerBitTable[(val >> 32) & 0xFF] + 32:
+ LowerBitTable[(val >> 40) & 0xFF] + 40;
+ }
+ return (val & 0xFF000000000000) ?
+ LowerBitTable[(val >> 48) & 0xFF] + 48:
+ LowerBitTable[(val >> 56) & 0xFF] + 56;
+ }
+
+#endif
+}
+
+
+
+// ******* Special (optimized) memory routines
+// Note: null (bad) pointer is not tested
+class MemUtil
+{
+public:
+
+ // Memory compare
+ static int Cmp (const void* p1, const void* p2, UPInt byteCount) { return memcmp(p1, p2, byteCount); }
+ static int Cmp16(const void* p1, const void* p2, UPInt int16Count);
+ static int Cmp32(const void* p1, const void* p2, UPInt int32Count);
+ static int Cmp64(const void* p1, const void* p2, UPInt int64Count);
+};
+
+// ** Inline Implementation
+
+inline int MemUtil::Cmp16(const void* p1, const void* p2, UPInt int16Count)
+{
+ SInt16* pa = (SInt16*)p1;
+ SInt16* pb = (SInt16*)p2;
+ unsigned ic = 0;
+ if (int16Count == 0)
+ return 0;
+ while (pa[ic] == pb[ic])
+ if (++ic==int16Count)
+ return 0;
+ return pa[ic] > pb[ic] ? 1 : -1;
+}
+inline int MemUtil::Cmp32(const void* p1, const void* p2, UPInt int32Count)
+{
+ SInt32* pa = (SInt32*)p1;
+ SInt32* pb = (SInt32*)p2;
+ unsigned ic = 0;
+ if (int32Count == 0)
+ return 0;
+ while (pa[ic] == pb[ic])
+ if (++ic==int32Count)
+ return 0;
+ return pa[ic] > pb[ic] ? 1 : -1;
+}
+inline int MemUtil::Cmp64(const void* p1, const void* p2, UPInt int64Count)
+{
+ SInt64* pa = (SInt64*)p1;
+ SInt64* pb = (SInt64*)p2;
+ unsigned ic = 0;
+ if (int64Count == 0)
+ return 0;
+ while (pa[ic] == pb[ic])
+ if (++ic==int64Count)
+ return 0;
+ return pa[ic] > pb[ic] ? 1 : -1;
+}
+
+// ** End Inline Implementation
+
+
+//-----------------------------------------------------------------------------------
+// ******* Byte Order Conversions
+namespace ByteUtil {
+
+ // *** Swap Byte Order
+
+ // Swap the byte order of a byte array
+ inline void SwapOrder(void* pv, int size)
+ {
+ UByte* pb = (UByte*)pv;
+ UByte temp;
+ for (int i = 0; i < size>>1; i++)
+ {
+ temp = pb[size-1-i];
+ pb[size-1-i] = pb[i];
+ pb[i] = temp;
+ }
+ }
+
+ // Swap the byte order of primitive types
+ inline UByte SwapOrder(UByte v) { return v; }
+ inline SByte SwapOrder(SByte v) { return v; }
+ inline UInt16 SwapOrder(UInt16 v) { return UInt16(v>>8)|UInt16(v<<8); }
+ inline SInt16 SwapOrder(SInt16 v) { return SInt16((UInt16(v)>>8)|(v<<8)); }
+ inline UInt32 SwapOrder(UInt32 v) { return (v>>24)|((v&0x00FF0000)>>8)|((v&0x0000FF00)<<8)|(v<<24); }
+ inline SInt32 SwapOrder(SInt32 p) { return (SInt32)SwapOrder(UInt32(p)); }
+ inline UInt64 SwapOrder(UInt64 v)
+ {
+ return (v>>56) |
+ ((v&UInt64(0x00FF000000000000))>>40) |
+ ((v&UInt64(0x0000FF0000000000))>>24) |
+ ((v&UInt64(0x000000FF00000000))>>8) |
+ ((v&UInt64(0x00000000FF000000))<<8) |
+ ((v&UInt64(0x0000000000FF0000))<<24) |
+ ((v&UInt64(0x000000000000FF00))<<40) |
+ (v<<56);
+ }
+ inline SInt64 SwapOrder(SInt64 v) { return (SInt64)SwapOrder(UInt64(v)); }
+ inline float SwapOrder(float p)
+ {
+ union {
+ float p;
+ UInt32 v;
+ } u;
+ u.p = p;
+ u.v = SwapOrder(u.v);
+ return u.p;
+ }
+
+ inline double SwapOrder(double p)
+ {
+ union {
+ double p;
+ UInt64 v;
+ } u;
+ u.p = p;
+ u.v = SwapOrder(u.v);
+ return u.p;
+ }
+
+ // *** Byte-order conversion
+
+#if (OVR_BYTE_ORDER == OVR_LITTLE_ENDIAN)
+ // Little Endian to System (LE)
+ inline UByte LEToSystem(UByte v) { return v; }
+ inline SByte LEToSystem(SByte v) { return v; }
+ inline UInt16 LEToSystem(UInt16 v) { return v; }
+ inline SInt16 LEToSystem(SInt16 v) { return v; }
+ inline UInt32 LEToSystem(UInt32 v) { return v; }
+ inline SInt32 LEToSystem(SInt32 v) { return v; }
+ inline UInt64 LEToSystem(UInt64 v) { return v; }
+ inline SInt64 LEToSystem(SInt64 v) { return v; }
+ inline float LEToSystem(float v) { return v; }
+ inline double LEToSystem(double v) { return v; }
+
+ // Big Endian to System (LE)
+ inline UByte BEToSystem(UByte v) { return SwapOrder(v); }
+ inline SByte BEToSystem(SByte v) { return SwapOrder(v); }
+ inline UInt16 BEToSystem(UInt16 v) { return SwapOrder(v); }
+ inline SInt16 BEToSystem(SInt16 v) { return SwapOrder(v); }
+ inline UInt32 BEToSystem(UInt32 v) { return SwapOrder(v); }
+ inline SInt32 BEToSystem(SInt32 v) { return SwapOrder(v); }
+ inline UInt64 BEToSystem(UInt64 v) { return SwapOrder(v); }
+ inline SInt64 BEToSystem(SInt64 v) { return SwapOrder(v); }
+ inline float BEToSystem(float v) { return SwapOrder(v); }
+ inline double BEToSystem(double v) { return SwapOrder(v); }
+
+ // System (LE) to Little Endian
+ inline UByte SystemToLE(UByte v) { return v; }
+ inline SByte SystemToLE(SByte v) { return v; }
+ inline UInt16 SystemToLE(UInt16 v) { return v; }
+ inline SInt16 SystemToLE(SInt16 v) { return v; }
+ inline UInt32 SystemToLE(UInt32 v) { return v; }
+ inline SInt32 SystemToLE(SInt32 v) { return v; }
+ inline UInt64 SystemToLE(UInt64 v) { return v; }
+ inline SInt64 SystemToLE(SInt64 v) { return v; }
+ inline float SystemToLE(float v) { return v; }
+ inline double SystemToLE(double v) { return v; }
+
+ // System (LE) to Big Endian
+ inline UByte SystemToBE(UByte v) { return SwapOrder(v); }
+ inline SByte SystemToBE(SByte v) { return SwapOrder(v); }
+ inline UInt16 SystemToBE(UInt16 v) { return SwapOrder(v); }
+ inline SInt16 SystemToBE(SInt16 v) { return SwapOrder(v); }
+ inline UInt32 SystemToBE(UInt32 v) { return SwapOrder(v); }
+ inline SInt32 SystemToBE(SInt32 v) { return SwapOrder(v); }
+ inline UInt64 SystemToBE(UInt64 v) { return SwapOrder(v); }
+ inline SInt64 SystemToBE(SInt64 v) { return SwapOrder(v); }
+ inline float SystemToBE(float v) { return SwapOrder(v); }
+ inline double SystemToBE(double v) { return SwapOrder(v); }
+
+#elif (OVR_BYTE_ORDER == OVR_BIG_ENDIAN)
+ // Little Endian to System (BE)
+ inline UByte LEToSystem(UByte v) { return SwapOrder(v); }
+ inline SByte LEToSystem(SByte v) { return SwapOrder(v); }
+ inline UInt16 LEToSystem(UInt16 v) { return SwapOrder(v); }
+ inline SInt16 LEToSystem(SInt16 v) { return SwapOrder(v); }
+ inline UInt32 LEToSystem(UInt32 v) { return SwapOrder(v); }
+ inline SInt32 LEToSystem(SInt32 v) { return SwapOrder(v); }
+ inline UInt64 LEToSystem(UInt64 v) { return SwapOrder(v); }
+ inline SInt64 LEToSystem(SInt64 v) { return SwapOrder(v); }
+ inline float LEToSystem(float v) { return SwapOrder(v); }
+ inline double LEToSystem(double v) { return SwapOrder(v); }
+
+ // Big Endian to System (BE)
+ inline UByte BEToSystem(UByte v) { return v; }
+ inline SByte BEToSystem(SByte v) { return v; }
+ inline UInt16 BEToSystem(UInt16 v) { return v; }
+ inline SInt16 BEToSystem(SInt16 v) { return v; }
+ inline UInt32 BEToSystem(UInt32 v) { return v; }
+ inline SInt32 BEToSystem(SInt32 v) { return v; }
+ inline UInt64 BEToSystem(UInt64 v) { return v; }
+ inline SInt64 BEToSystem(SInt64 v) { return v; }
+ inline float BEToSystem(float v) { return v; }
+ inline double BEToSystem(double v) { return v; }
+
+ // System (BE) to Little Endian
+ inline UByte SystemToLE(UByte v) { return SwapOrder(v); }
+ inline SByte SystemToLE(SByte v) { return SwapOrder(v); }
+ inline UInt16 SystemToLE(UInt16 v) { return SwapOrder(v); }
+ inline SInt16 SystemToLE(SInt16 v) { return SwapOrder(v); }
+ inline UInt32 SystemToLE(UInt32 v) { return SwapOrder(v); }
+ inline SInt32 SystemToLE(SInt32 v) { return SwapOrder(v); }
+ inline UInt64 SystemToLE(UInt64 v) { return SwapOrder(v); }
+ inline SInt64 SystemToLE(SInt64 v) { return SwapOrder(v); }
+ inline float SystemToLE(float v) { return SwapOrder(v); }
+ inline double SystemToLE(double v) { return SwapOrder(v); }
+
+ // System (BE) to Big Endian
+ inline UByte SystemToBE(UByte v) { return v; }
+ inline SByte SystemToBE(SByte v) { return v; }
+ inline UInt16 SystemToBE(UInt16 v) { return v; }
+ inline SInt16 SystemToBE(SInt16 v) { return v; }
+ inline UInt32 SystemToBE(UInt32 v) { return v; }
+ inline SInt32 SystemToBE(SInt32 v) { return v; }
+ inline UInt64 SystemToBE(UInt64 v) { return v; }
+ inline SInt64 SystemToBE(SInt64 v) { return v; }
+ inline float SystemToBE(float v) { return v; }
+ inline double SystemToBE(double v) { return v; }
+
+#else
+ #error "OVR_BYTE_ORDER must be defined to OVR_LITTLE_ENDIAN or OVR_BIG_ENDIAN"
+#endif
+
+} // namespace ByteUtil
+
+
+
+}} // OVR::Alg
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_Allocator.cpp b/LibOVR/Src/Kernel/OVR_Allocator.cpp
new file mode 100644
index 0000000..1f17ffe
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_Allocator.cpp
@@ -0,0 +1,84 @@
+/************************************************************************************
+
+Filename : OVR_Allocator.cpp
+Content : Installable memory allocator implementation
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#include "OVR_Allocator.h"
+#ifdef OVR_OS_MAC
+ #include <stdlib.h>
+#else
+ #include <malloc.h>
+#endif
+
+namespace OVR {
+
+//-----------------------------------------------------------------------------------
+// ***** Allocator
+
+Allocator* Allocator::pInstance = 0;
+
+// Default AlignedAlloc implementation will delegate to Alloc/Free after doing rounding.
+void* Allocator::AllocAligned(UPInt size, UPInt align)
+{
+ OVR_ASSERT((align & (align-1)) == 0);
+ align = (align > sizeof(UPInt)) ? align : sizeof(UPInt);
+ UPInt p = (UPInt)Alloc(size+align);
+ UPInt aligned = 0;
+ if (p)
+ {
+ aligned = (UPInt(p) + align-1) & ~(align-1);
+ if (aligned == p)
+ aligned += align;
+ *(((UPInt*)aligned)-1) = aligned-p;
+ }
+ return (void*)aligned;
+}
+
+void Allocator::FreeAligned(void* p)
+{
+ UPInt src = UPInt(p) - *(((UPInt*)p)-1);
+ Free((void*)src);
+}
+
+
+//------------------------------------------------------------------------
+// ***** Default Allocator
+
+// This allocator is created and used if no other allocator is installed.
+// Default allocator delegates to system malloc.
+
+void* DefaultAllocator::Alloc(UPInt size)
+{
+ return malloc(size);
+}
+void* DefaultAllocator::AllocDebug(UPInt size, const char* file, unsigned line)
+{
+#if defined(OVR_CC_MSVC) && defined(_CRTDBG_MAP_ALLOC)
+ return _malloc_dbg(size, _NORMAL_BLOCK, file, line);
+#else
+ OVR_UNUSED2(file, line);
+ return malloc(size);
+#endif
+}
+
+void* DefaultAllocator::Realloc(void* p, UPInt newSize)
+{
+ return realloc(p, newSize);
+}
+void DefaultAllocator::Free(void *p)
+{
+ return free(p);
+}
+
+
+} // OVR
diff --git a/LibOVR/Src/Kernel/OVR_Allocator.h b/LibOVR/Src/Kernel/OVR_Allocator.h
new file mode 100644
index 0000000..88cbb42
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_Allocator.h
@@ -0,0 +1,336 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_Allocator.h
+Content : Installable memory allocator
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#ifndef OVR_Allocator_h
+#define OVR_Allocator_h
+
+#include "OVR_Types.h"
+
+//-----------------------------------------------------------------------------------
+
+// ***** Disable template-unfriendly MS VC++ warnings
+#if defined(OVR_CC_MSVC)
+// Pragma to prevent long name warnings in in VC++
+#pragma warning(disable : 4503)
+#pragma warning(disable : 4786)
+// In MSVC 7.1, warning about placement new POD default initializer
+#pragma warning(disable : 4345)
+#endif
+
+// Un-define new so that placement constructors work
+#undef new
+
+
+//-----------------------------------------------------------------------------------
+// ***** Placement new overrides
+
+// Calls constructor on own memory created with "new(ptr) type"
+#ifndef __PLACEMENT_NEW_INLINE
+#define __PLACEMENT_NEW_INLINE
+
+# if defined(OVR_CC_MWERKS) || defined(OVR_CC_BORLAND) || defined(OVR_CC_GNU)
+# include <new>
+# else
+ // Useful on MSVC
+ OVR_FORCE_INLINE void* operator new (OVR::UPInt n, void *ptr) { OVR_UNUSED(n); return ptr; }
+ OVR_FORCE_INLINE void operator delete (void *, void *) { }
+# endif
+
+#endif // __PLACEMENT_NEW_INLINE
+
+
+
+//------------------------------------------------------------------------
+// ***** Macros to redefine class new/delete operators
+
+// Types specifically declared to allow disambiguation of address in
+// class member operator new.
+
+#define OVR_MEMORY_REDEFINE_NEW_IMPL(class_name, check_delete) \
+ void* operator new(UPInt sz) \
+ { void *p = OVR_ALLOC_DEBUG(sz, __FILE__, __LINE__); return p; } \
+ void* operator new(UPInt sz, const char* file, int line) \
+ { void* p = OVR_ALLOC_DEBUG(sz, file, line); OVR_UNUSED2(file, line); return p; } \
+ void operator delete(void *p) \
+ { check_delete(class_name, p); OVR_FREE(p); } \
+ void operator delete(void *p, const char*, int) \
+ { check_delete(class_name, p); OVR_FREE(p); }
+
+#define OVR_MEMORY_DEFINE_PLACEMENT_NEW \
+ void* operator new (UPInt n, void *ptr) { OVR_UNUSED(n); return ptr; } \
+ void operator delete (void *ptr, void *ptr2) { OVR_UNUSED2(ptr,ptr2); }
+
+
+#define OVR_MEMORY_CHECK_DELETE_NONE(class_name, p)
+
+// Redefined all delete/new operators in a class without custom memory initialization
+#define OVR_MEMORY_REDEFINE_NEW(class_name) \
+ OVR_MEMORY_REDEFINE_NEW_IMPL(class_name, OVR_MEMORY_CHECK_DELETE_NONE)
+
+
+namespace OVR {
+
+//-----------------------------------------------------------------------------------
+// ***** Construct / Destruct
+
+// Construct/Destruct functions are useful when new is redefined, as they can
+// be called instead of placement new constructors.
+
+
+template <class T>
+OVR_FORCE_INLINE T* Construct(void *p)
+{
+ return ::new(p) T;
+}
+
+template <class T>
+OVR_FORCE_INLINE T* Construct(void *p, const T& source)
+{
+ return ::new(p) T(source);
+}
+
+// Same as above, but allows for a different type of constructor.
+template <class T, class S>
+OVR_FORCE_INLINE T* ConstructAlt(void *p, const S& source)
+{
+ return ::new(p) T(source);
+}
+
+template <class T, class S1, class S2>
+OVR_FORCE_INLINE T* ConstructAlt(void *p, const S1& src1, const S2& src2)
+{
+ return ::new(p) T(src1, src2);
+}
+
+template <class T>
+OVR_FORCE_INLINE void ConstructArray(void *p, UPInt count)
+{
+ UByte *pdata = (UByte*)p;
+ for (UPInt i=0; i< count; ++i, pdata += sizeof(T))
+ {
+ Construct<T>(pdata);
+ }
+}
+
+template <class T>
+OVR_FORCE_INLINE void ConstructArray(void *p, UPInt count, const T& source)
+{
+ UByte *pdata = (UByte*)p;
+ for (UPInt i=0; i< count; ++i, pdata += sizeof(T))
+ {
+ Construct<T>(pdata, source);
+ }
+}
+
+template <class T>
+OVR_FORCE_INLINE void Destruct(T *pobj)
+{
+ pobj->~T();
+ OVR_UNUSED1(pobj); // Fix incorrect 'unused variable' MSVC warning.
+}
+
+template <class T>
+OVR_FORCE_INLINE void DestructArray(T *pobj, UPInt count)
+{
+ for (UPInt i=0; i<count; ++i, ++pobj)
+ pobj->~T();
+}
+
+
+//-----------------------------------------------------------------------------------
+// ***** Allocator
+
+// Allocator defines a memory allocation interface that developers can override
+// to to provide memory for OVR; an instance of this class is typically created on
+// application startup and passed into System or OVR::System constructor.
+//
+//
+// Users implementing this interface must provide three functions: Alloc, Free,
+// and Realloc. Implementations of these functions must honor the requested alignment.
+// Although arbitrary alignment requests are possible, requested alignment will
+// typically be small, such as 16 bytes or less.
+
+class Allocator
+{
+ friend class System;
+public:
+
+ // *** Standard Alignment Alloc/Free
+
+ // Allocate memory of specified size with default alignment.
+ // Alloc of size==0 will allocate a tiny block & return a valid pointer;
+ // this makes it suitable for new operator.
+ virtual void* Alloc(UPInt size) = 0;
+ // Same as Alloc, but provides an option of passing debug data.
+ virtual void* AllocDebug(UPInt size, const char* file, unsigned line)
+ { OVR_UNUSED2(file, line); return Alloc(size); }
+
+ // Reallocate memory block to a new size, copying data if necessary. Returns the pointer to
+ // new memory block, which may be the same as original pointer. Will return 0 if reallocation
+ // failed, in which case previous memory is still valid.
+ // Realloc to decrease size will never fail.
+ // Realloc of pointer == 0 is equivalent to Alloc
+ // Realloc to size == 0, shrinks to the minimal size, pointer remains valid and requires Free().
+ virtual void* Realloc(void* p, UPInt newSize) = 0;
+
+ // Frees memory allocated by Alloc/Realloc.
+ // Free of null pointer is valid and will do nothing.
+ virtual void Free(void *p) = 0;
+
+
+ // *** Standard Alignment Alloc/Free
+
+ // Allocate memory of specified alignment.
+ // Memory allocated with AllocAligned MUST be freed with FreeAligned.
+ // Default implementation will delegate to Alloc/Free after doing rounding.
+ virtual void* AllocAligned(UPInt size, UPInt align);
+ // Frees memory allocated with AllocAligned.
+ virtual void FreeAligned(void* p);
+
+ // Returns the pointer to the current globally installed Allocator instance.
+ // This pointer is used for most of the memory allocations.
+ static Allocator* GetInstance() { return pInstance; }
+
+
+protected:
+ // onSystemShutdown is called on the allocator during System::Shutdown.
+ // At this point, all allocations should've been freed.
+ virtual void onSystemShutdown() { }
+
+public:
+ static void setInstance(Allocator* palloc)
+ {
+ OVR_ASSERT((pInstance == 0) || (palloc == 0));
+ pInstance = palloc;
+ }
+
+private:
+
+ static Allocator* pInstance;
+};
+
+
+
+//------------------------------------------------------------------------
+// ***** Allocator_SingletonSupport
+
+// Allocator_SingletonSupport is a Allocator wrapper class that implements
+// the InitSystemSingleton static function, used to create a global singleton
+// used for the OVR::System default argument initialization.
+//
+// End users implementing custom Allocator interface don't need to make use of this base
+// class; they can just create an instance of their own class on stack and pass it to System.
+
+template<class D>
+class Allocator_SingletonSupport : public Allocator
+{
+ struct AllocContainer
+ {
+ UPInt Data[(sizeof(D) + sizeof(UPInt)-1) / sizeof(UPInt)];
+ bool Initialized;
+ AllocContainer() : Initialized(0) { }
+ };
+
+ AllocContainer* pContainer;
+
+public:
+ Allocator_SingletonSupport() : pContainer(0) { }
+
+ // Creates a singleton instance of this Allocator class used
+ // on OVR_DEFAULT_ALLOCATOR during System initialization.
+ static D* InitSystemSingleton()
+ {
+ static AllocContainer Container;
+ OVR_ASSERT(Container.Initialized == false);
+
+ Allocator_SingletonSupport<D> *presult = Construct<D>((void*)Container.Data);
+ presult->pContainer = &Container;
+ Container.Initialized = true;
+ return (D*)presult;
+ }
+
+protected:
+ virtual void onSystemShutdown()
+ {
+ Allocator::onSystemShutdown();
+ if (pContainer)
+ {
+ pContainer->Initialized = false;
+ Destruct((D*)this);
+ pContainer = 0;
+ }
+ }
+};
+
+//------------------------------------------------------------------------
+// ***** Default Allocator
+
+// This allocator is created and used if no other allocator is installed.
+// Default allocator delegates to system malloc.
+
+class DefaultAllocator : public Allocator_SingletonSupport<DefaultAllocator>
+{
+public:
+ virtual void* Alloc(UPInt size);
+ virtual void* AllocDebug(UPInt size, const char* file, unsigned line);
+ virtual void* Realloc(void* p, UPInt newSize);
+ virtual void Free(void *p);
+};
+
+
+//------------------------------------------------------------------------
+// ***** Memory Allocation Macros
+
+// These macros should be used for global allocation. In the future, these
+// macros will allows allocation to be extended with debug file/line information
+// if necessary.
+
+#define OVR_REALLOC(p,s) OVR::Allocator::GetInstance()->Realloc((p),(s))
+#define OVR_FREE(p) OVR::Allocator::GetInstance()->Free((p))
+#define OVR_ALLOC_ALIGNED(s,a) OVR::Allocator::GetInstance()->AllocAligned((s),(a))
+#define OVR_FREE_ALIGNED(p) OVR::Allocator::GetInstance()->FreeAligned((p))
+
+#ifdef OVR_BUILD_DEBUG
+#define OVR_ALLOC(s) OVR::Allocator::GetInstance()->AllocDebug((s), __FILE__, __LINE__)
+#define OVR_ALLOC_DEBUG(s,f,l) OVR::Allocator::GetInstance()->AllocDebug((s), f, l)
+#else
+#define OVR_ALLOC(s) OVR::Allocator::GetInstance()->Alloc((s))
+#define OVR_ALLOC_DEBUG(s,f,l) OVR::Allocator::GetInstance()->Alloc((s))
+#endif
+
+//------------------------------------------------------------------------
+
+// Base class that overrides the new and delete operators.
+// Deriving from this class, even as a multiple base, incurs no space overhead.
+class NewOverrideBase
+{
+public:
+
+ // Redefine all new & delete operators.
+ OVR_MEMORY_REDEFINE_NEW(NewOverrideBase)
+};
+
+
+} // OVR
+
+
+// Redefine operator 'new' if necessary.
+#if defined(OVR_DEFINE_NEW)
+#define new OVR_DEFINE_NEW
+#endif
+
+
+#endif // OVR_Memory
diff --git a/LibOVR/Src/Kernel/OVR_Array.h b/LibOVR/Src/Kernel/OVR_Array.h
new file mode 100644
index 0000000..905d58f
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_Array.h
@@ -0,0 +1,793 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_Array.h
+Content : Template implementation for Array
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#ifndef OVR_Array_h
+#define OVR_Array_h
+
+#include "OVR_ContainerAllocator.h"
+
+namespace OVR {
+
+//-----------------------------------------------------------------------------------
+// ***** ArrayDefaultPolicy
+//
+// Default resize behavior. No minimal capacity, Granularity=4,
+// Shrinking as needed. ArrayConstPolicy actually is the same as
+// ArrayDefaultPolicy, but parametrized with constants.
+// This struct is used only in order to reduce the template "matroska".
+struct ArrayDefaultPolicy
+{
+ ArrayDefaultPolicy() : Capacity(0) {}
+ ArrayDefaultPolicy(const ArrayDefaultPolicy&) : Capacity(0) {}
+
+ UPInt GetMinCapacity() const { return 0; }
+ UPInt GetGranularity() const { return 4; }
+ bool NeverShrinking() const { return 0; }
+
+ UPInt GetCapacity() const { return Capacity; }
+ void SetCapacity(UPInt capacity) { Capacity = capacity; }
+private:
+ UPInt Capacity;
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** ArrayConstPolicy
+//
+// Statically parametrized resizing behavior:
+// MinCapacity, Granularity, and Shrinking flag.
+template<int MinCapacity=0, int Granularity=4, bool NeverShrink=false>
+struct ArrayConstPolicy
+{
+ typedef ArrayConstPolicy<MinCapacity, Granularity, NeverShrink> SelfType;
+
+ ArrayConstPolicy() : Capacity(0) {}
+ ArrayConstPolicy(const SelfType&) : Capacity(0) {}
+
+ UPInt GetMinCapacity() const { return MinCapacity; }
+ UPInt GetGranularity() const { return Granularity; }
+ bool NeverShrinking() const { return NeverShrink; }
+
+ UPInt GetCapacity() const { return Capacity; }
+ void SetCapacity(UPInt capacity) { Capacity = capacity; }
+private:
+ UPInt Capacity;
+};
+
+//-----------------------------------------------------------------------------------
+// ***** ArrayDataBase
+//
+// Basic operations with array data: Reserve, Resize, Free, ArrayPolicy.
+// For internal use only: ArrayData,ArrayDataCC and others.
+template<class T, class Allocator, class SizePolicy>
+struct ArrayDataBase
+{
+ typedef T ValueType;
+ typedef Allocator AllocatorType;
+ typedef SizePolicy SizePolicyType;
+ typedef ArrayDataBase<T, Allocator, SizePolicy> SelfType;
+
+ ArrayDataBase()
+ : Data(0), Size(0), Policy() {}
+
+ ArrayDataBase(const SizePolicy& p)
+ : Data(0), Size(0), Policy(p) {}
+
+ ~ArrayDataBase()
+ {
+ Allocator::DestructArray(Data, Size);
+ Allocator::Free(Data);
+ }
+
+ UPInt GetCapacity() const
+ {
+ return Policy.GetCapacity();
+ }
+
+ void ClearAndRelease()
+ {
+ Allocator::DestructArray(Data, Size);
+ Allocator::Free(Data);
+ Data = 0;
+ Size = 0;
+ Policy.SetCapacity(0);
+ }
+
+ void Reserve(UPInt newCapacity)
+ {
+ if (Policy.NeverShrinking() && newCapacity < GetCapacity())
+ return;
+
+ if (newCapacity < Policy.GetMinCapacity())
+ newCapacity = Policy.GetMinCapacity();
+
+ // Resize the buffer.
+ if (newCapacity == 0)
+ {
+ if (Data)
+ {
+ Allocator::Free(Data);
+ Data = 0;
+ }
+ Policy.SetCapacity(0);
+ }
+ else
+ {
+ UPInt gran = Policy.GetGranularity();
+ newCapacity = (newCapacity + gran - 1) / gran * gran;
+ if (Data)
+ {
+ if (Allocator::IsMovable())
+ {
+ Data = (T*)Allocator::Realloc(Data, sizeof(T) * newCapacity);
+ }
+ else
+ {
+ T* newData = (T*)Allocator::Alloc(sizeof(T) * newCapacity);
+ UPInt i, s;
+ s = (Size < newCapacity) ? Size : newCapacity;
+ for (i = 0; i < s; ++i)
+ {
+ Allocator::Construct(&newData[i], Data[i]);
+ Allocator::Destruct(&Data[i]);
+ }
+ for (i = s; i < Size; ++i)
+ {
+ Allocator::Destruct(&Data[i]);
+ }
+ Allocator::Free(Data);
+ Data = newData;
+ }
+ }
+ else
+ {
+ Data = (T*)Allocator::Alloc(sizeof(T) * newCapacity);
+ //memset(Buffer, 0, (sizeof(ValueType) * newSize)); // Do we need this?
+ }
+ Policy.SetCapacity(newCapacity);
+ // OVR_ASSERT(Data); // need to throw (or something) on alloc failure!
+ }
+ }
+
+ // This version of Resize DOES NOT construct the elements.
+ // It's done to optimize PushBack, which uses a copy constructor
+ // instead of the default constructor and assignment
+ void ResizeNoConstruct(UPInt newSize)
+ {
+ UPInt oldSize = Size;
+
+ if (newSize < oldSize)
+ {
+ Allocator::DestructArray(Data + newSize, oldSize - newSize);
+ if (newSize < (Policy.GetCapacity() >> 1))
+ {
+ Reserve(newSize);
+ }
+ }
+ else if(newSize >= Policy.GetCapacity())
+ {
+ Reserve(newSize + (newSize >> 2));
+ }
+ //! IMPORTANT to modify Size only after Reserve completes, because garbage collectable
+ // array may use this array and may traverse it during Reserve (in the case, if
+ // collection occurs because of heap limit exceeded).
+ Size = newSize;
+ }
+
+ ValueType* Data;
+ UPInt Size;
+ SizePolicy Policy;
+};
+
+
+
+//-----------------------------------------------------------------------------------
+// ***** ArrayData
+//
+// General purpose array data.
+// For internal use only in Array, ArrayLH, ArrayPOD and so on.
+template<class T, class Allocator, class SizePolicy>
+struct ArrayData : ArrayDataBase<T, Allocator, SizePolicy>
+{
+ typedef T ValueType;
+ typedef Allocator AllocatorType;
+ typedef SizePolicy SizePolicyType;
+ typedef ArrayDataBase<T, Allocator, SizePolicy> BaseType;
+ typedef ArrayData <T, Allocator, SizePolicy> SelfType;
+
+ ArrayData()
+ : BaseType() { }
+
+ ArrayData(int size)
+ : BaseType() { Resize(size); }
+
+ ArrayData(const SelfType& a)
+ : BaseType(a.Policy) { Append(a.Data, a.Size); }
+
+
+ void Resize(UPInt newSize)
+ {
+ UPInt oldSize = this->Size;
+ BaseType::ResizeNoConstruct(newSize);
+ if(newSize > oldSize)
+ Allocator::ConstructArray(this->Data + oldSize, newSize - oldSize);
+ }
+
+ void PushBack(const ValueType& val)
+ {
+ BaseType::ResizeNoConstruct(this->Size + 1);
+ Allocator::Construct(this->Data + this->Size - 1, val);
+ }
+
+ template<class S>
+ void PushBackAlt(const S& val)
+ {
+ BaseType::ResizeNoConstruct(this->Size + 1);
+ Allocator::ConstructAlt(this->Data + this->Size - 1, val);
+ }
+
+ // Append the given data to the array.
+ void Append(const ValueType other[], UPInt count)
+ {
+ if (count)
+ {
+ UPInt oldSize = this->Size;
+ BaseType::ResizeNoConstruct(this->Size + count);
+ Allocator::ConstructArray(this->Data + oldSize, count, other);
+ }
+ }
+};
+
+
+
+//-----------------------------------------------------------------------------------
+// ***** ArrayDataCC
+//
+// A modification of ArrayData that always copy-constructs new elements
+// using a specified DefaultValue. For internal use only in ArrayCC.
+template<class T, class Allocator, class SizePolicy>
+struct ArrayDataCC : ArrayDataBase<T, Allocator, SizePolicy>
+{
+ typedef T ValueType;
+ typedef Allocator AllocatorType;
+ typedef SizePolicy SizePolicyType;
+ typedef ArrayDataBase<T, Allocator, SizePolicy> BaseType;
+ typedef ArrayDataCC <T, Allocator, SizePolicy> SelfType;
+
+ ArrayDataCC(const ValueType& defval)
+ : BaseType(), DefaultValue(defval) { }
+
+ ArrayDataCC(const ValueType& defval, int size)
+ : BaseType(), DefaultValue(defval) { Resize(size); }
+
+ ArrayDataCC(const SelfType& a)
+ : BaseType(a.Policy), DefaultValue(a.DefaultValue) { Append(a.Data, a.Size); }
+
+
+ void Resize(UPInt newSize)
+ {
+ UPInt oldSize = this->Size;
+ BaseType::ResizeNoConstruct(newSize);
+ if(newSize > oldSize)
+ Allocator::ConstructArray(this->Data + oldSize, newSize - oldSize, DefaultValue);
+ }
+
+ void PushBack(const ValueType& val)
+ {
+ BaseType::ResizeNoConstruct(this->Size + 1);
+ Allocator::Construct(this->Data + this->Size - 1, val);
+ }
+
+ template<class S>
+ void PushBackAlt(const S& val)
+ {
+ BaseType::ResizeNoConstruct(this->Size + 1);
+ Allocator::ConstructAlt(this->Data + this->Size - 1, val);
+ }
+
+ // Append the given data to the array.
+ void Append(const ValueType other[], UPInt count)
+ {
+ if (count)
+ {
+ UPInt oldSize = this->Size;
+ BaseType::ResizeNoConstruct(this->Size + count);
+ Allocator::ConstructArray(this->Data + oldSize, count, other);
+ }
+ }
+
+ ValueType DefaultValue;
+};
+
+
+
+
+
+//-----------------------------------------------------------------------------------
+// ***** ArrayBase
+//
+// Resizable array. The behavior can be POD (suffix _POD) and
+// Movable (no suffix) depending on the allocator policy.
+// In case of _POD the constructors and destructors are not called.
+//
+// Arrays can't handle non-movable objects! Don't put anything in here
+// that can't be moved around by bitwise copy.
+//
+// The addresses of elements are not persistent! Don't keep the address
+// of an element; the array contents will move around as it gets resized.
+template<class ArrayData>
+class ArrayBase
+{
+public:
+ typedef typename ArrayData::ValueType ValueType;
+ typedef typename ArrayData::AllocatorType AllocatorType;
+ typedef typename ArrayData::SizePolicyType SizePolicyType;
+ typedef ArrayBase<ArrayData> SelfType;
+
+
+#undef new
+ OVR_MEMORY_REDEFINE_NEW(ArrayBase)
+// Redefine operator 'new' if necessary.
+#if defined(OVR_DEFINE_NEW)
+#define new OVR_DEFINE_NEW
+#endif
+
+
+ ArrayBase()
+ : Data() {}
+ ArrayBase(int size)
+ : Data(size) {}
+ ArrayBase(const SelfType& a)
+ : Data(a.Data) {}
+
+ ArrayBase(const ValueType& defval)
+ : Data(defval) {}
+ ArrayBase(const ValueType& defval, int size)
+ : Data(defval, size) {}
+
+ SizePolicyType* GetSizePolicy() const { return Data.Policy; }
+ void SetSizePolicy(const SizePolicyType& p) { Data.Policy = p; }
+
+ bool NeverShrinking()const { return Data.Policy.NeverShrinking(); }
+ UPInt GetSize() const { return Data.Size; }
+ bool IsEmpty() const { return Data.Size == 0; }
+ UPInt GetCapacity() const { return Data.GetCapacity(); }
+ UPInt GetNumBytes() const { return Data.GetCapacity() * sizeof(ValueType); }
+
+ void ClearAndRelease() { Data.ClearAndRelease(); }
+ void Clear() { Data.Resize(0); }
+ void Resize(UPInt newSize) { Data.Resize(newSize); }
+
+ // Reserve can only increase the capacity
+ void Reserve(UPInt newCapacity)
+ {
+ if (newCapacity > Data.GetCapacity())
+ Data.Reserve(newCapacity);
+ }
+
+ // Basic access.
+ ValueType& At(UPInt index)
+ {
+ OVR_ASSERT(index < Data.Size);
+ return Data.Data[index];
+ }
+ const ValueType& At(UPInt index) const
+ {
+ OVR_ASSERT(index < Data.Size);
+ return Data.Data[index];
+ }
+
+ ValueType ValueAt(UPInt index) const
+ {
+ OVR_ASSERT(index < Data.Size);
+ return Data.Data[index];
+ }
+
+ // Basic access.
+ ValueType& operator [] (UPInt index)
+ {
+ OVR_ASSERT(index < Data.Size);
+ return Data.Data[index];
+ }
+ const ValueType& operator [] (UPInt index) const
+ {
+ OVR_ASSERT(index < Data.Size);
+ return Data.Data[index];
+ }
+
+ // Raw pointer to the data. Use with caution!
+ const ValueType* GetDataPtr() const { return Data.Data; }
+ ValueType* GetDataPtr() { return Data.Data; }
+
+ // Insert the given element at the end of the array.
+ void PushBack(const ValueType& val)
+ {
+ // DO NOT pass elements of your own vector into
+ // push_back()! Since we're using references,
+ // resize() may munge the element storage!
+ // OVR_ASSERT(&val < &Buffer[0] || &val > &Buffer[BufferSize]);
+ Data.PushBack(val);
+ }
+
+ template<class S>
+ void PushBackAlt(const S& val)
+ {
+ Data.PushBackAlt(val);
+ }
+
+ // Remove the last element.
+ void PopBack(UPInt count = 1)
+ {
+ OVR_ASSERT(Data.Size >= count);
+ Data.Resize(Data.Size - count);
+ }
+
+ ValueType& PushDefault()
+ {
+ Data.PushBack(ValueType());
+ return Back();
+ }
+
+ ValueType Pop()
+ {
+ ValueType t = Back();
+ PopBack();
+ return t;
+ }
+
+
+ // Access the first element.
+ ValueType& Front() { return At(0); }
+ const ValueType& Front() const { return At(0); }
+
+ // Access the last element.
+ ValueType& Back() { return At(Data.Size - 1); }
+ const ValueType& Back() const { return At(Data.Size - 1); }
+
+ // Array copy. Copies the contents of a into this array.
+ const SelfType& operator = (const SelfType& a)
+ {
+ Resize(a.GetSize());
+ for (UPInt i = 0; i < Data.Size; i++) {
+ *(Data.Data + i) = a[i];
+ }
+ return *this;
+ }
+
+ // Removing multiple elements from the array.
+ void RemoveMultipleAt(UPInt index, UPInt num)
+ {
+ OVR_ASSERT(index + num <= Data.Size);
+ if (Data.Size == num)
+ {
+ Clear();
+ }
+ else
+ {
+ AllocatorType::DestructArray(Data.Data + index, num);
+ AllocatorType::CopyArrayForward(
+ Data.Data + index,
+ Data.Data + index + num,
+ Data.Size - num - index);
+ Data.Size -= num;
+ }
+ }
+
+ // Removing an element from the array is an expensive operation!
+ // It compacts only after removing the last element.
+ void RemoveAt(UPInt index)
+ {
+ OVR_ASSERT(index < Data.Size);
+ if (Data.Size == 1)
+ {
+ Clear();
+ }
+ else
+ {
+ AllocatorType::Destruct(Data.Data + index);
+ AllocatorType::CopyArrayForward(
+ Data.Data + index,
+ Data.Data + index + 1,
+ Data.Size - 1 - index);
+ --Data.Size;
+ }
+ }
+
+ // Insert the given object at the given index shifting all the elements up.
+ void InsertAt(UPInt index, const ValueType& val = ValueType())
+ {
+ OVR_ASSERT(index <= Data.Size);
+
+ Data.Resize(Data.Size + 1);
+ if (index < Data.Size - 1)
+ {
+ AllocatorType::CopyArrayBackward(
+ Data.Data + index + 1,
+ Data.Data + index,
+ Data.Size - 1 - index);
+ }
+ AllocatorType::Construct(Data.Data + index, val);
+ }
+
+ // Insert the given object at the given index shifting all the elements up.
+ void InsertMultipleAt(UPInt index, UPInt num, const ValueType& val = ValueType())
+ {
+ OVR_ASSERT(index <= Data.Size);
+
+ Data.Resize(Data.Size + num);
+ if (index < Data.Size - num)
+ {
+ AllocatorType::CopyArrayBackward(
+ Data.Data + index + num,
+ Data.Data + index,
+ Data.Size - num - index);
+ }
+ for (UPInt i = 0; i < num; ++i)
+ AllocatorType::Construct(Data.Data + index + i, val);
+ }
+
+ // Append the given data to the array.
+ void Append(const SelfType& other)
+ {
+ Append(other.Data.Data, other.GetSize());
+ }
+
+ // Append the given data to the array.
+ void Append(const ValueType other[], UPInt count)
+ {
+ Data.Append(other, count);
+ }
+
+ class Iterator
+ {
+ SelfType* pArray;
+ SPInt CurIndex;
+
+ public:
+ Iterator() : pArray(0), CurIndex(-1) {}
+ Iterator(SelfType* parr, SPInt idx = 0) : pArray(parr), CurIndex(idx) {}
+
+ bool operator==(const Iterator& it) const { return pArray == it.pArray && CurIndex == it.CurIndex; }
+ bool operator!=(const Iterator& it) const { return pArray != it.pArray || CurIndex != it.CurIndex; }
+
+ Iterator& operator++()
+ {
+ if (pArray)
+ {
+ if (CurIndex < (SPInt)pArray->GetSize())
+ ++CurIndex;
+ }
+ return *this;
+ }
+ Iterator operator++(int)
+ {
+ Iterator it(*this);
+ operator++();
+ return it;
+ }
+ Iterator& operator--()
+ {
+ if (pArray)
+ {
+ if (CurIndex >= 0)
+ --CurIndex;
+ }
+ return *this;
+ }
+ Iterator operator--(int)
+ {
+ Iterator it(*this);
+ operator--();
+ return it;
+ }
+ Iterator operator+(int delta) const
+ {
+ return Iterator(pArray, CurIndex + delta);
+ }
+ Iterator operator-(int delta) const
+ {
+ return Iterator(pArray, CurIndex - delta);
+ }
+ SPInt operator-(const Iterator& right) const
+ {
+ OVR_ASSERT(pArray == right.pArray);
+ return CurIndex - right.CurIndex;
+ }
+ ValueType& operator*() const { OVR_ASSERT(pArray); return (*pArray)[CurIndex]; }
+ ValueType* operator->() const { OVR_ASSERT(pArray); return &(*pArray)[CurIndex]; }
+ ValueType* GetPtr() const { OVR_ASSERT(pArray); return &(*pArray)[CurIndex]; }
+
+ bool IsFinished() const { return !pArray || CurIndex < 0 || CurIndex >= (int)pArray->GetSize(); }
+
+ void Remove()
+ {
+ if (!IsFinished())
+ pArray->RemoveAt(CurIndex);
+ }
+
+ SPInt GetIndex() const { return CurIndex; }
+ };
+
+ Iterator Begin() { return Iterator(this); }
+ Iterator End() { return Iterator(this, (SPInt)GetSize()); }
+ Iterator Last() { return Iterator(this, (SPInt)GetSize() - 1); }
+
+ class ConstIterator
+ {
+ const SelfType* pArray;
+ SPInt CurIndex;
+
+ public:
+ ConstIterator() : pArray(0), CurIndex(-1) {}
+ ConstIterator(const SelfType* parr, SPInt idx = 0) : pArray(parr), CurIndex(idx) {}
+
+ bool operator==(const ConstIterator& it) const { return pArray == it.pArray && CurIndex == it.CurIndex; }
+ bool operator!=(const ConstIterator& it) const { return pArray != it.pArray || CurIndex != it.CurIndex; }
+
+ ConstIterator& operator++()
+ {
+ if (pArray)
+ {
+ if (CurIndex < (int)pArray->GetSize())
+ ++CurIndex;
+ }
+ return *this;
+ }
+ ConstIterator operator++(int)
+ {
+ ConstIterator it(*this);
+ operator++();
+ return it;
+ }
+ ConstIterator& operator--()
+ {
+ if (pArray)
+ {
+ if (CurIndex >= 0)
+ --CurIndex;
+ }
+ return *this;
+ }
+ ConstIterator operator--(int)
+ {
+ ConstIterator it(*this);
+ operator--();
+ return it;
+ }
+ ConstIterator operator+(int delta) const
+ {
+ return ConstIterator(pArray, CurIndex + delta);
+ }
+ ConstIterator operator-(int delta) const
+ {
+ return ConstIterator(pArray, CurIndex - delta);
+ }
+ SPInt operator-(const ConstIterator& right) const
+ {
+ OVR_ASSERT(pArray == right.pArray);
+ return CurIndex - right.CurIndex;
+ }
+ const ValueType& operator*() const { OVR_ASSERT(pArray); return (*pArray)[CurIndex]; }
+ const ValueType* operator->() const { OVR_ASSERT(pArray); return &(*pArray)[CurIndex]; }
+ const ValueType* GetPtr() const { OVR_ASSERT(pArray); return &(*pArray)[CurIndex]; }
+
+ bool IsFinished() const { return !pArray || CurIndex < 0 || CurIndex >= (int)pArray->GetSize(); }
+
+ SPInt GetIndex() const { return CurIndex; }
+ };
+ ConstIterator Begin() const { return ConstIterator(this); }
+ ConstIterator End() const { return ConstIterator(this, (SPInt)GetSize()); }
+ ConstIterator Last() const { return ConstIterator(this, (SPInt)GetSize() - 1); }
+
+protected:
+ ArrayData Data;
+};
+
+
+
+//-----------------------------------------------------------------------------------
+// ***** Array
+//
+// General purpose array for movable objects that require explicit
+// construction/destruction.
+template<class T, class SizePolicy=ArrayDefaultPolicy>
+class Array : public ArrayBase<ArrayData<T, ContainerAllocator<T>, SizePolicy> >
+{
+public:
+ typedef T ValueType;
+ typedef ContainerAllocator<T> AllocatorType;
+ typedef SizePolicy SizePolicyType;
+ typedef Array<T, SizePolicy> SelfType;
+ typedef ArrayBase<ArrayData<T, ContainerAllocator<T>, SizePolicy> > BaseType;
+
+ Array() : BaseType() {}
+ Array(int size) : BaseType(size) {}
+ Array(const SizePolicyType& p) : BaseType() { SetSizePolicy(p); }
+ Array(const SelfType& a) : BaseType(a) {}
+ const SelfType& operator=(const SelfType& a) { BaseType::operator=(a); return *this; }
+};
+
+// ***** ArrayPOD
+//
+// General purpose array for movable objects that DOES NOT require
+// construction/destruction. Constructors and destructors are not called!
+// Global heap is in use.
+template<class T, class SizePolicy=ArrayDefaultPolicy>
+class ArrayPOD : public ArrayBase<ArrayData<T, ContainerAllocator_POD<T>, SizePolicy> >
+{
+public:
+ typedef T ValueType;
+ typedef ContainerAllocator_POD<T> AllocatorType;
+ typedef SizePolicy SizePolicyType;
+ typedef ArrayPOD<T, SizePolicy> SelfType;
+ typedef ArrayBase<ArrayData<T, ContainerAllocator_POD<T>, SizePolicy> > BaseType;
+
+ ArrayPOD() : BaseType() {}
+ ArrayPOD(int size) : BaseType(size) {}
+ ArrayPOD(const SizePolicyType& p) : BaseType() { SetSizePolicy(p); }
+ ArrayPOD(const SelfType& a) : BaseType(a) {}
+ const SelfType& operator=(const SelfType& a) { BaseType::operator=(a); return *this; }
+};
+
+
+// ***** ArrayCPP
+//
+// General purpose, fully C++ compliant array. Can be used with non-movable data.
+// Global heap is in use.
+template<class T, class SizePolicy=ArrayDefaultPolicy>
+class ArrayCPP : public ArrayBase<ArrayData<T, ContainerAllocator_CPP<T>, SizePolicy> >
+{
+public:
+ typedef T ValueType;
+ typedef ContainerAllocator_CPP<T> AllocatorType;
+ typedef SizePolicy SizePolicyType;
+ typedef ArrayCPP<T, SizePolicy> SelfType;
+ typedef ArrayBase<ArrayData<T, ContainerAllocator_CPP<T>, SizePolicy> > BaseType;
+
+ ArrayCPP() : BaseType() {}
+ ArrayCPP(int size) : BaseType(size) {}
+ ArrayCPP(const SizePolicyType& p) : BaseType() { SetSizePolicy(p); }
+ ArrayCPP(const SelfType& a) : BaseType(a) {}
+ const SelfType& operator=(const SelfType& a) { BaseType::operator=(a); return *this; }
+};
+
+
+// ***** ArrayCC
+//
+// A modification of the array that uses the given default value to
+// construct the elements. The constructors and destructors are
+// properly called, the objects must be movable.
+
+template<class T, class SizePolicy=ArrayDefaultPolicy>
+class ArrayCC : public ArrayBase<ArrayDataCC<T, ContainerAllocator<T>, SizePolicy> >
+{
+public:
+ typedef T ValueType;
+ typedef ContainerAllocator<T> AllocatorType;
+ typedef SizePolicy SizePolicyType;
+ typedef ArrayCC<T, SizePolicy> SelfType;
+ typedef ArrayBase<ArrayDataCC<T, ContainerAllocator<T>, SizePolicy> > BaseType;
+
+ ArrayCC(const ValueType& defval) : BaseType(defval) {}
+ ArrayCC(const ValueType& defval, int size) : BaseType(defval, size) {}
+ ArrayCC(const ValueType& defval, const SizePolicyType& p) : BaseType(defval) { SetSizePolicy(p); }
+ ArrayCC(const SelfType& a) : BaseType(a) {}
+ const SelfType& operator=(const SelfType& a) { BaseType::operator=(a); return *this; }
+};
+
+} // OVR
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_Atomic.cpp b/LibOVR/Src/Kernel/OVR_Atomic.cpp
new file mode 100644
index 0000000..a2f9230
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_Atomic.cpp
@@ -0,0 +1,82 @@
+/************************************************************************************
+
+Filename : OVR_Atomic.cpp
+Content : Contains atomic operations and inline fastest locking
+ functionality. Will contain #ifdefs for OS efficiency.
+ Have non-thread-safe implementation if not available.
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#include "OVR_Atomic.h"
+
+#ifdef OVR_ENABLE_THREADS
+
+// Include Windows 8-Metro compatible Synchronization API
+#if defined(OVR_OS_WIN32) && defined(NTDDI_WIN8) && (NTDDI_VERSION >= NTDDI_WIN8)
+#include <synchapi.h>
+#endif
+
+
+namespace OVR {
+
+// ***** Windows Lock implementation
+
+#if defined(OVR_OS_WIN32)
+
+// ***** Standard Win32 Lock implementation
+
+// Constructors
+Lock::Lock(unsigned spinCount)
+{
+#if defined(NTDDI_WIN8) && (NTDDI_VERSION >= NTDDI_WIN8)
+ // On Windows 8 we use InitializeCriticalSectionEx due to Metro-Compatibility
+ InitializeCriticalSectionEx(&cs, spinCount,
+ OVR_DEBUG_SELECT(NULL, CRITICAL_SECTION_NO_DEBUG_INFO));
+#else
+ // Spin count init critical section function prototype for Window NT
+ typedef BOOL (WINAPI *Function_InitializeCriticalSectionAndSpinCount)
+ (LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount);
+
+
+ // Try to load function dynamically so that we don't require NT
+ // On Windows NT we will use InitializeCriticalSectionAndSpinCount
+ static bool initTried = 0;
+ static Function_InitializeCriticalSectionAndSpinCount pInitFn = 0;
+
+ if (!initTried)
+ {
+ HMODULE hmodule = ::LoadLibrary(OVR_STR("kernel32.dll"));
+ pInitFn = (Function_InitializeCriticalSectionAndSpinCount)
+ ::GetProcAddress(hmodule, "InitializeCriticalSectionAndSpinCount");
+ initTried = true;
+ }
+
+ // Initialize the critical section
+ if (pInitFn)
+ pInitFn(&cs, spinCount);
+ else
+ ::InitializeCriticalSection(&cs);
+#endif
+
+}
+
+
+Lock::~Lock()
+{
+ DeleteCriticalSection(&cs);
+}
+
+
+#endif
+
+} // OVR
+
+#endif // OVR_ENABLE_THREADS
diff --git a/LibOVR/Src/Kernel/OVR_Atomic.h b/LibOVR/Src/Kernel/OVR_Atomic.h
new file mode 100644
index 0000000..089125b
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_Atomic.h
@@ -0,0 +1,859 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_Atomic.h
+Content : Contains atomic operations and inline fastest locking
+ functionality. Will contain #ifdefs for OS efficiency.
+ Have non-thread-safe implementaion if not available.
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+#ifndef OVR_Atomic_h
+#define OVR_Atomic_h
+
+#include "OVR_Types.h"
+
+// Include System thread functionality.
+#if defined(OVR_OS_WIN32)
+#include <windows.h>
+#else
+#include <pthread.h>
+#endif
+
+
+namespace OVR {
+
+
+// ****** Declared classes
+
+// If there is NO thread support we implement AtomicOps and
+// Lock objects as no-ops. The other classes are not defined.
+template<class C> class AtomicOps;
+template<class T> class AtomicInt;
+template<class T> class AtomicPtr;
+
+class Lock;
+
+
+//-----------------------------------------------------------------------------------
+// ***** AtomicOps
+
+// Atomic operations are provided by the AtomicOps templates class,
+// implemented through system-specific AtomicOpsRaw specializations.
+// It provides several fundamental operations such as Exchange, ExchangeAdd
+// CompareAndSet, and Store_Release. Each function includes several memory
+// synchronization versions, important for multiprocessing CPUs with weak
+// memory consistency. The following memory fencing strategies are supported:
+//
+// - NoSync. No memory synchronization is done for atomic op.
+// - Release. All other memory writes are completed before atomic op
+// writes its results.
+// - Acquire. Further memory reads are forced to wait until atomic op
+// executes, guaranteeing that the right values will be seen.
+// - Sync. A combination of Release and Acquire.
+
+
+// *** AtomicOpsRaw
+
+// AtomicOpsRaw is a specialized template that provides atomic operations
+// used by AtomicOps. This class has two fundamental qualities: (1) it
+// defines a type T of correct size, and (2) provides operations that work
+// atomically, such as Exchange_Sync and CompareAndSet_Release.
+
+// AtomicOpsRawBase class contains shared constants/classes for AtomicOpsRaw.
+// The primary thing is does is define sync class objects, whose destructor and
+// constructor provide places to insert appropriate synchronization calls, on
+// systems where such calls are necessary. So far, the breakdown is as follows:
+//
+// - X86 systems don't need custom syncs, since their exchange/atomic
+// instructions are implicitly synchronized.
+// - PowerPC requires lwsync/isync instructions that can use this mechanism.
+// - If some other systems require a mechanism where syncing type is associated
+// with a particular instruction, the default implementation (which implements
+// all Sync, Acquire, and Release modes in terms of NoSync and fence) may not
+// work. Ii that case it will need to be #ifdef-ed conditionally.
+
+struct AtomicOpsRawBase
+{
+#if !defined(OVR_ENABLE_THREADS) || defined(OVR_CPU_X86) || defined(OVR_OS_WIN32) || defined(OVR_OS_IPHONE)
+ // Need to have empty constructor to avoid class 'unused' variable warning.
+ struct FullSync { inline FullSync() { } };
+ struct AcquireSync { inline AcquireSync() { } };
+ struct ReleaseSync { inline ReleaseSync() { } };
+
+#elif defined(OVR_CPU_PPC64) || defined(OVR_CPU_PPC)
+ struct FullSync { inline FullSync() { asm volatile("sync\n"); } ~FullSync() { asm volatile("isync\n"); } };
+ struct AcquireSync { inline AcquireSync() { } ~AcquireSync() { asm volatile("isync\n"); } };
+ struct ReleaseSync { inline ReleaseSync() { asm volatile("sync\n"); } };
+
+#elif defined(OVR_CPU_MIPS)
+ struct FullSync { inline FullSync() { asm volatile("sync\n"); } ~FullSync() { asm volatile("sync\n"); } };
+ struct AcquireSync { inline AcquireSync() { } ~AcquireSync() { asm volatile("sync\n"); } };
+ struct ReleaseSync { inline ReleaseSync() { asm volatile("sync\n"); } };
+
+#elif defined(OVR_CPU_ARM)
+ struct FullSync { inline FullSync() { asm volatile("dmb\n"); } ~FullSync() { asm volatile("dmb\n"); } };
+ struct AcquireSync { inline AcquireSync() { } ~AcquireSync() { asm volatile("dmb\n"); } };
+ struct ReleaseSync { inline ReleaseSync() { asm volatile("dmb\n"); } };
+
+
+#elif defined(OVR_CC_GNU) && (__GNUC__ >= 4)
+ // __sync functions are already full sync
+ struct FullSync { inline FullSync() { } };
+ struct AcquireSync { inline AcquireSync() { } };
+ struct ReleaseSync { inline ReleaseSync() { } };
+#endif
+};
+
+
+// 4-Byte raw data atomic op implementation class.
+struct AtomicOpsRaw_4ByteImpl : public AtomicOpsRawBase
+{
+#if !defined(OVR_ENABLE_THREADS)
+
+ // Provide a type for no-thread-support cases. Used by AtomicOpsRaw_DefImpl.
+ typedef UInt32 T;
+
+ // *** Thread - Safe Atomic Versions.
+
+#elif defined(OVR_OS_WIN32)
+
+ // Use special defined for VC6, where volatile is not used and
+ // InterlockedCompareExchange is declared incorrectly.
+ typedef LONG T;
+#if defined(OVR_CC_MSVC) && (OVR_CC_MSVC < 1300)
+ typedef T* InterlockTPtr;
+ typedef LPVOID ET;
+ typedef ET* InterlockETPtr;
+#else
+ typedef volatile T* InterlockTPtr;
+ typedef T ET;
+ typedef InterlockTPtr InterlockETPtr;
+#endif
+ inline static T Exchange_NoSync(volatile T* p, T val) { return InterlockedExchange((InterlockTPtr)p, val); }
+ inline static T ExchangeAdd_NoSync(volatile T* p, T val) { return InterlockedExchangeAdd((InterlockTPtr)p, val); }
+ inline static bool CompareAndSet_NoSync(volatile T* p, T c, T val) { return InterlockedCompareExchange((InterlockETPtr)p, (ET)val, (ET)c) == (ET)c; }
+
+#elif defined(OVR_CPU_PPC64) || defined(OVR_CPU_PPC)
+ typedef UInt32 T;
+ static inline UInt32 Exchange_NoSync(volatile UInt32 *i, UInt32 j)
+ {
+ UInt32 ret;
+
+ asm volatile("1:\n\t"
+ "lwarx %[r],0,%[i]\n\t"
+ "stwcx. %[j],0,%[i]\n\t"
+ "bne- 1b\n"
+ : "+m" (*i), [r] "=&b" (ret) : [i] "b" (i), [j] "b" (j) : "cc", "memory");
+
+ return ret;
+ }
+
+ static inline UInt32 ExchangeAdd_NoSync(volatile UInt32 *i, UInt32 j)
+ {
+ UInt32 dummy, ret;
+
+ asm volatile("1:\n\t"
+ "lwarx %[r],0,%[i]\n\t"
+ "add %[o],%[r],%[j]\n\t"
+ "stwcx. %[o],0,%[i]\n\t"
+ "bne- 1b\n"
+ : "+m" (*i), [r] "=&b" (ret), [o] "=&r" (dummy) : [i] "b" (i), [j] "b" (j) : "cc", "memory");
+
+ return ret;
+ }
+
+ static inline bool CompareAndSet_NoSync(volatile UInt32 *i, UInt32 c, UInt32 value)
+ {
+ UInt32 ret;
+
+ asm volatile("1:\n\t"
+ "lwarx %[r],0,%[i]\n\t"
+ "cmpw 0,%[r],%[cmp]\n\t"
+ "mfcr %[r]\n\t"
+ "bne- 2f\n\t"
+ "stwcx. %[val],0,%[i]\n\t"
+ "bne- 1b\n\t"
+ "2:\n"
+ : "+m" (*i), [r] "=&b" (ret) : [i] "b" (i), [cmp] "b" (c), [val] "b" (value) : "cc", "memory");
+
+ return (ret & 0x20000000) ? 1 : 0;
+ }
+
+#elif defined(OVR_CPU_MIPS)
+ typedef UInt32 T;
+
+ static inline UInt32 Exchange_NoSync(volatile UInt32 *i, UInt32 j)
+ {
+ UInt32 ret;
+
+ asm volatile("1:\n\t"
+ "ll %[r],0(%[i])\n\t"
+ "sc %[j],0(%[i])\n\t"
+ "beq %[j],$0,1b\n\t"
+ "nop \n"
+ : "+m" (*i), [r] "=&d" (ret) : [i] "d" (i), [j] "d" (j) : "cc", "memory");
+
+ return ret;
+ }
+
+ static inline UInt32 ExchangeAdd_NoSync(volatile UInt32 *i, UInt32 j)
+ {
+ UInt32 ret;
+
+ asm volatile("1:\n\t"
+ "ll %[r],0(%[i])\n\t"
+ "addu %[j],%[r],%[j]\n\t"
+ "sc %[j],0(%[i])\n\t"
+ "beq %[j],$0,1b\n\t"
+ "nop \n"
+ : "+m" (*i), [r] "=&d" (ret) : [i] "d" (i), [j] "d" (j) : "cc", "memory");
+
+ return ret;
+ }
+
+ static inline bool CompareAndSet_NoSync(volatile UInt32 *i, UInt32 c, UInt32 value)
+ {
+ UInt32 ret, dummy;
+
+ asm volatile("1:\n\t"
+ "move %[r],$0\n\t"
+ "ll %[o],0(%[i])\n\t"
+ "bne %[o],%[c],2f\n\t"
+ "move %[r],%[v]\n\t"
+ "sc %[r],0(%[i])\n\t"
+ "beq %[r],$0,1b\n\t"
+ "nop \n\t"
+ "2:\n"
+ : "+m" (*i),[r] "=&d" (ret), [o] "=&d" (dummy) : [i] "d" (i), [c] "d" (c), [v] "d" (value)
+ : "cc", "memory");
+
+ return ret;
+ }
+
+#elif defined(OVR_CPU_ARM) && defined(OVR_CC_ARM)
+ typedef UInt32 T;
+
+ static inline UInt32 Exchange_NoSync(volatile UInt32 *i, UInt32 j)
+ {
+ for(;;)
+ {
+ T r = __ldrex(i);
+ if (__strex(j, i) == 0)
+ return r;
+ }
+ }
+ static inline UInt32 ExchangeAdd_NoSync(volatile UInt32 *i, UInt32 j)
+ {
+ for(;;)
+ {
+ T r = __ldrex(i);
+ if (__strex(r + j, i) == 0)
+ return r;
+ }
+ }
+
+ static inline bool CompareAndSet_NoSync(volatile UInt32 *i, UInt32 c, UInt32 value)
+ {
+ for(;;)
+ {
+ T r = __ldrex(i);
+ if (r != c)
+ return 0;
+ if (__strex(value, i) == 0)
+ return 1;
+ }
+ }
+
+#elif defined(OVR_CPU_ARM)
+ typedef UInt32 T;
+
+ static inline UInt32 Exchange_NoSync(volatile UInt32 *i, UInt32 j)
+ {
+ UInt32 ret, dummy;
+
+ asm volatile("1:\n\t"
+ "ldrex %[r],[%[i]]\n\t"
+ "strex %[t],%[j],[%[i]]\n\t"
+ "cmp %[t],#0\n\t"
+ "bne 1b\n\t"
+ : "+m" (*i), [r] "=&r" (ret), [t] "=&r" (dummy) : [i] "r" (i), [j] "r" (j) : "cc", "memory");
+
+ return ret;
+ }
+
+ static inline UInt32 ExchangeAdd_NoSync(volatile UInt32 *i, UInt32 j)
+ {
+ UInt32 ret, dummy, test;
+
+ asm volatile("1:\n\t"
+ "ldrex %[r],[%[i]]\n\t"
+ "add %[o],%[r],%[j]\n\t"
+ "strex %[t],%[o],[%[i]]\n\t"
+ "cmp %[t],#0\n\t"
+ "bne 1b\n\t"
+ : "+m" (*i), [r] "=&r" (ret), [o] "=&r" (dummy), [t] "=&r" (test) : [i] "r" (i), [j] "r" (j) : "cc", "memory");
+
+ return ret;
+ }
+
+ static inline bool CompareAndSet_NoSync(volatile UInt32 *i, UInt32 c, UInt32 value)
+ {
+ UInt32 ret = 1, dummy, test;
+
+ asm volatile("1:\n\t"
+ "ldrex %[o],[%[i]]\n\t"
+ "cmp %[o],%[c]\n\t"
+ "bne 2f\n\t"
+ "strex %[r],%[v],[%[i]]\n\t"
+ "cmp %[r],#0\n\t"
+ "bne 1b\n\t"
+ "2:\n"
+ : "+m" (*i),[r] "=&r" (ret), [o] "=&r" (dummy), [t] "=&r" (test) : [i] "r" (i), [c] "r" (c), [v] "r" (value)
+ : "cc", "memory");
+
+ return !ret;
+ }
+
+#elif defined(OVR_CPU_X86)
+ typedef UInt32 T;
+
+ static inline UInt32 Exchange_NoSync(volatile UInt32 *i, UInt32 j)
+ {
+ asm volatile("xchgl %1,%[i]\n"
+ : "+m" (*i), "=q" (j) : [i] "m" (*i), "1" (j) : "cc", "memory");
+
+ return j;
+ }
+
+ static inline UInt32 ExchangeAdd_NoSync(volatile UInt32 *i, UInt32 j)
+ {
+ asm volatile("lock; xaddl %1,%[i]\n"
+ : "+m" (*i), "+q" (j) : [i] "m" (*i) : "cc", "memory");
+
+ return j;
+ }
+
+ static inline bool CompareAndSet_NoSync(volatile UInt32 *i, UInt32 c, UInt32 value)
+ {
+ UInt32 ret;
+
+ asm volatile("lock; cmpxchgl %[v],%[i]\n"
+ : "+m" (*i), "=a" (ret) : [i] "m" (*i), "1" (c), [v] "q" (value) : "cc", "memory");
+
+ return (ret == c);
+ }
+
+#elif defined(OVR_CC_GNU) && (__GNUC__ >= 4 && __GNUC_MINOR__ >= 1)
+
+ typedef UInt32 T;
+
+ static inline T Exchange_NoSync(volatile T *i, T j)
+ {
+ T v;
+ do {
+ v = *i;
+ } while (!__sync_bool_compare_and_swap(i, v, j));
+ return v;
+ }
+
+ static inline T ExchangeAdd_NoSync(volatile T *i, T j)
+ {
+ return __sync_fetch_and_add(i, j);
+ }
+
+ static inline bool CompareAndSet_NoSync(volatile T *i, T c, T value)
+ {
+ return __sync_bool_compare_and_swap(i, c, value);
+ }
+
+#endif // OS
+};
+
+
+// 8-Byte raw data data atomic op implementation class.
+// Currently implementation is provided only on systems with 64-bit pointers.
+struct AtomicOpsRaw_8ByteImpl : public AtomicOpsRawBase
+{
+#if !defined(OVR_64BIT_POINTERS) || !defined(OVR_ENABLE_THREADS)
+
+ // Provide a type for no-thread-support cases. Used by AtomicOpsRaw_DefImpl.
+ typedef UInt64 T;
+
+ // *** Thread - Safe OS specific versions.
+#elif defined(OVR_OS_WIN32)
+
+ // This is only for 64-bit systems.
+ typedef LONG64 T;
+ typedef volatile T* InterlockTPtr;
+ inline static T Exchange_NoSync(volatile T* p, T val) { return InterlockedExchange64((InterlockTPtr)p, val); }
+ inline static T ExchangeAdd_NoSync(volatile T* p, T val) { return InterlockedExchangeAdd64((InterlockTPtr)p, val); }
+ inline static bool CompareAndSet_NoSync(volatile T* p, T c, T val) { return InterlockedCompareExchange64((InterlockTPtr)p, val, c) == c; }
+
+#elif defined(OVR_CPU_PPC64)
+
+ typedef UInt64 T;
+
+ static inline UInt64 Exchange_NoSync(volatile UInt64 *i, UInt64 j)
+ {
+ UInt64 dummy, ret;
+
+ asm volatile("1:\n\t"
+ "ldarx %[r],0,%[i]\n\t"
+ "mr %[o],%[j]\n\t"
+ "stdcx. %[o],0,%[i]\n\t"
+ "bne- 1b\n"
+ : "+m" (*i), [r] "=&b" (ret), [o] "=&r" (dummy) : [i] "b" (i), [j] "b" (j) : "cc");
+
+ return ret;
+ }
+
+ static inline UInt64 ExchangeAdd_NoSync(volatile UInt64 *i, UInt64 j)
+ {
+ UInt64 dummy, ret;
+
+ asm volatile("1:\n\t"
+ "ldarx %[r],0,%[i]\n\t"
+ "add %[o],%[r],%[j]\n\t"
+ "stdcx. %[o],0,%[i]\n\t"
+ "bne- 1b\n"
+ : "+m" (*i), [r] "=&b" (ret), [o] "=&r" (dummy) : [i] "b" (i), [j] "b" (j) : "cc");
+
+ return ret;
+ }
+
+ static inline bool CompareAndSet_NoSync(volatile UInt64 *i, UInt64 c, UInt64 value)
+ {
+ UInt64 ret, dummy;
+
+ asm volatile("1:\n\t"
+ "ldarx %[r],0,%[i]\n\t"
+ "cmpw 0,%[r],%[cmp]\n\t"
+ "mfcr %[r]\n\t"
+ "bne- 2f\n\t"
+ "stdcx. %[val],0,%[i]\n\t"
+ "bne- 1b\n\t"
+ "2:\n"
+ : "+m" (*i), [r] "=&b" (ret), [o] "=&r" (dummy) : [i] "b" (i), [cmp] "b" (c), [val] "b" (value) : "cc");
+
+ return (ret & 0x20000000) ? 1 : 0;
+ }
+
+#elif defined(OVR_CC_GNU) && (__GNUC__ >= 4 && __GNUC_MINOR__ >= 1)
+
+ typedef UInt64 T;
+
+ static inline T Exchange_NoSync(volatile T *i, T j)
+ {
+ T v;
+ do {
+ v = *i;
+ } while (!__sync_bool_compare_and_swap(i, v, j));
+ return v;
+ }
+
+ static inline T ExchangeAdd_NoSync(volatile T *i, T j)
+ {
+ return __sync_fetch_and_add(i, j);
+ }
+
+ static inline bool CompareAndSet_NoSync(volatile T *i, T c, T value)
+ {
+ return __sync_bool_compare_and_swap(i, c, value);
+ }
+
+#endif // OS
+};
+
+
+// Default implementation for AtomicOpsRaw; provides implementation of mem-fenced
+// atomic operations where fencing is done with a sync object wrapped around a NoSync
+// operation implemented in the base class. If such implementation is not possible
+// on a given platform, #ifdefs can be used to disable it and then op functions can be
+// implemented individually in the appropriate AtomicOpsRaw<size> class.
+
+template<class O>
+struct AtomicOpsRaw_DefImpl : public O
+{
+ typedef typename O::T O_T;
+ typedef typename O::FullSync O_FullSync;
+ typedef typename O::AcquireSync O_AcquireSync;
+ typedef typename O::ReleaseSync O_ReleaseSync;
+
+ // If there is no thread support, provide the default implementation. In this case,
+ // the base class (0) must still provide the T declaration.
+#ifndef OVR_ENABLE_THREADS
+
+ // Atomic exchange of val with argument. Returns old val.
+ inline static O_T Exchange_NoSync(volatile O_T* p, O_T val) { O_T old = *p; *p = val; return old; }
+ // Adds a new val to argument; returns its old val.
+ inline static O_T ExchangeAdd_NoSync(volatile O_T* p, O_T val) { O_T old = *p; *p += val; return old; }
+ // Compares the argument data with 'c' val.
+ // If succeeded, stores val int '*p' and returns true; otherwise returns false.
+ inline static bool CompareAndSet_NoSync(volatile O_T* p, O_T c, O_T val) { if (*p==c) { *p = val; return 1; } return 0; }
+
+#endif
+
+ // If NoSync wrapped implementation may not be possible, it this block should be
+ // replaced with per-function implementation in O.
+ // "AtomicOpsRaw_DefImpl<O>::" prefix in calls below.
+ inline static O_T Exchange_Sync(volatile O_T* p, O_T val) { O_FullSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl<O>::Exchange_NoSync(p, val); }
+ inline static O_T Exchange_Release(volatile O_T* p, O_T val) { O_ReleaseSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl<O>::Exchange_NoSync(p, val); }
+ inline static O_T Exchange_Acquire(volatile O_T* p, O_T val) { O_AcquireSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl<O>::Exchange_NoSync(p, val); }
+ inline static O_T ExchangeAdd_Sync(volatile O_T* p, O_T val) { O_FullSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl<O>::ExchangeAdd_NoSync(p, val); }
+ inline static O_T ExchangeAdd_Release(volatile O_T* p, O_T val) { O_ReleaseSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl<O>::ExchangeAdd_NoSync(p, val); }
+ inline static O_T ExchangeAdd_Acquire(volatile O_T* p, O_T val) { O_AcquireSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl<O>::ExchangeAdd_NoSync(p, val); }
+ inline static bool CompareAndSet_Sync(volatile O_T* p, O_T c, O_T val) { O_FullSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl<O>::CompareAndSet_NoSync(p,c,val); }
+ inline static bool CompareAndSet_Release(volatile O_T* p, O_T c, O_T val) { O_ReleaseSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl<O>::CompareAndSet_NoSync(p,c,val); }
+ inline static bool CompareAndSet_Acquire(volatile O_T* p, O_T c, O_T val) { O_AcquireSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl<O>::CompareAndSet_NoSync(p,c,val); }
+
+ // Loads and stores with memory fence. These have only the relevant versions.
+#ifdef OVR_CPU_X86
+ // On X86, Store_Release is implemented as exchange. Note that we can also
+ // consider 'sfence' in the future, although it is not as compatible with older CPUs.
+ inline static void Store_Release(volatile O_T* p, O_T val) { Exchange_Release(p, val); }
+#else
+ inline static void Store_Release(volatile O_T* p, O_T val) { O_ReleaseSync sync; OVR_UNUSED(sync); *p = val; }
+#endif
+ inline static O_T Load_Acquire(const volatile O_T* p) { O_AcquireSync sync; OVR_UNUSED(sync); return *p; }
+};
+
+
+template<int size>
+struct AtomicOpsRaw : public AtomicOpsRawBase { };
+
+template<>
+struct AtomicOpsRaw<4> : public AtomicOpsRaw_DefImpl<AtomicOpsRaw_4ByteImpl>
+{
+ // Ensure that assigned type size is correct.
+ AtomicOpsRaw()
+ { OVR_COMPILER_ASSERT(sizeof(AtomicOpsRaw_DefImpl<AtomicOpsRaw_4ByteImpl>::T) == 4); }
+};
+template<>
+struct AtomicOpsRaw<8> : public AtomicOpsRaw_DefImpl<AtomicOpsRaw_8ByteImpl>
+{
+ AtomicOpsRaw()
+ { OVR_COMPILER_ASSERT(sizeof(AtomicOpsRaw_DefImpl<AtomicOpsRaw_8ByteImpl>::T) == 8); }
+};
+
+
+// *** AtomicOps - implementation of atomic Ops for specified class
+
+// Implements atomic ops on a class, provided that the object is either
+// 4 or 8 bytes in size (depending on the AtomicOpsRaw specializations
+// available). Relies on AtomicOpsRaw for much of implementation.
+
+template<class C>
+class AtomicOps
+{
+ typedef AtomicOpsRaw<sizeof(C)> Ops;
+ typedef typename Ops::T T;
+ typedef volatile typename Ops::T* PT;
+ // We cast through unions to (1) avoid pointer size compiler warnings
+ // and (2) ensure that there are no problems with strict pointer aliasing.
+ union C2T_union { C c; T t; };
+
+public:
+ // General purpose implementation for standard syncs.
+ inline static C Exchange_Sync(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::Exchange_Sync((PT)p, u.t); return u.c; }
+ inline static C Exchange_Release(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::Exchange_Release((PT)p, u.t); return u.c; }
+ inline static C Exchange_Acquire(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::Exchange_Acquire((PT)p, u.t); return u.c; }
+ inline static C Exchange_NoSync(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::Exchange_NoSync((PT)p, u.t); return u.c; }
+ inline static C ExchangeAdd_Sync(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::ExchangeAdd_Sync((PT)p, u.t); return u.c; }
+ inline static C ExchangeAdd_Release(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::ExchangeAdd_Release((PT)p, u.t); return u.c; }
+ inline static C ExchangeAdd_Acquire(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::ExchangeAdd_Acquire((PT)p, u.t); return u.c; }
+ inline static C ExchangeAdd_NoSync(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::ExchangeAdd_NoSync((PT)p, u.t); return u.c; }
+ inline static bool CompareAndSet_Sync(volatile C* p, C c, C val) { C2T_union u,cu; u.c = val; cu.c = c; return Ops::CompareAndSet_Sync((PT)p, cu.t, u.t); }
+ inline static bool CompareAndSet_Release(volatile C* p, C c, C val){ C2T_union u,cu; u.c = val; cu.c = c; return Ops::CompareAndSet_Release((PT)p, cu.t, u.t); }
+ inline static bool CompareAndSet_Relse(volatile C* p, C c, C val){ C2T_union u,cu; u.c = val; cu.c = c; return Ops::CompareAndSet_Acquire((PT)p, cu.t, u.t); }
+ inline static bool CompareAndSet_NoSync(volatile C* p, C c, C val) { C2T_union u,cu; u.c = val; cu.c = c; return Ops::CompareAndSet_NoSync((PT)p, cu.t, u.t); }
+ // Loads and stores with memory fence. These have only the relevant versions.
+ inline static void Store_Release(volatile C* p, C val) { C2T_union u; u.c = val; Ops::Store_Release((PT)p, u.t); }
+ inline static C Load_Acquire(const volatile C* p) { C2T_union u; u.t = Ops::Load_Acquire((PT)p); return u.c; }
+};
+
+
+
+// Atomic value base class - implements operations shared for integers and pointers.
+template<class T>
+class AtomicValueBase
+{
+protected:
+ typedef AtomicOps<T> Ops;
+public:
+
+ volatile T Value;
+
+ inline AtomicValueBase() { }
+ explicit inline AtomicValueBase(T val) { Ops::Store_Release(&Value, val); }
+
+ // Most libraries (TBB and Joshua Scholar's) library do not do Load_Acquire
+ // here, since most algorithms do not require atomic loads. Needs some research.
+ inline operator T() const { return Value; }
+
+ // *** Standard Atomic inlines
+ inline T Exchange_Sync(T val) { return Ops::Exchange_Sync(&Value, val); }
+ inline T Exchange_Release(T val) { return Ops::Exchange_Release(&Value, val); }
+ inline T Exchange_Acquire(T val) { return Ops::Exchange_Acquire(&Value, val); }
+ inline T Exchange_NoSync(T val) { return Ops::Exchange_NoSync(&Value, val); }
+ inline bool CompareAndSet_Sync(T c, T val) { return Ops::CompareAndSet_Sync(&Value, c, val); }
+ inline bool CompareAndSet_Release(T c, T val) { return Ops::CompareAndSet_Release(&Value, c, val); }
+ inline bool CompareAndSet_Acquire(T c, T val) { return Ops::CompareAndSet_Relse(&Value, c, val); }
+ inline bool CompareAndSet_NoSync(T c, T val) { return Ops::CompareAndSet_NoSync(&Value, c, val); }
+ // Load & Store.
+ inline void Store_Release(T val) { Ops::Store_Release(&Value, val); }
+ inline T Load_Acquire() const { return Ops::Load_Acquire(&Value); }
+};
+
+
+// ***** AtomicPtr - Atomic pointer template
+
+// This pointer class supports atomic assignments with release,
+// increment / decrement operations, and conditional compare + set.
+
+template<class T>
+class AtomicPtr : public AtomicValueBase<T*>
+{
+ typedef typename AtomicValueBase<T*>::Ops Ops;
+
+public:
+ // Initialize pointer value to 0 by default; use Store_Release only with explicit constructor.
+ inline AtomicPtr() : AtomicValueBase<T*>() { this->Value = 0; }
+ explicit inline AtomicPtr(T* val) : AtomicValueBase<T*>(val) { }
+
+ // Pointer access.
+ inline T* operator -> () const { return this->Load_Acquire(); }
+
+ // It looks like it is convenient to have Load_Acquire characteristics
+ // for this, since that is convenient for algorithms such as linked
+ // list traversals that can be added to bu another thread.
+ inline operator T* () const { return this->Load_Acquire(); }
+
+
+ // *** Standard Atomic inlines (applicable to pointers)
+
+ // ExhangeAdd considers pointer size for pointers.
+ template<class I>
+ inline T* ExchangeAdd_Sync(I incr) { return Ops::ExchangeAdd_Sync(&this->Value, ((T*)0) + incr); }
+ template<class I>
+ inline T* ExchangeAdd_Release(I incr) { return Ops::ExchangeAdd_Release(&this->Value, ((T*)0) + incr); }
+ template<class I>
+ inline T* ExchangeAdd_Acquire(I incr) { return Ops::ExchangeAdd_Acquire(&this->Value, ((T*)0) + incr); }
+ template<class I>
+ inline T* ExchangeAdd_NoSync(I incr) { return Ops::ExchangeAdd_NoSync(&this->Value, ((T*)0) + incr); }
+
+ // *** Atomic Operators
+
+ inline T* operator = (T* val) { this->Store_Release(val); return val; }
+
+ template<class I>
+ inline T* operator += (I val) { return ExchangeAdd_Sync(val) + val; }
+ template<class I>
+ inline T* operator -= (I val) { return operator += (-val); }
+
+ inline T* operator ++ () { return ExchangeAdd_Sync(1) + 1; }
+ inline T* operator -- () { return ExchangeAdd_Sync(-1) - 1; }
+ inline T* operator ++ (int) { return ExchangeAdd_Sync(1); }
+ inline T* operator -- (int) { return ExchangeAdd_Sync(-1); }
+};
+
+
+// ***** AtomicInt - Atomic integer template
+
+// Implements an atomic integer type; the exact type to use is provided
+// as an argument. Supports atomic Acquire / Release semantics, atomic
+// arithmetic operations, and atomic conditional compare + set.
+
+template<class T>
+class AtomicInt : public AtomicValueBase<T>
+{
+ typedef typename AtomicValueBase<T>::Ops Ops;
+
+public:
+ inline AtomicInt() : AtomicValueBase<T>() { }
+ explicit inline AtomicInt(T val) : AtomicValueBase<T>(val) { }
+
+
+ // *** Standard Atomic inlines (applicable to int)
+ inline T ExchangeAdd_Sync(T val) { return Ops::ExchangeAdd_Sync(&this->Value, val); }
+ inline T ExchangeAdd_Release(T val) { return Ops::ExchangeAdd_Release(&this->Value, val); }
+ inline T ExchangeAdd_Acquire(T val) { return Ops::ExchangeAdd_Acquire(&this->Value, val); }
+ inline T ExchangeAdd_NoSync(T val) { return Ops::ExchangeAdd_NoSync(&this->Value, val); }
+ // These increments could be more efficient because they don't return a value.
+ inline void Increment_Sync() { ExchangeAdd_Sync((T)1); }
+ inline void Increment_Release() { ExchangeAdd_Release((T)1); }
+ inline void Increment_Acquire() { ExchangeAdd_Acquire((T)1); }
+ inline void Increment_NoSync() { ExchangeAdd_NoSync((T)1); }
+
+ // *** Atomic Operators
+
+ inline T operator = (T val) { this->Store_Release(val); return val; }
+ inline T operator += (T val) { return ExchangeAdd_Sync(val) + val; }
+ inline T operator -= (T val) { return ExchangeAdd_Sync(0 - val) - val; }
+
+ inline T operator ++ () { return ExchangeAdd_Sync((T)1) + 1; }
+ inline T operator -- () { return ExchangeAdd_Sync(((T)0)-1) - 1; }
+ inline T operator ++ (int) { return ExchangeAdd_Sync((T)1); }
+ inline T operator -- (int) { return ExchangeAdd_Sync(((T)0)-1); }
+
+ // More complex atomic operations. Leave it to compiler whether to optimize them or not.
+ T operator &= (T arg)
+ {
+ T comp, newVal;
+ do {
+ comp = this->Value;
+ newVal = comp & arg;
+ } while(!this->CompareAndSet_Sync(comp, newVal));
+ return newVal;
+ }
+
+ T operator |= (T arg)
+ {
+ T comp, newVal;
+ do {
+ comp = this->Value;
+ newVal = comp | arg;
+ } while(!this->CompareAndSet_Sync(comp, newVal));
+ return newVal;
+ }
+
+ T operator ^= (T arg)
+ {
+ T comp, newVal;
+ do {
+ comp = this->Value;
+ newVal = comp ^ arg;
+ } while(!this->CompareAndSet_Sync(comp, newVal));
+ return newVal;
+ }
+
+ T operator *= (T arg)
+ {
+ T comp, newVal;
+ do {
+ comp = this->Value;
+ newVal = comp * arg;
+ } while(!this->CompareAndSet_Sync(comp, newVal));
+ return newVal;
+ }
+
+ T operator /= (T arg)
+ {
+ T comp, newVal;
+ do {
+ comp = this->Value;
+ newVal = comp / arg;
+ } while(!CompareAndSet_Sync(comp, newVal));
+ return newVal;
+ }
+
+ T operator >>= (unsigned bits)
+ {
+ T comp, newVal;
+ do {
+ comp = this->Value;
+ newVal = comp >> bits;
+ } while(!CompareAndSet_Sync(comp, newVal));
+ return newVal;
+ }
+
+ T operator <<= (unsigned bits)
+ {
+ T comp, newVal;
+ do {
+ comp = this->Value;
+ newVal = comp << bits;
+ } while(!this->CompareAndSet_Sync(comp, newVal));
+ return newVal;
+ }
+};
+
+
+
+//-----------------------------------------------------------------------------------
+// ***** Lock
+
+// Lock is a simplest and most efficient mutual-exclusion lock class.
+// Unlike Mutex, it cannot be waited on.
+
+class Lock
+{
+ // NOTE: Locks are not allocatable and they themselves should not allocate
+ // memory by standard means. This is the case because StandardAllocator
+ // relies on this class.
+ // Make 'delete' private. Don't do this for 'new' since it can be redefined.
+ void operator delete(void*) {}
+
+
+ // *** Lock implementation for various platforms.
+
+#if !defined(OVR_ENABLE_THREADS)
+
+public:
+ // With no thread support, lock does nothing.
+ inline Lock() { }
+ inline Lock(unsigned) { }
+ inline ~Lock() { }
+ inline void DoLock() { }
+ inline void Unlock() { }
+
+ // Windows.
+#elif defined(OVR_OS_WIN32)
+
+ CRITICAL_SECTION cs;
+public:
+ Lock(unsigned spinCount = 0);
+ ~Lock();
+ // Locking functions.
+ inline void DoLock() { ::EnterCriticalSection(&cs); }
+ inline void Unlock() { ::LeaveCriticalSection(&cs); }
+
+#else
+ pthread_mutex_t mutex;
+
+public:
+ static pthread_mutexattr_t RecursiveAttr;
+ static bool RecursiveAttrInit;
+
+ Lock (unsigned dummy = 0)
+ {
+ if (!RecursiveAttrInit)
+ {
+ pthread_mutexattr_init(&RecursiveAttr);
+ pthread_mutexattr_settype(&RecursiveAttr, PTHREAD_MUTEX_RECURSIVE);
+ RecursiveAttrInit = 1;
+ }
+ pthread_mutex_init(&mutex,&RecursiveAttr);
+ }
+ ~Lock () { pthread_mutex_destroy(&mutex); }
+ inline void DoLock() { pthread_mutex_lock(&mutex); }
+ inline void Unlock() { pthread_mutex_unlock(&mutex); }
+
+#endif // OVR_ENABLE_THREDS
+
+
+public:
+ // Locker class, used for automatic locking
+ class Locker
+ {
+ public:
+ Lock *pLock;
+ inline Locker(Lock *plock)
+ { pLock = plock; pLock->DoLock(); }
+ inline ~Locker()
+ { pLock->Unlock(); }
+ };
+};
+
+
+
+} // OVR
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_Color.h b/LibOVR/Src/Kernel/OVR_Color.h
new file mode 100644
index 0000000..7980d57
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_Color.h
@@ -0,0 +1,55 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_Color.h
+Content : Contains color struct.
+Created : February 7, 2013
+Notes :
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+#ifndef OVR_Color_h
+#define OVR_Color_h
+
+#include "OVR_Types.h"
+
+namespace OVR {
+
+struct Color
+{
+ UByte R,G,B,A;
+
+ Color() {}
+
+ // Constructs color by channel. Alpha is set to 0xFF (fully visible)
+ // if not specified.
+ Color(unsigned char r,unsigned char g,unsigned char b, unsigned char a = 0xFF)
+ : R(r), G(g), B(b), A(a) { }
+
+ // 0xAARRGGBB - Common HTML color Hex layout
+ Color(unsigned c)
+ : R((unsigned char)(c>>16)), G((unsigned char)(c>>8)),
+ B((unsigned char)c), A((unsigned char)(c>>24)) { }
+
+ bool operator==(const Color& b) const
+ {
+ return R == b.R && G == b.G && B == b.B && A == b.A;
+ }
+
+ void GetRGBA(float *r, float *g, float *b, float* a) const
+ {
+ *r = R / 255.0f;
+ *g = G / 255.0f;
+ *b = B / 255.0f;
+ *a = A / 255.0f;
+ }
+};
+
+}
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_ContainerAllocator.h b/LibOVR/Src/Kernel/OVR_ContainerAllocator.h
new file mode 100644
index 0000000..d2adf49
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_ContainerAllocator.h
@@ -0,0 +1,256 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_ContainerAllocator.h
+Content : Template allocators and constructors for containers.
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#ifndef OVR_ContainerAllocator_h
+#define OVR_ContainerAllocator_h
+
+#include "OVR_Allocator.h"
+#include <string.h>
+
+
+namespace OVR {
+
+
+//-----------------------------------------------------------------------------------
+// ***** Container Allocator
+
+// ContainerAllocator serves as a template argument for allocations done by
+// containers, such as Array and Hash; replacing it could allow allocator
+// substitution in containers.
+
+class ContainerAllocatorBase
+{
+public:
+ static void* Alloc(UPInt size) { return OVR_ALLOC(size); }
+ static void* Realloc(void* p, UPInt newSize) { return OVR_REALLOC(p, newSize); }
+ static void Free(void *p) { OVR_FREE(p); }
+};
+
+
+
+//-----------------------------------------------------------------------------------
+// ***** Constructors, Destructors, Copiers
+
+// Plain Old Data - movable, no special constructors/destructor.
+template<class T>
+class ConstructorPOD
+{
+public:
+ static void Construct(void *) {}
+ static void Construct(void *p, const T& source)
+ {
+ *(T*)p = source;
+ }
+
+ // Same as above, but allows for a different type of constructor.
+ template <class S>
+ static void ConstructAlt(void *p, const S& source)
+ {
+ *(T*)p = source;
+ }
+
+ static void ConstructArray(void*, UPInt) {}
+
+ static void ConstructArray(void* p, UPInt count, const T& source)
+ {
+ UByte *pdata = (UByte*)p;
+ for (UPInt i=0; i< count; ++i, pdata += sizeof(T))
+ *(T*)pdata = source;
+ }
+
+ static void ConstructArray(void* p, UPInt count, const T* psource)
+ {
+ memcpy(p, psource, sizeof(T) * count);
+ }
+
+ static void Destruct(T*) {}
+ static void DestructArray(T*, UPInt) {}
+
+ static void CopyArrayForward(T* dst, const T* src, UPInt count)
+ {
+ memmove(dst, src, count * sizeof(T));
+ }
+
+ static void CopyArrayBackward(T* dst, const T* src, UPInt count)
+ {
+ memmove(dst, src, count * sizeof(T));
+ }
+
+ static bool IsMovable() { return true; }
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** ConstructorMov
+//
+// Correct C++ construction and destruction for movable objects
+template<class T>
+class ConstructorMov
+{
+public:
+ static void Construct(void* p)
+ {
+ OVR::Construct<T>(p);
+ }
+
+ static void Construct(void* p, const T& source)
+ {
+ OVR::Construct<T>(p, source);
+ }
+
+ // Same as above, but allows for a different type of constructor.
+ template <class S>
+ static void ConstructAlt(void* p, const S& source)
+ {
+ OVR::ConstructAlt<T,S>(p, source);
+ }
+
+ static void ConstructArray(void* p, UPInt count)
+ {
+ UByte* pdata = (UByte*)p;
+ for (UPInt i=0; i< count; ++i, pdata += sizeof(T))
+ Construct(pdata);
+ }
+
+ static void ConstructArray(void* p, UPInt count, const T& source)
+ {
+ UByte* pdata = (UByte*)p;
+ for (UPInt i=0; i< count; ++i, pdata += sizeof(T))
+ Construct(pdata, source);
+ }
+
+ static void ConstructArray(void* p, UPInt count, const T* psource)
+ {
+ UByte* pdata = (UByte*)p;
+ for (UPInt i=0; i< count; ++i, pdata += sizeof(T))
+ Construct(pdata, *psource++);
+ }
+
+ static void Destruct(T* p)
+ {
+ p->~T();
+ OVR_UNUSED(p); // Suppress silly MSVC warning
+ }
+
+ static void DestructArray(T* p, UPInt count)
+ {
+ p += count - 1;
+ for (UPInt i=0; i<count; ++i, --p)
+ p->~T();
+ }
+
+ static void CopyArrayForward(T* dst, const T* src, UPInt count)
+ {
+ memmove(dst, src, count * sizeof(T));
+ }
+
+ static void CopyArrayBackward(T* dst, const T* src, UPInt count)
+ {
+ memmove(dst, src, count * sizeof(T));
+ }
+
+ static bool IsMovable() { return true; }
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** ConstructorCPP
+//
+// Correct C++ construction and destruction for movable objects
+template<class T>
+class ConstructorCPP
+{
+public:
+ static void Construct(void* p)
+ {
+ OVR::Construct<T>(p);
+ }
+
+ static void Construct(void* p, const T& source)
+ {
+ OVR::Construct<T>(p, source);
+ }
+
+ // Same as above, but allows for a different type of constructor.
+ template <class S>
+ static void ConstructAlt(void* p, const S& source)
+ {
+ OVR::ConstructAlt<T,S>(p, source);
+ }
+
+ static void ConstructArray(void* p, UPInt count)
+ {
+ UByte* pdata = (UByte*)p;
+ for (UPInt i=0; i< count; ++i, pdata += sizeof(T))
+ Construct(pdata);
+ }
+
+ static void ConstructArray(void* p, UPInt count, const T& source)
+ {
+ UByte* pdata = (UByte*)p;
+ for (UPInt i=0; i< count; ++i, pdata += sizeof(T))
+ Construct(pdata, source);
+ }
+
+ static void ConstructArray(void* p, UPInt count, const T* psource)
+ {
+ UByte* pdata = (UByte*)p;
+ for (UPInt i=0; i< count; ++i, pdata += sizeof(T))
+ Construct(pdata, *psource++);
+ }
+
+ static void Destruct(T* p)
+ {
+ p->~T();
+ OVR_UNUSED(p); // Suppress silly MSVC warning
+ }
+
+ static void DestructArray(T* p, UPInt count)
+ {
+ p += count - 1;
+ for (UPInt i=0; i<count; ++i, --p)
+ p->~T();
+ }
+
+ static void CopyArrayForward(T* dst, const T* src, UPInt count)
+ {
+ for(UPInt i = 0; i < count; ++i)
+ dst[i] = src[i];
+ }
+
+ static void CopyArrayBackward(T* dst, const T* src, UPInt count)
+ {
+ for(UPInt i = count; i; --i)
+ dst[i-1] = src[i-1];
+ }
+
+ static bool IsMovable() { return false; }
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** Container Allocator with movement policy
+//
+// Simple wraps as specialized allocators
+template<class T> struct ContainerAllocator_POD : ContainerAllocatorBase, ConstructorPOD<T> {};
+template<class T> struct ContainerAllocator : ContainerAllocatorBase, ConstructorMov<T> {};
+template<class T> struct ContainerAllocator_CPP : ContainerAllocatorBase, ConstructorCPP<T> {};
+
+
+} // OVR
+
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_File.cpp b/LibOVR/Src/Kernel/OVR_File.cpp
new file mode 100644
index 0000000..7620e37
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_File.cpp
@@ -0,0 +1,571 @@
+/**************************************************************************
+
+Filename : OVR_File.cpp
+Content : File wrapper class implementation (Win32)
+
+Created : April 5, 1999
+Authors : Michael Antonov
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+**************************************************************************/
+
+#define GFILE_CXX
+
+// Standard C library (Captain Obvious guarantees!)
+#include <stdio.h>
+
+#include "OVR_File.h"
+
+namespace OVR {
+
+// Buffered file adds buffering to an existing file
+// FILEBUFFER_SIZE defines the size of internal buffer, while
+// FILEBUFFER_TOLERANCE controls the amount of data we'll effectively try to buffer
+#define FILEBUFFER_SIZE (8192-8)
+#define FILEBUFFER_TOLERANCE 4096
+
+// ** Constructor/Destructor
+
+// Hidden constructor
+// Not supposed to be used
+BufferedFile::BufferedFile() : DelegatedFile(0)
+{
+ pBuffer = (UByte*)OVR_ALLOC(FILEBUFFER_SIZE);
+ BufferMode = NoBuffer;
+ FilePos = 0;
+ Pos = 0;
+ DataSize = 0;
+}
+
+// Takes another file as source
+BufferedFile::BufferedFile(File *pfile) : DelegatedFile(pfile)
+{
+ pBuffer = (UByte*)OVR_ALLOC(FILEBUFFER_SIZE);
+ BufferMode = NoBuffer;
+ FilePos = pfile->LTell();
+ Pos = 0;
+ DataSize = 0;
+}
+
+
+// Destructor
+BufferedFile::~BufferedFile()
+{
+ // Flush in case there's data
+ if (pFile)
+ FlushBuffer();
+ // Get rid of buffer
+ if (pBuffer)
+ OVR_FREE(pBuffer);
+}
+
+/*
+bool BufferedFile::VCopy(const Object &source)
+{
+ if (!DelegatedFile::VCopy(source))
+ return 0;
+
+ // Data members
+ BufferedFile *psource = (BufferedFile*)&source;
+
+ // Buffer & the mode it's in
+ pBuffer = psource->pBuffer;
+ BufferMode = psource->BufferMode;
+ Pos = psource->Pos;
+ DataSize = psource->DataSize;
+ return 1;
+}
+*/
+
+// Initializes buffering to a certain mode
+bool BufferedFile::SetBufferMode(BufferModeType mode)
+{
+ if (!pBuffer)
+ return false;
+ if (mode == BufferMode)
+ return true;
+
+ FlushBuffer();
+
+ // Can't set write mode if we can't write
+ if ((mode==WriteBuffer) && (!pFile || !pFile->IsWritable()) )
+ return 0;
+
+ // And SetMode
+ BufferMode = mode;
+ Pos = 0;
+ DataSize = 0;
+ return 1;
+}
+
+// Flushes buffer
+void BufferedFile::FlushBuffer()
+{
+ switch(BufferMode)
+ {
+ case WriteBuffer:
+ // Write data in buffer
+ FilePos += pFile->Write(pBuffer,Pos);
+ Pos = 0;
+ break;
+
+ case ReadBuffer:
+ // Seek back & reset buffer data
+ if ((DataSize-Pos)>0)
+ FilePos = pFile->LSeek(-(int)(DataSize-Pos), Seek_Cur);
+ DataSize = 0;
+ Pos = 0;
+ break;
+ default:
+ // not handled!
+ break;
+ }
+}
+
+// Reloads data for ReadBuffer
+void BufferedFile::LoadBuffer()
+{
+ if (BufferMode == ReadBuffer)
+ {
+ // We should only reload once all of pre-loaded buffer is consumed.
+ OVR_ASSERT(Pos == DataSize);
+
+ // WARNING: Right now LoadBuffer() assumes the buffer's empty
+ int sz = pFile->Read(pBuffer,FILEBUFFER_SIZE);
+ DataSize = sz<0 ? 0 : (unsigned)sz;
+ Pos = 0;
+ FilePos += DataSize;
+ }
+}
+
+
+// ** Overridden functions
+
+// We override all the functions that can possibly
+// require buffer mode switch, flush, or extra calculations
+
+// Tell() requires buffer adjustment
+int BufferedFile::Tell()
+{
+ if (BufferMode == ReadBuffer)
+ return int (FilePos - DataSize + Pos);
+
+ int pos = pFile->Tell();
+ // Adjust position based on buffer mode & data
+ if (pos!=-1)
+ {
+ OVR_ASSERT(BufferMode != ReadBuffer);
+ if (BufferMode == WriteBuffer)
+ pos += Pos;
+ }
+ return pos;
+}
+
+SInt64 BufferedFile::LTell()
+{
+ if (BufferMode == ReadBuffer)
+ return FilePos - DataSize + Pos;
+
+ SInt64 pos = pFile->LTell();
+ if (pos!=-1)
+ {
+ OVR_ASSERT(BufferMode != ReadBuffer);
+ if (BufferMode == WriteBuffer)
+ pos += Pos;
+ }
+ return pos;
+}
+
+int BufferedFile::GetLength()
+{
+ int len = pFile->GetLength();
+ // If writing through buffer, file length may actually be bigger
+ if ((len!=-1) && (BufferMode==WriteBuffer))
+ {
+ int currPos = pFile->Tell() + Pos;
+ if (currPos>len)
+ len = currPos;
+ }
+ return len;
+}
+SInt64 BufferedFile::LGetLength()
+{
+ SInt64 len = pFile->LGetLength();
+ // If writing through buffer, file length may actually be bigger
+ if ((len!=-1) && (BufferMode==WriteBuffer))
+ {
+ SInt64 currPos = pFile->LTell() + Pos;
+ if (currPos>len)
+ len = currPos;
+ }
+ return len;
+}
+
+/*
+bool BufferedFile::Stat(FileStats *pfs)
+{
+ // Have to fix up length is stat
+ if (pFile->Stat(pfs))
+ {
+ if (BufferMode==WriteBuffer)
+ {
+ SInt64 currPos = pFile->LTell() + Pos;
+ if (currPos > pfs->Size)
+ {
+ pfs->Size = currPos;
+ // ??
+ pfs->Blocks = (pfs->Size+511) >> 9;
+ }
+ }
+ return 1;
+ }
+ return 0;
+}
+*/
+
+int BufferedFile::Write(const UByte *psourceBuffer, int numBytes)
+{
+ if ( (BufferMode==WriteBuffer) || SetBufferMode(WriteBuffer))
+ {
+ // If not data space in buffer, flush
+ if ((FILEBUFFER_SIZE-(int)Pos)<numBytes)
+ {
+ FlushBuffer();
+ // If bigger then tolerance, just write directly
+ if (numBytes>FILEBUFFER_TOLERANCE)
+ {
+ int sz = pFile->Write(psourceBuffer,numBytes);
+ if (sz > 0)
+ FilePos += sz;
+ return sz;
+ }
+ }
+
+ // Enough space in buffer.. so copy to it
+ memcpy(pBuffer+Pos, psourceBuffer, numBytes);
+ Pos += numBytes;
+ return numBytes;
+ }
+ int sz = pFile->Write(psourceBuffer,numBytes);
+ if (sz > 0)
+ FilePos += sz;
+ return sz;
+}
+
+int BufferedFile::Read(UByte *pdestBuffer, int numBytes)
+{
+ if ( (BufferMode==ReadBuffer) || SetBufferMode(ReadBuffer))
+ {
+ // Data in buffer... copy it
+ if ((int)(DataSize-Pos) >= numBytes)
+ {
+ memcpy(pdestBuffer, pBuffer+Pos, numBytes);
+ Pos += numBytes;
+ return numBytes;
+ }
+
+ // Not enough data in buffer, copy buffer
+ int readBytes = DataSize-Pos;
+ memcpy(pdestBuffer, pBuffer+Pos, readBytes);
+ numBytes -= readBytes;
+ pdestBuffer += readBytes;
+ Pos = DataSize;
+
+ // Don't reload buffer if more then tolerance
+ // (No major advantage, and we don't want to write a loop)
+ if (numBytes>FILEBUFFER_TOLERANCE)
+ {
+ numBytes = pFile->Read(pdestBuffer,numBytes);
+ if (numBytes > 0)
+ {
+ FilePos += numBytes;
+ Pos = DataSize = 0;
+ }
+ return readBytes + ((numBytes==-1) ? 0 : numBytes);
+ }
+
+ // Reload the buffer
+ // WARNING: Right now LoadBuffer() assumes the buffer's empty
+ LoadBuffer();
+ if ((int)(DataSize-Pos) < numBytes)
+ numBytes = (int)DataSize-Pos;
+
+ memcpy(pdestBuffer, pBuffer+Pos, numBytes);
+ Pos += numBytes;
+ return numBytes + readBytes;
+
+ /*
+ // Alternative Read implementation. The one above is probably better
+ // due to FILEBUFFER_TOLERANCE.
+ int total = 0;
+
+ do {
+ int bufferBytes = (int)(DataSize-Pos);
+ int copyBytes = (bufferBytes > numBytes) ? numBytes : bufferBytes;
+
+ memcpy(pdestBuffer, pBuffer+Pos, copyBytes);
+ numBytes -= copyBytes;
+ pdestBuffer += copyBytes;
+ Pos += copyBytes;
+ total += copyBytes;
+
+ if (numBytes == 0)
+ break;
+ LoadBuffer();
+
+ } while (DataSize > 0);
+
+ return total;
+ */
+ }
+ int sz = pFile->Read(pdestBuffer,numBytes);
+ if (sz > 0)
+ FilePos += sz;
+ return sz;
+}
+
+
+int BufferedFile::SkipBytes(int numBytes)
+{
+ int skippedBytes = 0;
+
+ // Special case for skipping a little data in read buffer
+ if (BufferMode==ReadBuffer)
+ {
+ skippedBytes = (((int)DataSize-(int)Pos) >= numBytes) ? numBytes : (DataSize-Pos);
+ Pos += skippedBytes;
+ numBytes -= skippedBytes;
+ }
+
+ if (numBytes)
+ {
+ numBytes = pFile->SkipBytes(numBytes);
+ // Make sure we return the actual number skipped, or error
+ if (numBytes!=-1)
+ {
+ skippedBytes += numBytes;
+ FilePos += numBytes;
+ Pos = DataSize = 0;
+ }
+ else if (skippedBytes <= 0)
+ skippedBytes = -1;
+ }
+ return skippedBytes;
+}
+
+int BufferedFile::BytesAvailable()
+{
+ int available = pFile->BytesAvailable();
+ // Adjust available size based on buffers
+ switch(BufferMode)
+ {
+ case ReadBuffer:
+ available += DataSize-Pos;
+ break;
+ case WriteBuffer:
+ available -= Pos;
+ if (available<0)
+ available= 0;
+ break;
+ default:
+ break;
+ }
+ return available;
+}
+
+bool BufferedFile::Flush()
+{
+ FlushBuffer();
+ return pFile->Flush();
+}
+
+// Seeking could be optimized better..
+int BufferedFile::Seek(int offset, int origin)
+{
+ if (BufferMode == ReadBuffer)
+ {
+ if (origin == Seek_Cur)
+ {
+ // Seek can fall either before or after Pos in the buffer,
+ // but it must be within bounds.
+ if (((unsigned(offset) + Pos)) <= DataSize)
+ {
+ Pos += offset;
+ return int (FilePos - DataSize + Pos);
+ }
+
+ // Lightweight buffer "Flush". We do this to avoid an extra seek
+ // back operation which would take place if we called FlushBuffer directly.
+ origin = Seek_Set;
+ OVR_ASSERT(((FilePos - DataSize + Pos) + (UInt64)offset) < ~(UInt64)0);
+ offset = (int)(FilePos - DataSize + Pos) + offset;
+ Pos = DataSize = 0;
+ }
+ else if (origin == Seek_Set)
+ {
+ if (((unsigned)offset - (FilePos-DataSize)) <= DataSize)
+ {
+ OVR_ASSERT((FilePos-DataSize) < ~(UInt64)0);
+ Pos = (unsigned)offset - (unsigned)(FilePos-DataSize);
+ return offset;
+ }
+ Pos = DataSize = 0;
+ }
+ else
+ {
+ FlushBuffer();
+ }
+ }
+ else
+ {
+ FlushBuffer();
+ }
+
+ /*
+ // Old Seek Logic
+ if (origin == Seek_Cur && offset + Pos < DataSize)
+ {
+ //OVR_ASSERT((FilePos - DataSize) >= (FilePos - DataSize + Pos + offset));
+ Pos += offset;
+ OVR_ASSERT(int (Pos) >= 0);
+ return int (FilePos - DataSize + Pos);
+ }
+ else if (origin == Seek_Set && unsigned(offset) >= FilePos - DataSize && unsigned(offset) < FilePos)
+ {
+ Pos = unsigned(offset - FilePos + DataSize);
+ OVR_ASSERT(int (Pos) >= 0);
+ return int (FilePos - DataSize + Pos);
+ }
+
+ FlushBuffer();
+ */
+
+
+ FilePos = pFile->Seek(offset,origin);
+ return int (FilePos);
+}
+
+SInt64 BufferedFile::LSeek(SInt64 offset, int origin)
+{
+ if (BufferMode == ReadBuffer)
+ {
+ if (origin == Seek_Cur)
+ {
+ // Seek can fall either before or after Pos in the buffer,
+ // but it must be within bounds.
+ if (((unsigned(offset) + Pos)) <= DataSize)
+ {
+ Pos += (unsigned)offset;
+ return SInt64(FilePos - DataSize + Pos);
+ }
+
+ // Lightweight buffer "Flush". We do this to avoid an extra seek
+ // back operation which would take place if we called FlushBuffer directly.
+ origin = Seek_Set;
+ offset = (SInt64)(FilePos - DataSize + Pos) + offset;
+ Pos = DataSize = 0;
+ }
+ else if (origin == Seek_Set)
+ {
+ if (((UInt64)offset - (FilePos-DataSize)) <= DataSize)
+ {
+ Pos = (unsigned)((UInt64)offset - (FilePos-DataSize));
+ return offset;
+ }
+ Pos = DataSize = 0;
+ }
+ else
+ {
+ FlushBuffer();
+ }
+ }
+ else
+ {
+ FlushBuffer();
+ }
+
+/*
+ OVR_ASSERT(BufferMode != NoBuffer);
+
+ if (origin == Seek_Cur && offset + Pos < DataSize)
+ {
+ Pos += int (offset);
+ return FilePos - DataSize + Pos;
+ }
+ else if (origin == Seek_Set && offset >= SInt64(FilePos - DataSize) && offset < SInt64(FilePos))
+ {
+ Pos = unsigned(offset - FilePos + DataSize);
+ return FilePos - DataSize + Pos;
+ }
+
+ FlushBuffer();
+ */
+
+ FilePos = pFile->LSeek(offset,origin);
+ return FilePos;
+}
+
+int BufferedFile::CopyFromStream(File *pstream, int byteSize)
+{
+ // We can't rely on overridden Write()
+ // because delegation doesn't override virtual pointers
+ // So, just re-implement
+ UByte buff[0x4000];
+ int count = 0;
+ int szRequest, szRead, szWritten;
+
+ while(byteSize)
+ {
+ szRequest = (byteSize > int(sizeof(buff))) ? int(sizeof(buff)) : byteSize;
+
+ szRead = pstream->Read(buff,szRequest);
+ szWritten = 0;
+ if (szRead > 0)
+ szWritten = Write(buff,szRead);
+
+ count +=szWritten;
+ byteSize-=szWritten;
+ if (szWritten < szRequest)
+ break;
+ }
+ return count;
+}
+
+// Closing files
+bool BufferedFile::Close()
+{
+ switch(BufferMode)
+ {
+ case WriteBuffer:
+ FlushBuffer();
+ break;
+ case ReadBuffer:
+ // No need to seek back on close
+ BufferMode = NoBuffer;
+ break;
+ default:
+ break;
+ }
+ return pFile->Close();
+}
+
+
+// ***** Global path helpers
+
+// Find trailing short filename in a path.
+const char* OVR_CDECL GetShortFilename(const char* purl)
+{
+ UPInt len = OVR_strlen(purl);
+ for (UPInt i=len; i>0; i--)
+ if (purl[i]=='\\' || purl[i]=='/')
+ return purl+i+1;
+ return purl;
+}
+
+} // OVR
+
diff --git a/LibOVR/Src/Kernel/OVR_File.h b/LibOVR/Src/Kernel/OVR_File.h
new file mode 100644
index 0000000..262f5cf
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_File.h
@@ -0,0 +1,518 @@
+/************************************************************************************
+
+PublicHeader: Kernel
+Filename : OVR_File.h
+Content : Header for all internal file management - functions and structures
+ to be inherited by OS specific subclasses.
+Created : September 19, 2012
+Notes :
+
+Notes : errno may not be preserved across use of BaseFile member functions
+ : Directories cannot be deleted while files opened from them are in use
+ (For the GetFullName function)
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#ifndef OVR_File_h
+#define OVR_File_h
+
+#include "OVR_RefCount.h"
+#include "OVR_Std.h"
+#include "OVR_Alg.h"
+
+#include <stdio.h>
+#include "OVR_String.h"
+
+namespace OVR {
+
+// ***** Declared classes
+class FileConstants;
+class File;
+class DelegatedFile;
+class BufferedFile;
+
+
+// ***** Flags for File & Directory accesses
+
+class FileConstants
+{
+public:
+
+ // *** File open flags
+ enum OpenFlags
+ {
+ Open_Read = 1,
+ Open_Write = 2,
+ Open_ReadWrite = 3,
+
+ // Opens file and truncates it to zero length
+ // - file must have write permission
+ // - when used with Create, it opens an existing
+ // file and empties it or creates a new file
+ Open_Truncate = 4,
+
+ // Creates and opens new file
+ // - does not erase contents if file already
+ // exists unless combined with Truncate
+ Open_Create = 8,
+
+ // Returns an error value if the file already exists
+ Open_CreateOnly = 24,
+
+ // Open file with buffering
+ Open_Buffered = 32
+ };
+
+ // *** File Mode flags
+ enum Modes
+ {
+ Mode_Read = 0444,
+ Mode_Write = 0222,
+ Mode_Execute = 0111,
+
+ Mode_ReadWrite = 0666
+ };
+
+ // *** Seek operations
+ enum SeekOps
+ {
+ Seek_Set = 0,
+ Seek_Cur = 1,
+ Seek_End = 2
+ };
+
+ // *** Errors
+ enum Errors
+ {
+ Error_FileNotFound = 0x1001,
+ Error_Access = 0x1002,
+ Error_IOError = 0x1003,
+ Error_DiskFull = 0x1004
+ };
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** File Class
+
+// The pure virtual base random-access file
+// This is a base class to all files
+
+class File : public RefCountBase<File>, public FileConstants
+{
+public:
+ File() { }
+ // ** Location Information
+
+ // Returns a file name path relative to the 'reference' directory
+ // This is often a path that was used to create a file
+ // (this is not a global path, global path can be obtained with help of directory)
+ virtual const char* GetFilePath() = 0;
+
+
+ // ** File Information
+
+ // Return 1 if file's usable (open)
+ virtual bool IsValid() = 0;
+ // Return 1 if file's writable, otherwise 0
+ virtual bool IsWritable() = 0;
+
+ // Return position
+ virtual int Tell() = 0;
+ virtual SInt64 LTell() = 0;
+
+ // File size
+ virtual int GetLength() = 0;
+ virtual SInt64 LGetLength() = 0;
+
+ // Returns file stats
+ // 0 for failure
+ //virtual bool Stat(FileStats *pfs) = 0;
+
+ // Return errno-based error code
+ // Useful if any other function failed
+ virtual int GetErrorCode() = 0;
+
+
+ // ** Stream implementation & I/O
+
+ // Blocking write, will write in the given number of bytes to the stream
+ // Returns : -1 for error
+ // Otherwise number of bytes read
+ virtual int Write(const UByte *pbufer, int numBytes) = 0;
+ // Blocking read, will read in the given number of bytes or less from the stream
+ // Returns : -1 for error
+ // Otherwise number of bytes read,
+ // if 0 or < numBytes, no more bytes available; end of file or the other side of stream is closed
+ virtual int Read(UByte *pbufer, int numBytes) = 0;
+
+ // Skips (ignores) a given # of bytes
+ // Same return values as Read
+ virtual int SkipBytes(int numBytes) = 0;
+
+ // Returns the number of bytes available to read from a stream without blocking
+ // For a file, this should generally be number of bytes to the end
+ virtual int BytesAvailable() = 0;
+
+ // Causes any implementation's buffered data to be delivered to destination
+ // Return 0 for error
+ virtual bool Flush() = 0;
+
+
+ // Need to provide a more optimized implementation that doe snot necessarily involve a lot of seeking
+ inline bool IsEOF() { return !BytesAvailable(); }
+
+
+ // Seeking
+ // Returns new position, -1 for error
+ virtual int Seek(int offset, int origin=Seek_Set) = 0;
+ virtual SInt64 LSeek(SInt64 offset, int origin=Seek_Set) = 0;
+ // Seek simplification
+ int SeekToBegin() {return Seek(0); }
+ int SeekToEnd() {return Seek(0,Seek_End); }
+ int Skip(int numBytes) {return Seek(numBytes,Seek_Cur); }
+
+
+ // Appends other file data from a stream
+ // Return -1 for error, else # of bytes written
+ virtual int CopyFromStream(File *pstream, int byteSize) = 0;
+
+ // Closes the file
+ // After close, file cannot be accessed
+ virtual bool Close() = 0;
+
+
+ // ***** Inlines for convenient primitive type serialization
+
+ // Read/Write helpers
+private:
+ UInt64 PRead64() { UInt64 v = 0; Read((UByte*)&v, 8); return v; }
+ UInt32 PRead32() { UInt32 v = 0; Read((UByte*)&v, 4); return v; }
+ UInt16 PRead16() { UInt16 v = 0; Read((UByte*)&v, 2); return v; }
+ UByte PRead8() { UByte v = 0; Read((UByte*)&v, 1); return v; }
+ void PWrite64(UInt64 v) { Write((UByte*)&v, 8); }
+ void PWrite32(UInt32 v) { Write((UByte*)&v, 4); }
+ void PWrite16(UInt16 v) { Write((UByte*)&v, 2); }
+ void PWrite8(UByte v) { Write((UByte*)&v, 1); }
+
+public:
+
+ // Writing primitive types - Little Endian
+ inline void WriteUByte(UByte v) { PWrite8((UByte)Alg::ByteUtil::SystemToLE(v)); }
+ inline void WriteSByte(SByte v) { PWrite8((UByte)Alg::ByteUtil::SystemToLE(v)); }
+ inline void WriteUInt8(UByte v) { PWrite8((UByte)Alg::ByteUtil::SystemToLE(v)); }
+ inline void WriteSInt8(SByte v) { PWrite8((UByte)Alg::ByteUtil::SystemToLE(v)); }
+ inline void WriteUInt16(UInt16 v) { PWrite16((UInt16)Alg::ByteUtil::SystemToLE(v)); }
+ inline void WriteSInt16(SInt16 v) { PWrite16((UInt16)Alg::ByteUtil::SystemToLE(v)); }
+ inline void WriteUInt32(UInt32 v) { PWrite32((UInt32)Alg::ByteUtil::SystemToLE(v)); }
+ inline void WriteSInt32(SInt32 v) { PWrite32((UInt32)Alg::ByteUtil::SystemToLE(v)); }
+ inline void WriteUInt64(UInt64 v) { PWrite64((UInt64)Alg::ByteUtil::SystemToLE(v)); }
+ inline void WriteSInt64(SInt64 v) { PWrite64((UInt64)Alg::ByteUtil::SystemToLE(v)); }
+ inline void WriteFloat(float v) { v = Alg::ByteUtil::SystemToLE(v); Write((UByte*)&v, 4); }
+ inline void WriteDouble(double v) { v = Alg::ByteUtil::SystemToLE(v); Write((UByte*)&v, 8); }
+ // Writing primitive types - Big Endian
+ inline void WriteUByteBE(UByte v) { PWrite8((UByte)Alg::ByteUtil::SystemToBE(v)); }
+ inline void WriteSByteBE(SByte v) { PWrite8((UByte)Alg::ByteUtil::SystemToBE(v)); }
+ inline void WriteUInt8BE(UInt16 v) { PWrite8((UByte)Alg::ByteUtil::SystemToBE(v)); }
+ inline void WriteSInt8BE(SInt16 v) { PWrite8((UByte)Alg::ByteUtil::SystemToBE(v)); }
+ inline void WriteUInt16BE(UInt16 v) { PWrite16((UInt16)Alg::ByteUtil::SystemToBE(v)); }
+ inline void WriteSInt16BE(UInt16 v) { PWrite16((UInt16)Alg::ByteUtil::SystemToBE(v)); }
+ inline void WriteUInt32BE(UInt32 v) { PWrite32((UInt32)Alg::ByteUtil::SystemToBE(v)); }
+ inline void WriteSInt32BE(UInt32 v) { PWrite32((UInt32)Alg::ByteUtil::SystemToBE(v)); }
+ inline void WriteUInt64BE(UInt64 v) { PWrite64((UInt64)Alg::ByteUtil::SystemToBE(v)); }
+ inline void WriteSInt64BE(UInt64 v) { PWrite64((UInt64)Alg::ByteUtil::SystemToBE(v)); }
+ inline void WriteFloatBE(float v) { v = Alg::ByteUtil::SystemToBE(v); Write((UByte*)&v, 4); }
+ inline void WriteDoubleBE(double v) { v = Alg::ByteUtil::SystemToBE(v); Write((UByte*)&v, 8); }
+
+ // Reading primitive types - Little Endian
+ inline UByte ReadUByte() { return (UByte)Alg::ByteUtil::LEToSystem(PRead8()); }
+ inline SByte ReadSByte() { return (SByte)Alg::ByteUtil::LEToSystem(PRead8()); }
+ inline UByte ReadUInt8() { return (UByte)Alg::ByteUtil::LEToSystem(PRead8()); }
+ inline SByte ReadSInt8() { return (SByte)Alg::ByteUtil::LEToSystem(PRead8()); }
+ inline UInt16 ReadUInt16() { return (UInt16)Alg::ByteUtil::LEToSystem(PRead16()); }
+ inline SInt16 ReadSInt16() { return (SInt16)Alg::ByteUtil::LEToSystem(PRead16()); }
+ inline UInt32 ReadUInt32() { return (UInt32)Alg::ByteUtil::LEToSystem(PRead32()); }
+ inline SInt32 ReadSInt32() { return (SInt32)Alg::ByteUtil::LEToSystem(PRead32()); }
+ inline UInt64 ReadUInt64() { return (UInt64)Alg::ByteUtil::LEToSystem(PRead64()); }
+ inline SInt64 ReadSInt64() { return (SInt64)Alg::ByteUtil::LEToSystem(PRead64()); }
+ inline float ReadFloat() { float v = 0.0f; Read((UByte*)&v, 4); return Alg::ByteUtil::LEToSystem(v); }
+ inline double ReadDouble() { double v = 0.0; Read((UByte*)&v, 8); return Alg::ByteUtil::LEToSystem(v); }
+ // Reading primitive types - Big Endian
+ inline UByte ReadUByteBE() { return (UByte)Alg::ByteUtil::BEToSystem(PRead8()); }
+ inline SByte ReadSByteBE() { return (SByte)Alg::ByteUtil::BEToSystem(PRead8()); }
+ inline UByte ReadUInt8BE() { return (UByte)Alg::ByteUtil::BEToSystem(PRead8()); }
+ inline SByte ReadSInt8BE() { return (SByte)Alg::ByteUtil::BEToSystem(PRead8()); }
+ inline UInt16 ReadUInt16BE() { return (UInt16)Alg::ByteUtil::BEToSystem(PRead16()); }
+ inline SInt16 ReadSInt16BE() { return (SInt16)Alg::ByteUtil::BEToSystem(PRead16()); }
+ inline UInt32 ReadUInt32BE() { return (UInt32)Alg::ByteUtil::BEToSystem(PRead32()); }
+ inline SInt32 ReadSInt32BE() { return (SInt32)Alg::ByteUtil::BEToSystem(PRead32()); }
+ inline UInt64 ReadUInt64BE() { return (UInt64)Alg::ByteUtil::BEToSystem(PRead64()); }
+ inline SInt64 ReadSInt64BE() { return (SInt64)Alg::ByteUtil::BEToSystem(PRead64()); }
+ inline float ReadFloatBE() { float v = 0.0f; Read((UByte*)&v, 4); return Alg::ByteUtil::BEToSystem(v); }
+ inline double ReadDoubleBE() { double v = 0.0; Read((UByte*)&v, 8); return Alg::ByteUtil::BEToSystem(v); }
+};
+
+
+// *** Delegated File
+
+class DelegatedFile : public File
+{
+protected:
+ // Delegating file pointer
+ Ptr<File> pFile;
+
+ // Hidden default constructor
+ DelegatedFile() : pFile(0) { }
+ DelegatedFile(const DelegatedFile &source) : File() { OVR_UNUSED(source); }
+public:
+ // Constructors
+ DelegatedFile(File *pfile) : pFile(pfile) { }
+
+ // ** Location Information
+ virtual const char* GetFilePath() { return pFile->GetFilePath(); }
+
+ // ** File Information
+ virtual bool IsValid() { return pFile && pFile->IsValid(); }
+ virtual bool IsWritable() { return pFile->IsWritable(); }
+// virtual bool IsRecoverable() { return pFile->IsRecoverable(); }
+
+ virtual int Tell() { return pFile->Tell(); }
+ virtual SInt64 LTell() { return pFile->LTell(); }
+
+ virtual int GetLength() { return pFile->GetLength(); }
+ virtual SInt64 LGetLength() { return pFile->LGetLength(); }
+
+ //virtual bool Stat(FileStats *pfs) { return pFile->Stat(pfs); }
+
+ virtual int GetErrorCode() { return pFile->GetErrorCode(); }
+
+ // ** Stream implementation & I/O
+ virtual int Write(const UByte *pbuffer, int numBytes) { return pFile->Write(pbuffer,numBytes); }
+ virtual int Read(UByte *pbuffer, int numBytes) { return pFile->Read(pbuffer,numBytes); }
+
+ virtual int SkipBytes(int numBytes) { return pFile->SkipBytes(numBytes); }
+
+ virtual int BytesAvailable() { return pFile->BytesAvailable(); }
+
+ virtual bool Flush() { return pFile->Flush(); }
+
+ // Seeking
+ virtual int Seek(int offset, int origin=Seek_Set) { return pFile->Seek(offset,origin); }
+ virtual SInt64 LSeek(SInt64 offset, int origin=Seek_Set) { return pFile->LSeek(offset,origin); }
+
+ virtual int CopyFromStream(File *pstream, int byteSize) { return pFile->CopyFromStream(pstream,byteSize); }
+
+ // Closing the file
+ virtual bool Close() { return pFile->Close(); }
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** Buffered File
+
+// This file class adds buffering to an existing file
+// Buffered file never fails by itself; if there's not
+// enough memory for buffer, no buffer's used
+
+class BufferedFile : public DelegatedFile
+{
+protected:
+ enum BufferModeType
+ {
+ NoBuffer,
+ ReadBuffer,
+ WriteBuffer
+ };
+
+ // Buffer & the mode it's in
+ UByte* pBuffer;
+ BufferModeType BufferMode;
+ // Position in buffer
+ unsigned Pos;
+ // Data in buffer if reading
+ unsigned DataSize;
+ // Underlying file position
+ UInt64 FilePos;
+
+ // Initializes buffering to a certain mode
+ bool SetBufferMode(BufferModeType mode);
+ // Flushes buffer
+ // WriteBuffer - write data to disk, ReadBuffer - reset buffer & fix file position
+ void FlushBuffer();
+ // Loads data into ReadBuffer
+ // WARNING: Right now LoadBuffer() assumes the buffer's empty
+ void LoadBuffer();
+
+ // Hidden constructor
+ BufferedFile();
+ inline BufferedFile(const BufferedFile &source) : DelegatedFile() { OVR_UNUSED(source); }
+public:
+
+ // Constructor
+ // - takes another file as source
+ BufferedFile(File *pfile);
+ ~BufferedFile();
+
+
+ // ** Overridden functions
+
+ // We override all the functions that can possibly
+ // require buffer mode switch, flush, or extra calculations
+ virtual int Tell();
+ virtual SInt64 LTell();
+
+ virtual int GetLength();
+ virtual SInt64 LGetLength();
+
+// virtual bool Stat(GFileStats *pfs);
+
+ virtual int Write(const UByte *pbufer, int numBytes);
+ virtual int Read(UByte *pbufer, int numBytes);
+
+ virtual int SkipBytes(int numBytes);
+
+ virtual int BytesAvailable();
+
+ virtual bool Flush();
+
+ virtual int Seek(int offset, int origin=Seek_Set);
+ virtual SInt64 LSeek(SInt64 offset, int origin=Seek_Set);
+
+ virtual int CopyFromStream(File *pstream, int byteSize);
+
+ virtual bool Close();
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** Memory File
+
+class MemoryFile : public File
+{
+public:
+
+ const char* GetFilePath() { return FilePath.ToCStr(); }
+
+ bool IsValid() { return Valid; }
+ bool IsWritable() { return false; }
+
+ bool Flush() { return true; }
+ int GetErrorCode() { return 0; }
+
+ int Tell() { return FileIndex; }
+ SInt64 LTell() { return (SInt64) FileIndex; }
+
+ int GetLength() { return FileSize; }
+ SInt64 LGetLength() { return (SInt64) FileSize; }
+
+ bool Close()
+ {
+ Valid = false;
+ return false;
+ }
+
+ int CopyFromStream(File *pstream, int byteSize)
+ { OVR_UNUSED2(pstream, byteSize);
+ return 0;
+ }
+
+ int Write(const UByte *pbuffer, int numBytes)
+ { OVR_UNUSED2(pbuffer, numBytes);
+ return 0;
+ }
+
+ int Read(UByte *pbufer, int numBytes)
+ {
+ if (FileIndex + numBytes > FileSize)
+ {
+ numBytes = FileSize - FileIndex;
+ }
+
+ if (numBytes > 0)
+ {
+ ::memcpy (pbufer, &FileData [FileIndex], numBytes);
+
+ FileIndex += numBytes;
+ }
+
+ return numBytes;
+ }
+
+ int SkipBytes(int numBytes)
+ {
+ if (FileIndex + numBytes > FileSize)
+ {
+ numBytes = FileSize - FileIndex;
+ }
+
+ FileIndex += numBytes;
+
+ return numBytes;
+ }
+
+ int BytesAvailable()
+ {
+ return (FileSize - FileIndex);
+ }
+
+ int Seek(int offset, int origin = Seek_Set)
+ {
+ switch (origin)
+ {
+ case Seek_Set : FileIndex = offset; break;
+ case Seek_Cur : FileIndex += offset; break;
+ case Seek_End : FileIndex = FileSize - offset; break;
+ }
+
+ return FileIndex;
+ }
+
+ SInt64 LSeek(SInt64 offset, int origin = Seek_Set)
+ {
+ return (SInt64) Seek((int) offset, origin);
+ }
+
+public:
+
+ MemoryFile (const String& fileName, const UByte *pBuffer, int buffSize)
+ : FilePath(fileName)
+ {
+ FileData = pBuffer;
+ FileSize = buffSize;
+ FileIndex = 0;
+ Valid = (!fileName.IsEmpty() && pBuffer && buffSize > 0) ? true : false;
+ }
+
+ // pfileName should be encoded as UTF-8 to support international file names.
+ MemoryFile (const char* pfileName, const UByte *pBuffer, int buffSize)
+ : FilePath(pfileName)
+ {
+ FileData = pBuffer;
+ FileSize = buffSize;
+ FileIndex = 0;
+ Valid = (pfileName && pBuffer && buffSize > 0) ? true : false;
+ }
+private:
+
+ String FilePath;
+ const UByte *FileData;
+ int FileSize;
+ int FileIndex;
+ bool Valid;
+};
+
+
+// ***** Global path helpers
+
+// Find trailing short filename in a path.
+const char* OVR_CDECL GetShortFilename(const char* purl);
+
+} // OVR
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_FileFILE.cpp b/LibOVR/Src/Kernel/OVR_FileFILE.cpp
new file mode 100644
index 0000000..4ee32b6
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_FileFILE.cpp
@@ -0,0 +1,583 @@
+/**************************************************************************
+
+Filename : OVR_FileFILE.cpp
+Content : File wrapper class implementation (Win32)
+
+Created : April 5, 1999
+Authors : Michael Antonov
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+**************************************************************************/
+
+#define GFILE_CXX
+
+#include "OVR_Types.h"
+#include "OVR_Log.h"
+
+// Standard C library (Captain Obvious guarantees!)
+#include <stdio.h>
+#ifndef OVR_OS_WINCE
+#include <sys/stat.h>
+#endif
+
+#include "OVR_SysFile.h"
+
+#ifndef OVR_OS_WINCE
+#include <errno.h>
+#endif
+
+namespace OVR {
+
+// ***** File interface
+
+// ***** FILEFile - C streams file
+
+static int SFerror ()
+{
+ if (errno == ENOENT)
+ return FileConstants::Error_FileNotFound;
+ else if (errno == EACCES || errno == EPERM)
+ return FileConstants::Error_Access;
+ else if (errno == ENOSPC)
+ return FileConstants::Error_DiskFull;
+ else
+ return FileConstants::Error_IOError;
+};
+
+#ifdef OVR_OS_WIN32
+#include "windows.h"
+// A simple helper class to disable/enable system error mode, if necessary
+// Disabling happens conditionally only if a drive name is involved
+class SysErrorModeDisabler
+{
+ BOOL Disabled;
+ UINT OldMode;
+public:
+ SysErrorModeDisabler(const char* pfileName)
+ {
+ if (pfileName && (pfileName[0]!=0) && pfileName[1]==':')
+ {
+ Disabled = 1;
+ OldMode = ::SetErrorMode(SEM_FAILCRITICALERRORS);
+ }
+ else
+ Disabled = 0;
+ }
+
+ ~SysErrorModeDisabler()
+ {
+ if (Disabled) ::SetErrorMode(OldMode);
+ }
+};
+#else
+class SysErrorModeDisabler
+{
+public:
+ SysErrorModeDisabler(const char* pfileName) { }
+};
+#endif // OVR_OS_WIN32
+
+
+// This macro enables verification of I/O results after seeks against a pre-loaded
+// full file buffer copy. This is generally not necessary, but can been used to debug
+// memory corruptions; we've seen this fail due to EAX2/DirectSound corrupting memory
+// under FMOD with XP64 (32-bit) and Realtek HA Audio driver.
+//#define GFILE_VERIFY_SEEK_ERRORS
+
+
+// This is the simplest possible file implementation, it wraps around the descriptor
+// This file is delegated to by SysFile.
+
+class FILEFile : public File
+{
+protected:
+
+ // Allocated filename
+ String FileName;
+
+ // File handle & open mode
+ bool Opened;
+ FILE* fs;
+ int OpenFlags;
+ // Error code for last request
+ int ErrorCode;
+
+ int LastOp;
+
+#ifdef OVR_FILE_VERIFY_SEEK_ERRORS
+ UByte* pFileTestBuffer;
+ unsigned FileTestLength;
+ unsigned TestPos; // File pointer position during tests.
+#endif
+
+public:
+
+ FILEFile()
+ {
+ Opened = 0; FileName = "";
+
+#ifdef OVR_FILE_VERIFY_SEEK_ERRORS
+ pFileTestBuffer =0;
+ FileTestLength =0;
+ TestPos =0;
+#endif
+ }
+ // Initialize file by opening it
+ FILEFile(const String& fileName, int flags, int Mode);
+ // The 'pfileName' should be encoded as UTF-8 to support international file names.
+ FILEFile(const char* pfileName, int flags, int Mode);
+
+ ~FILEFile()
+ {
+ if (Opened)
+ Close();
+ }
+
+ virtual const char* GetFilePath();
+
+ // ** File Information
+ virtual bool IsValid();
+ virtual bool IsWritable();
+
+ // Return position / file size
+ virtual int Tell();
+ virtual SInt64 LTell();
+ virtual int GetLength();
+ virtual SInt64 LGetLength();
+
+// virtual bool Stat(FileStats *pfs);
+ virtual int GetErrorCode();
+
+ // ** Stream implementation & I/O
+ virtual int Write(const UByte *pbuffer, int numBytes);
+ virtual int Read(UByte *pbuffer, int numBytes);
+ virtual int SkipBytes(int numBytes);
+ virtual int BytesAvailable();
+ virtual bool Flush();
+ virtual int Seek(int offset, int origin);
+ virtual SInt64 LSeek(SInt64 offset, int origin);
+
+ virtual int CopyFromStream(File *pStream, int byteSize);
+ virtual bool Close();
+private:
+ void init();
+};
+
+
+// Initialize file by opening it
+FILEFile::FILEFile(const String& fileName, int flags, int mode)
+ : FileName(fileName), OpenFlags(flags)
+{
+ OVR_UNUSED(mode);
+ init();
+}
+
+// The 'pfileName' should be encoded as UTF-8 to support international file names.
+FILEFile::FILEFile(const char* pfileName, int flags, int mode)
+ : FileName(pfileName), OpenFlags(flags)
+{
+ OVR_UNUSED(mode);
+ init();
+}
+
+void FILEFile::init()
+{
+ // Open mode for file's open
+ const char *omode = "rb";
+
+ if (OpenFlags & Open_Truncate)
+ {
+ if (OpenFlags & Open_Read)
+ omode = "w+b";
+ else
+ omode = "wb";
+ }
+ else if (OpenFlags & Open_Create)
+ {
+ if (OpenFlags & Open_Read)
+ omode = "a+b";
+ else
+ omode = "ab";
+ }
+ else if (OpenFlags & Open_Write)
+ omode = "r+b";
+
+#ifdef OVR_OS_WIN32
+ SysErrorModeDisabler disabler(FileName.ToCStr());
+#endif
+
+#if defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400)
+ wchar_t womode[16];
+ wchar_t *pwFileName = (wchar_t*)OVR_ALLOC((UTF8Util::GetLength(FileName.ToCStr())+1) * sizeof(wchar_t));
+ UTF8Util::DecodeString(pwFileName, FileName.ToCStr());
+ OVR_ASSERT(strlen(omode) < sizeof(womode)/sizeof(womode[0]));
+ UTF8Util::DecodeString(womode, omode);
+ _wfopen_s(&fs, pwFileName, womode);
+ OVR_FREE(pwFileName);
+#else
+ fs = fopen(FileName.ToCStr(), omode);
+#endif
+ if (fs)
+ rewind (fs);
+ Opened = (fs != NULL);
+ // Set error code
+ if (!Opened)
+ ErrorCode = SFerror();
+ else
+ {
+ // If we are testing file seek correctness, pre-load the entire file so
+ // that we can do comparison tests later.
+#ifdef OVR_FILE_VERIFY_SEEK_ERRORS
+ TestPos = 0;
+ fseek(fs, 0, SEEK_END);
+ FileTestLength = ftell(fs);
+ fseek(fs, 0, SEEK_SET);
+ pFileTestBuffer = (UByte*)OVR_ALLOC(FileTestLength);
+ if (pFileTestBuffer)
+ {
+ OVR_ASSERT(FileTestLength == (unsigned)Read(pFileTestBuffer, FileTestLength));
+ Seek(0, Seek_Set);
+ }
+#endif
+
+ ErrorCode = 0;
+ }
+ LastOp = 0;
+}
+
+
+const char* FILEFile::GetFilePath()
+{
+ return FileName.ToCStr();
+}
+
+
+// ** File Information
+bool FILEFile::IsValid()
+{
+ return Opened;
+}
+bool FILEFile::IsWritable()
+{
+ return IsValid() && (OpenFlags&Open_Write);
+}
+/*
+bool FILEFile::IsRecoverable()
+{
+ return IsValid() && ((OpenFlags&OVR_FO_SAFETRUNC) == OVR_FO_SAFETRUNC);
+}
+*/
+
+// Return position / file size
+int FILEFile::Tell()
+{
+ int pos = (int)ftell (fs);
+ if (pos < 0)
+ ErrorCode = SFerror();
+ return pos;
+}
+
+SInt64 FILEFile::LTell()
+{
+ SInt64 pos = ftell(fs);
+ if (pos < 0)
+ ErrorCode = SFerror();
+ return pos;
+}
+
+int FILEFile::GetLength()
+{
+ int pos = Tell();
+ if (pos >= 0)
+ {
+ Seek (0, Seek_End);
+ int size = Tell();
+ Seek (pos, Seek_Set);
+ return size;
+ }
+ return -1;
+}
+SInt64 FILEFile::LGetLength()
+{
+ SInt64 pos = LTell();
+ if (pos >= 0)
+ {
+ LSeek (0, Seek_End);
+ SInt64 size = LTell();
+ LSeek (pos, Seek_Set);
+ return size;
+ }
+ return -1;
+}
+
+int FILEFile::GetErrorCode()
+{
+ return ErrorCode;
+}
+
+// ** Stream implementation & I/O
+int FILEFile::Write(const UByte *pbuffer, int numBytes)
+{
+ if (LastOp && LastOp != Open_Write)
+ fflush(fs);
+ LastOp = Open_Write;
+ int written = (int) fwrite(pbuffer, 1, numBytes, fs);
+ if (written < numBytes)
+ ErrorCode = SFerror();
+
+#ifdef OVR_FILE_VERIFY_SEEK_ERRORS
+ if (written > 0)
+ TestPos += written;
+#endif
+
+ return written;
+}
+
+int FILEFile::Read(UByte *pbuffer, int numBytes)
+{
+ if (LastOp && LastOp != Open_Read)
+ fflush(fs);
+ LastOp = Open_Read;
+ int read = (int) fread(pbuffer, 1, numBytes, fs);
+ if (read < numBytes)
+ ErrorCode = SFerror();
+
+#ifdef OVR_FILE_VERIFY_SEEK_ERRORS
+ if (read > 0)
+ {
+ // Read-in data must match our pre-loaded buffer data!
+ UByte* pcompareBuffer = pFileTestBuffer + TestPos;
+ for (int i=0; i< read; i++)
+ {
+ OVR_ASSERT(pcompareBuffer[i] == pbuffer[i]);
+ }
+
+ //OVR_ASSERT(!memcmp(pFileTestBuffer + TestPos, pbuffer, read));
+ TestPos += read;
+ OVR_ASSERT(ftell(fs) == (int)TestPos);
+ }
+#endif
+
+ return read;
+}
+
+// Seeks ahead to skip bytes
+int FILEFile::SkipBytes(int numBytes)
+{
+ SInt64 pos = LTell();
+ SInt64 newPos = LSeek(numBytes, Seek_Cur);
+
+ // Return -1 for major error
+ if ((pos==-1) || (newPos==-1))
+ {
+ return -1;
+ }
+ //ErrorCode = ((NewPos-Pos)<numBytes) ? errno : 0;
+
+ return int (newPos-(int)pos);
+}
+
+// Return # of bytes till EOF
+int FILEFile::BytesAvailable()
+{
+ SInt64 pos = LTell();
+ SInt64 endPos = LGetLength();
+
+ // Return -1 for major error
+ if ((pos==-1) || (endPos==-1))
+ {
+ ErrorCode = SFerror();
+ return 0;
+ }
+ else
+ ErrorCode = 0;
+
+ return int (endPos-(int)pos);
+}
+
+// Flush file contents
+bool FILEFile::Flush()
+{
+ return !fflush(fs);
+}
+
+int FILEFile::Seek(int offset, int origin)
+{
+ int newOrigin = 0;
+ switch(origin)
+ {
+ case Seek_Set: newOrigin = SEEK_SET; break;
+ case Seek_Cur: newOrigin = SEEK_CUR; break;
+ case Seek_End: newOrigin = SEEK_END; break;
+ }
+
+ if (newOrigin == SEEK_SET && offset == Tell())
+ return Tell();
+
+ if (fseek (fs, offset, newOrigin))
+ {
+#ifdef OVR_FILE_VERIFY_SEEK_ERRORS
+ OVR_ASSERT(0);
+#endif
+ return -1;
+ }
+
+#ifdef OVR_FILE_VERIFY_SEEK_ERRORS
+ // Track file position after seeks for read verification later.
+ switch(origin)
+ {
+ case Seek_Set: TestPos = offset; break;
+ case Seek_Cur: TestPos += offset; break;
+ case Seek_End: TestPos = FileTestLength + offset; break;
+ }
+ OVR_ASSERT((int)TestPos == Tell());
+#endif
+
+ return (int)Tell();
+}
+
+SInt64 FILEFile::LSeek(SInt64 offset, int origin)
+{
+ return Seek((int)offset,origin);
+}
+
+int FILEFile::CopyFromStream(File *pstream, int byteSize)
+{
+ UByte buff[0x4000];
+ int count = 0;
+ int szRequest, szRead, szWritten;
+
+ while (byteSize)
+ {
+ szRequest = (byteSize > int(sizeof(buff))) ? int(sizeof(buff)) : byteSize;
+
+ szRead = pstream->Read(buff, szRequest);
+ szWritten = 0;
+ if (szRead > 0)
+ szWritten = Write(buff, szRead);
+
+ count += szWritten;
+ byteSize -= szWritten;
+ if (szWritten < szRequest)
+ break;
+ }
+ return count;
+}
+
+
+bool FILEFile::Close()
+{
+#ifdef OVR_FILE_VERIFY_SEEK_ERRORS
+ if (pFileTestBuffer)
+ {
+ OVR_FREE(pFileTestBuffer);
+ pFileTestBuffer = 0;
+ FileTestLength = 0;
+ }
+#endif
+
+ bool closeRet = !fclose(fs);
+
+ if (!closeRet)
+ {
+ ErrorCode = SFerror();
+ return 0;
+ }
+ else
+ {
+ Opened = 0;
+ fs = 0;
+ ErrorCode = 0;
+ }
+
+ // Handle safe truncate
+ /*
+ if ((OpenFlags & OVR_FO_SAFETRUNC) == OVR_FO_SAFETRUNC)
+ {
+ // Delete original file (if it existed)
+ DWORD oldAttributes = FileUtilWin32::GetFileAttributes(FileName);
+ if (oldAttributes!=0xFFFFFFFF)
+ if (!FileUtilWin32::DeleteFile(FileName))
+ {
+ // Try to remove the readonly attribute
+ FileUtilWin32::SetFileAttributes(FileName, oldAttributes & (~FILE_ATTRIBUTE_READONLY) );
+ // And delete the file again
+ if (!FileUtilWin32::DeleteFile(FileName))
+ return 0;
+ }
+
+ // Rename temp file to real filename
+ if (!FileUtilWin32::MoveFile(TempName, FileName))
+ {
+ //ErrorCode = errno;
+ return 0;
+ }
+ }
+ */
+ return 1;
+}
+
+/*
+bool FILEFile::CloseCancel()
+{
+ bool closeRet = (bool)::CloseHandle(fd);
+
+ if (!closeRet)
+ {
+ //ErrorCode = errno;
+ return 0;
+ }
+ else
+ {
+ Opened = 0;
+ fd = INVALID_HANDLE_VALUE;
+ ErrorCode = 0;
+ }
+
+ // Handle safe truncate (delete tmp file, leave original unchanged)
+ if ((OpenFlags&OVR_FO_SAFETRUNC) == OVR_FO_SAFETRUNC)
+ if (!FileUtilWin32::DeleteFile(TempName))
+ {
+ //ErrorCode = errno;
+ return 0;
+ }
+ return 1;
+}
+*/
+
+File *FileFILEOpen(const String& path, int flags, int mode)
+{
+ return new FILEFile(path, flags, mode);
+}
+
+// Helper function: obtain file information time.
+bool SysFile::GetFileStat(FileStat* pfileStat, const String& path)
+{
+#if defined(OVR_OS_WIN32)
+ // 64-bit implementation on Windows.
+ struct __stat64 fileStat;
+ // Stat returns 0 for success.
+ wchar_t *pwpath = (wchar_t*)OVR_ALLOC((UTF8Util::GetLength(path.ToCStr())+1)*sizeof(wchar_t));
+ UTF8Util::DecodeString(pwpath, path.ToCStr());
+
+ int ret = _wstat64(pwpath, &fileStat);
+ OVR_FREE(pwpath);
+ if (ret) return false;
+#else
+ struct stat fileStat;
+ // Stat returns 0 for success.
+ if (stat(path, &fileStat) != 0)
+ return false;
+#endif
+ pfileStat->AccessTime = fileStat.st_atime;
+ pfileStat->ModifyTime = fileStat.st_mtime;
+ pfileStat->FileSize = fileStat.st_size;
+ return true;
+}
+
+} // Scaleform
diff --git a/LibOVR/Src/Kernel/OVR_Hash.h b/LibOVR/Src/Kernel/OVR_Hash.h
new file mode 100644
index 0000000..6667ac2
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_Hash.h
@@ -0,0 +1,1291 @@
+/************************************************************************************
+
+PublicHeader: None
+Filename : OVR_Hash.h
+Content : Template hash-table/set implementation
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#ifndef OVR_Hash_h
+#define OVR_Hash_h
+
+#include "OVR_ContainerAllocator.h"
+#include "OVR_Alg.h"
+
+// 'new' operator is redefined/used in this file.
+#undef new
+
+namespace OVR {
+
+//-----------------------------------------------------------------------------------
+// ***** Hash Table Implementation
+
+// HastSet and Hash.
+//
+// Hash table, linear probing, internal chaining. One interesting/nice thing
+// about this implementation is that the table itself is a flat chunk of memory
+// containing no pointers, only relative indices. If the key and value types
+// of the Hash contain no pointers, then the Hash can be serialized using raw IO.
+//
+// Never shrinks, unless you explicitly Clear() it. Expands on
+// demand, though. For best results, if you know roughly how big your
+// table will be, default it to that size when you create it.
+//
+// Key usability feature:
+//
+// 1. Allows node hash values to either be cached or not.
+//
+// 2. Allows for alternative keys with methods such as GetAlt(). Handy
+// if you need to search nodes by their components; no need to create
+// temporary nodes.
+//
+
+
+// *** Hash functors:
+//
+// IdentityHash - use when the key is already a good hash
+// HFixedSizeHash - general hash based on object's in-memory representation.
+
+
+// Hash is just the input value; can use this for integer-indexed hash tables.
+template<class C>
+class IdentityHash
+{
+public:
+ UPInt operator()(const C& data) const
+ { return (UPInt) data; }
+};
+
+// Computes a hash of an object's representation.
+template<class C>
+class FixedSizeHash
+{
+public:
+ // Alternative: "sdbm" hash function, suggested at same web page
+ // above, http::/www.cs.yorku.ca/~oz/hash.html
+ // This is somewhat slower then Bernstein, but it works way better than the above
+ // hash function for hashing large numbers of 32-bit ints.
+ static OVR_FORCE_INLINE UPInt SDBM_Hash(const void* data_in, UPInt size, UPInt seed = 5381)
+ {
+ const UByte* data = (const UByte*) data_in;
+ UPInt h = seed;
+ while (size > 0)
+ {
+ size--;
+ h = (h << 16) + (h << 6) - h + (UPInt)data[size];
+ }
+ return h;
+ }
+
+ UPInt operator()(const C& data) const
+ {
+ unsigned char* p = (unsigned char*) &data;
+ int size = sizeof(C);
+
+ return SDBM_Hash(p, size);
+ }
+};
+
+
+
+// *** HashsetEntry Entry types.
+
+// Compact hash table Entry type that re-computes hash keys during hash traversal.
+// Good to use if the hash function is cheap or the hash value is already cached in C.
+template<class C, class HashF>
+class HashsetEntry
+{
+public:
+ // Internal chaining for collisions.
+ SPInt NextInChain;
+ C Value;
+
+ HashsetEntry()
+ : NextInChain(-2) { }
+ HashsetEntry(const HashsetEntry& e)
+ : NextInChain(e.NextInChain), Value(e.Value) { }
+ HashsetEntry(const C& key, SPInt next)
+ : NextInChain(next), Value(key) { }
+
+ bool IsEmpty() const { return NextInChain == -2; }
+ bool IsEndOfChain() const { return NextInChain == -1; }
+
+ // Cached hash value access - can be optimized bu storing hash locally.
+ // Mask value only needs to be used if SetCachedHash is not implemented.
+ UPInt GetCachedHash(UPInt maskValue) const { return HashF()(Value) & maskValue; }
+ void SetCachedHash(UPInt) {}
+
+ void Clear()
+ {
+ Value.~C(); // placement delete
+ NextInChain = -2;
+ }
+ // Free is only used from dtor of hash; Clear is used during regular operations:
+ // assignment, hash reallocations, value reassignments, so on.
+ void Free() { Clear(); }
+};
+
+// Hash table Entry type that caches the Entry hash value for nodes, so that it
+// does not need to be re-computed during access.
+template<class C, class HashF>
+class HashsetCachedEntry
+{
+public:
+ // Internal chaining for collisions.
+ SPInt NextInChain;
+ UPInt HashValue;
+ C Value;
+
+ HashsetCachedEntry()
+ : NextInChain(-2) { }
+ HashsetCachedEntry(const HashsetCachedEntry& e)
+ : NextInChain(e.NextInChain), HashValue(e.HashValue), Value(e.Value) { }
+ HashsetCachedEntry(const C& key, SPInt next)
+ : NextInChain(next), Value(key) { }
+
+ bool IsEmpty() const { return NextInChain == -2; }
+ bool IsEndOfChain() const { return NextInChain == -1; }
+
+ // Cached hash value access - can be optimized bu storing hash locally.
+ // Mask value only needs to be used if SetCachedHash is not implemented.
+ UPInt GetCachedHash(UPInt maskValue) const { OVR_UNUSED(maskValue); return HashValue; }
+ void SetCachedHash(UPInt hashValue) { HashValue = hashValue; }
+
+ void Clear()
+ {
+ Value.~C();
+ NextInChain = -2;
+ }
+ // Free is only used from dtor of hash; Clear is used during regular operations:
+ // assignment, hash reallocations, value reassignments, so on.
+ void Free() { Clear(); }
+};
+
+
+//-----------------------------------------------------------------------------------
+// *** HashSet implementation - relies on either cached or regular entries.
+//
+// Use: Entry = HashsetCachedEntry<C, HashF> if hashes are expensive to
+// compute and thus need caching in entries.
+// Entry = HashsetEntry<C, HashF> if hashes are already externally cached.
+//
+template<class C, class HashF = FixedSizeHash<C>,
+ class AltHashF = HashF,
+ class Allocator = ContainerAllocator<C>,
+ class Entry = HashsetCachedEntry<C, HashF> >
+class HashSetBase
+{
+ enum { HashMinSize = 8 };
+
+public:
+ OVR_MEMORY_REDEFINE_NEW(HashSetBase)
+
+ typedef HashSetBase<C, HashF, AltHashF, Allocator, Entry> SelfType;
+
+ HashSetBase() : pTable(NULL) { }
+ HashSetBase(int sizeHint) : pTable(NULL) { SetCapacity(this, sizeHint); }
+ HashSetBase(const SelfType& src) : pTable(NULL) { Assign(this, src); }
+
+ ~HashSetBase()
+ {
+ if (pTable)
+ {
+ // Delete the entries.
+ for (UPInt i = 0, n = pTable->SizeMask; i <= n; i++)
+ {
+ Entry* e = &E(i);
+ if (!e->IsEmpty())
+ e->Free();
+ }
+
+ Allocator::Free(pTable);
+ pTable = NULL;
+ }
+ }
+
+
+ void Assign(const SelfType& src)
+ {
+ Clear();
+ if (src.IsEmpty() == false)
+ {
+ SetCapacity(src.GetSize());
+
+ for (ConstIterator it = src.Begin(); it != src.End(); ++it)
+ {
+ Add(*it);
+ }
+ }
+ }
+
+
+ // Remove all entries from the HashSet table.
+ void Clear()
+ {
+ if (pTable)
+ {
+ // Delete the entries.
+ for (UPInt i = 0, n = pTable->SizeMask; i <= n; i++)
+ {
+ Entry* e = &E(i);
+ if (!e->IsEmpty())
+ e->Clear();
+ }
+
+ Allocator::Free(pTable);
+ pTable = NULL;
+ }
+ }
+
+ // Returns true if the HashSet is empty.
+ bool IsEmpty() const
+ {
+ return pTable == NULL || pTable->EntryCount == 0;
+ }
+
+
+ // Set a new or existing value under the key, to the value.
+ // Pass a different class of 'key' so that assignment reference object
+ // can be passed instead of the actual object.
+ template<class CRef>
+ void Set(const CRef& key)
+ {
+ UPInt hashValue = HashF()(key);
+ SPInt index = (SPInt)-1;
+
+ if (pTable != NULL)
+ index = findIndexCore(key, hashValue & pTable->SizeMask);
+
+ if (index >= 0)
+ {
+ E(index).Value = key;
+ }
+ else
+ {
+ // Entry under key doesn't exist.
+ add(key, hashValue);
+ }
+ }
+
+ template<class CRef>
+ inline void Add(const CRef& key)
+ {
+ UPInt hashValue = HashF()(key);
+ add(key, hashValue);
+ }
+
+ // Remove by alternative key.
+ template<class K>
+ void RemoveAlt(const K& key)
+ {
+ if (pTable == NULL)
+ return;
+
+ UPInt hashValue = AltHashF()(key);
+ SPInt index = hashValue & pTable->SizeMask;
+
+ Entry* e = &E(index);
+
+ // If empty node or occupied by collider, we have nothing to remove.
+ if (e->IsEmpty() || (e->GetCachedHash(pTable->SizeMask) != (UPInt)index))
+ return;
+
+ // Save index
+ SPInt naturalIndex = index;
+ SPInt prevIndex = -1;
+
+ while ((e->GetCachedHash(pTable->SizeMask) != (UPInt)naturalIndex) || !(e->Value == key))
+ {
+ // Keep looking through the chain.
+ prevIndex = index;
+ index = e->NextInChain;
+ if (index == -1)
+ return; // End of chain, item not found
+ e = &E(index);
+ }
+
+ // Found it - our item is at index
+ if (naturalIndex == index)
+ {
+ // If we have a follower, move it to us
+ if (!e->IsEndOfChain())
+ {
+ Entry* enext = &E(e->NextInChain);
+ e->Clear();
+ new (e) Entry(*enext);
+ // Point us to the follower's cell that will be cleared
+ e = enext;
+ }
+ }
+ else
+ {
+ // We are not at natural index, so deal with the prev items next index
+ E(prevIndex).NextInChain = e->NextInChain;
+ }
+
+ // Clear us, of the follower cell that was moved.
+ e->Clear();
+ pTable->EntryCount --;
+ // Should we check the size to condense hash? ...
+ }
+
+ // Remove by main key.
+ template<class CRef>
+ void Remove(const CRef& key)
+ {
+ RemoveAlt(key);
+ }
+
+ // Retrieve the pointer to a value under the given key.
+ // - If there's no value under the key, then return NULL.
+ // - If there is a value, return the pointer.
+ template<class K>
+ C* Get(const K& key)
+ {
+ SPInt index = findIndex(key);
+ if (index >= 0)
+ return &E(index).Value;
+ return 0;
+ }
+
+ template<class K>
+ const C* Get(const K& key) const
+ {
+ SPInt index = findIndex(key);
+ if (index >= 0)
+ return &E(index).Value;
+ return 0;
+ }
+
+ // Alternative key versions of Get. Used by Hash.
+ template<class K>
+ const C* GetAlt(const K& key) const
+ {
+ SPInt index = findIndexAlt(key);
+ if (index >= 0)
+ return &E(index).Value;
+ return 0;
+ }
+
+ template<class K>
+ C* GetAlt(const K& key)
+ {
+ SPInt index = findIndexAlt(key);
+ if (index >= 0)
+ return &E(index).Value;
+ return 0;
+ }
+
+ template<class K>
+ bool GetAlt(const K& key, C* pval) const
+ {
+ SPInt index = findIndexAlt(key);
+ if (index >= 0)
+ {
+ if (pval)
+ *pval = E(index).Value;
+ return true;
+ }
+ return false;
+ }
+
+
+ UPInt GetSize() const
+ {
+ return pTable == NULL ? 0 : (UPInt)pTable->EntryCount;
+ }
+
+
+ // Resize the HashSet table to fit one more Entry. Often this
+ // doesn't involve any action.
+ void CheckExpand()
+ {
+ if (pTable == NULL)
+ {
+ // Initial creation of table. Make a minimum-sized table.
+ setRawCapacity(HashMinSize);
+ }
+ else if (pTable->EntryCount * 5 > (pTable->SizeMask + 1) * 4)
+ {
+ // pTable is more than 5/4 ths full. Expand.
+ setRawCapacity((pTable->SizeMask + 1) * 2);
+ }
+ }
+
+ // Hint the bucket count to >= n.
+ void Resize(UPInt n)
+ {
+ // Not really sure what this means in relation to
+ // STLport's hash_map... they say they "increase the
+ // bucket count to at least n" -- but does that mean
+ // their real capacity after Resize(n) is more like
+ // n*2 (since they do linked-list chaining within
+ // buckets?).
+ SetCapacity(n);
+ }
+
+ // Size the HashSet so that it can comfortably contain the given
+ // number of elements. If the HashSet already contains more
+ // elements than newSize, then this may be a no-op.
+ void SetCapacity(UPInt newSize)
+ {
+ UPInt newRawSize = (newSize * 5) / 4;
+ if (newRawSize <= GetSize())
+ return;
+ setRawCapacity(newRawSize);
+ }
+
+ // Disable inappropriate 'operator ->' warning on MSVC6.
+#ifdef OVR_CC_MSVC
+#if (OVR_CC_MSVC < 1300)
+# pragma warning(disable : 4284)
+#endif
+#endif
+
+ // Iterator API, like STL.
+ struct ConstIterator
+ {
+ const C& operator * () const
+ {
+ OVR_ASSERT(Index >= 0 && Index <= (SPInt)pHash->pTable->SizeMask);
+ return pHash->E(Index).Value;
+ }
+
+ const C* operator -> () const
+ {
+ OVR_ASSERT(Index >= 0 && Index <= (SPInt)pHash->pTable->SizeMask);
+ return &pHash->E(Index).Value;
+ }
+
+ void operator ++ ()
+ {
+ // Find next non-empty Entry.
+ if (Index <= (SPInt)pHash->pTable->SizeMask)
+ {
+ Index++;
+ while ((UPInt)Index <= pHash->pTable->SizeMask &&
+ pHash->E(Index).IsEmpty())
+ {
+ Index++;
+ }
+ }
+ }
+
+ bool operator == (const ConstIterator& it) const
+ {
+ if (IsEnd() && it.IsEnd())
+ {
+ return true;
+ }
+ else
+ {
+ return (pHash == it.pHash) && (Index == it.Index);
+ }
+ }
+
+ bool operator != (const ConstIterator& it) const
+ {
+ return ! (*this == it);
+ }
+
+
+ bool IsEnd() const
+ {
+ return (pHash == NULL) ||
+ (pHash->pTable == NULL) ||
+ (Index > (SPInt)pHash->pTable->SizeMask);
+ }
+
+ ConstIterator()
+ : pHash(NULL), Index(0)
+ { }
+
+ public:
+ // Constructor was intentionally made public to allow create
+ // iterator with arbitrary index.
+ ConstIterator(const SelfType* h, SPInt index)
+ : pHash(h), Index(index)
+ { }
+
+ const SelfType* GetContainer() const
+ {
+ return pHash;
+ }
+ SPInt GetIndex() const
+ {
+ return Index;
+ }
+
+ protected:
+ friend class HashSetBase<C, HashF, AltHashF, Allocator, Entry>;
+
+ const SelfType* pHash;
+ SPInt Index;
+ };
+
+ friend struct ConstIterator;
+
+
+ // Non-const Iterator; Get most of it from ConstIterator.
+ struct Iterator : public ConstIterator
+ {
+ // Allow non-const access to entries.
+ C& operator*() const
+ {
+ OVR_ASSERT(ConstIterator::Index >= 0 && ConstIterator::Index <= (SPInt)ConstIterator::pHash->pTable->SizeMask);
+ return const_cast<SelfType*>(ConstIterator::pHash)->E(ConstIterator::Index).Value;
+ }
+
+ C* operator->() const
+ {
+ return &(operator*());
+ }
+
+ Iterator()
+ : ConstIterator(NULL, 0)
+ { }
+
+ // Removes current element from Hash
+ void Remove()
+ {
+ RemoveAlt(operator*());
+ }
+
+ template <class K>
+ void RemoveAlt(const K& key)
+ {
+ SelfType* phash = const_cast<SelfType*>(ConstIterator::pHash);
+ //Entry* ee = &phash->E(ConstIterator::Index);
+ //const C& key = ee->Value;
+
+ UPInt hashValue = AltHashF()(key);
+ SPInt index = hashValue & phash->pTable->SizeMask;
+
+ Entry* e = &phash->E(index);
+
+ // If empty node or occupied by collider, we have nothing to remove.
+ if (e->IsEmpty() || (e->GetCachedHash(phash->pTable->SizeMask) != (UPInt)index))
+ return;
+
+ // Save index
+ SPInt naturalIndex = index;
+ SPInt prevIndex = -1;
+
+ while ((e->GetCachedHash(phash->pTable->SizeMask) != (UPInt)naturalIndex) || !(e->Value == key))
+ {
+ // Keep looking through the chain.
+ prevIndex = index;
+ index = e->NextInChain;
+ if (index == -1)
+ return; // End of chain, item not found
+ e = &phash->E(index);
+ }
+
+ if (index == (SPInt)ConstIterator::Index)
+ {
+ // Found it - our item is at index
+ if (naturalIndex == index)
+ {
+ // If we have a follower, move it to us
+ if (!e->IsEndOfChain())
+ {
+ Entry* enext = &phash->E(e->NextInChain);
+ e->Clear();
+ new (e) Entry(*enext);
+ // Point us to the follower's cell that will be cleared
+ e = enext;
+ --ConstIterator::Index;
+ }
+ }
+ else
+ {
+ // We are not at natural index, so deal with the prev items next index
+ phash->E(prevIndex).NextInChain = e->NextInChain;
+ }
+
+ // Clear us, of the follower cell that was moved.
+ e->Clear();
+ phash->pTable->EntryCount --;
+ }
+ else
+ OVR_ASSERT(0); //?
+ }
+
+ private:
+ friend class HashSetBase<C, HashF, AltHashF, Allocator, Entry>;
+
+ Iterator(SelfType* h, SPInt i0)
+ : ConstIterator(h, i0)
+ { }
+ };
+
+ friend struct Iterator;
+
+ Iterator Begin()
+ {
+ if (pTable == 0)
+ return Iterator(NULL, 0);
+
+ // Scan till we hit the First valid Entry.
+ UPInt i0 = 0;
+ while (i0 <= pTable->SizeMask && E(i0).IsEmpty())
+ {
+ i0++;
+ }
+ return Iterator(this, i0);
+ }
+ Iterator End() { return Iterator(NULL, 0); }
+
+ ConstIterator Begin() const { return const_cast<SelfType*>(this)->Begin(); }
+ ConstIterator End() const { return const_cast<SelfType*>(this)->End(); }
+
+ template<class K>
+ Iterator Find(const K& key)
+ {
+ SPInt index = findIndex(key);
+ if (index >= 0)
+ return Iterator(this, index);
+ return Iterator(NULL, 0);
+ }
+
+ template<class K>
+ Iterator FindAlt(const K& key)
+ {
+ SPInt index = findIndexAlt(key);
+ if (index >= 0)
+ return Iterator(this, index);
+ return Iterator(NULL, 0);
+ }
+
+ template<class K>
+ ConstIterator Find(const K& key) const { return const_cast<SelfType*>(this)->Find(key); }
+
+ template<class K>
+ ConstIterator FindAlt(const K& key) const { return const_cast<SelfType*>(this)->FindAlt(key); }
+
+private:
+ // Find the index of the matching Entry. If no match, then return -1.
+ template<class K>
+ SPInt findIndex(const K& key) const
+ {
+ if (pTable == NULL)
+ return -1;
+ UPInt hashValue = HashF()(key) & pTable->SizeMask;
+ return findIndexCore(key, hashValue);
+ }
+
+ template<class K>
+ SPInt findIndexAlt(const K& key) const
+ {
+ if (pTable == NULL)
+ return -1;
+ UPInt hashValue = AltHashF()(key) & pTable->SizeMask;
+ return findIndexCore(key, hashValue);
+ }
+
+ // Find the index of the matching Entry. If no match, then return -1.
+ template<class K>
+ SPInt findIndexCore(const K& key, UPInt hashValue) const
+ {
+ // Table must exist.
+ OVR_ASSERT(pTable != 0);
+ // Hash key must be 'and-ed' by the caller.
+ OVR_ASSERT((hashValue & ~pTable->SizeMask) == 0);
+
+ UPInt index = hashValue;
+ const Entry* e = &E(index);
+
+ // If empty or occupied by a collider, not found.
+ if (e->IsEmpty() || (e->GetCachedHash(pTable->SizeMask) != index))
+ return -1;
+
+ while(1)
+ {
+ OVR_ASSERT(e->GetCachedHash(pTable->SizeMask) == hashValue);
+
+ if (e->GetCachedHash(pTable->SizeMask) == hashValue && e->Value == key)
+ {
+ // Found it.
+ return index;
+ }
+ // Values can not be equal at this point.
+ // That would mean that the hash key for the same value differs.
+ OVR_ASSERT(!(e->Value == key));
+
+ // Keep looking through the chain.
+ index = e->NextInChain;
+ if (index == (UPInt)-1)
+ break; // end of chain
+
+ e = &E(index);
+ OVR_ASSERT(!e->IsEmpty());
+ }
+ return -1;
+ }
+
+
+ // Add a new value to the HashSet table, under the specified key.
+ template<class CRef>
+ void add(const CRef& key, UPInt hashValue)
+ {
+ CheckExpand();
+ hashValue &= pTable->SizeMask;
+
+ pTable->EntryCount++;
+
+ SPInt index = hashValue;
+ Entry* naturalEntry = &(E(index));
+
+ if (naturalEntry->IsEmpty())
+ {
+ // Put the new Entry in.
+ new (naturalEntry) Entry(key, -1);
+ }
+ else
+ {
+ // Find a blank spot.
+ SPInt blankIndex = index;
+ do {
+ blankIndex = (blankIndex + 1) & pTable->SizeMask;
+ } while(!E(blankIndex).IsEmpty());
+
+ Entry* blankEntry = &E(blankIndex);
+
+ if (naturalEntry->GetCachedHash(pTable->SizeMask) == (UPInt)index)
+ {
+ // Collision. Link into this chain.
+
+ // Move existing list head.
+ new (blankEntry) Entry(*naturalEntry); // placement new, copy ctor
+
+ // Put the new info in the natural Entry.
+ naturalEntry->Value = key;
+ naturalEntry->NextInChain = blankIndex;
+ }
+ else
+ {
+ // Existing Entry does not naturally
+ // belong in this slot. Existing
+ // Entry must be moved.
+
+ // Find natural location of collided element (i.e. root of chain)
+ SPInt collidedIndex = naturalEntry->GetCachedHash(pTable->SizeMask);
+ OVR_ASSERT(collidedIndex >= 0 && collidedIndex <= (SPInt)pTable->SizeMask);
+ for (;;)
+ {
+ Entry* e = &E(collidedIndex);
+ if (e->NextInChain == index)
+ {
+ // Here's where we need to splice.
+ new (blankEntry) Entry(*naturalEntry);
+ e->NextInChain = blankIndex;
+ break;
+ }
+ collidedIndex = e->NextInChain;
+ OVR_ASSERT(collidedIndex >= 0 && collidedIndex <= (SPInt)pTable->SizeMask);
+ }
+
+ // Put the new data in the natural Entry.
+ naturalEntry->Value = key;
+ naturalEntry->NextInChain = -1;
+ }
+ }
+
+ // Record hash value: has effect only if cached node is used.
+ naturalEntry->SetCachedHash(hashValue);
+ }
+
+ // Index access helpers.
+ Entry& E(UPInt index)
+ {
+ // Must have pTable and access needs to be within bounds.
+ OVR_ASSERT(index <= pTable->SizeMask);
+ return *(((Entry*) (pTable + 1)) + index);
+ }
+ const Entry& E(UPInt index) const
+ {
+ OVR_ASSERT(index <= pTable->SizeMask);
+ return *(((Entry*) (pTable + 1)) + index);
+ }
+
+
+ // Resize the HashSet table to the given size (Rehash the
+ // contents of the current table). The arg is the number of
+ // HashSet table entries, not the number of elements we should
+ // actually contain (which will be less than this).
+ void setRawCapacity(UPInt newSize)
+ {
+ if (newSize == 0)
+ {
+ // Special case.
+ Clear();
+ return;
+ }
+
+ // Minimum size; don't incur rehashing cost when expanding
+ // very small tables. Not that we perform this check before
+ // 'log2f' call to avoid fp exception with newSize == 1.
+ if (newSize < HashMinSize)
+ newSize = HashMinSize;
+ else
+ {
+ // Force newSize to be a power of two.
+ int bits = Alg::UpperBit(newSize-1) + 1; // Chop( Log2f((float)(newSize-1)) + 1);
+ OVR_ASSERT((UPInt(1) << bits) >= newSize);
+ newSize = UPInt(1) << bits;
+ }
+
+ SelfType newHash;
+ newHash.pTable = (TableType*)
+ Allocator::Alloc(
+ sizeof(TableType) + sizeof(Entry) * newSize);
+ // Need to do something on alloc failure!
+ OVR_ASSERT(newHash.pTable);
+
+ newHash.pTable->EntryCount = 0;
+ newHash.pTable->SizeMask = newSize - 1;
+ UPInt i, n;
+
+ // Mark all entries as empty.
+ for (i = 0; i < newSize; i++)
+ newHash.E(i).NextInChain = -2;
+
+ // Copy stuff to newHash
+ if (pTable)
+ {
+ for (i = 0, n = pTable->SizeMask; i <= n; i++)
+ {
+ Entry* e = &E(i);
+ if (e->IsEmpty() == false)
+ {
+ // Insert old Entry into new HashSet.
+ newHash.Add(e->Value);
+ // placement delete of old element
+ e->Clear();
+ }
+ }
+
+ // Delete our old data buffer.
+ Allocator::Free(pTable);
+ }
+
+ // Steal newHash's data.
+ pTable = newHash.pTable;
+ newHash.pTable = NULL;
+ }
+
+ struct TableType
+ {
+ UPInt EntryCount;
+ UPInt SizeMask;
+ // Entry array follows this structure
+ // in memory.
+ };
+ TableType* pTable;
+};
+
+
+
+//-----------------------------------------------------------------------------------
+template<class C, class HashF = FixedSizeHash<C>,
+ class AltHashF = HashF,
+ class Allocator = ContainerAllocator<C>,
+ class Entry = HashsetCachedEntry<C, HashF> >
+class HashSet : public HashSetBase<C, HashF, AltHashF, Allocator, Entry>
+{
+public:
+ typedef HashSetBase<C, HashF, AltHashF, Allocator, Entry> BaseType;
+ typedef HashSet<C, HashF, AltHashF, Allocator, Entry> SelfType;
+
+ HashSet() { }
+ HashSet(int sizeHint) : BaseType(sizeHint) { }
+ HashSet(const SelfType& src) : BaseType(src) { }
+ ~HashSet() { }
+
+ void operator = (const SelfType& src) { BaseType::Assign(src); }
+
+ // Set a new or existing value under the key, to the value.
+ // Pass a different class of 'key' so that assignment reference object
+ // can be passed instead of the actual object.
+ template<class CRef>
+ void Set(const CRef& key)
+ {
+ BaseType::Set(key);
+ }
+
+ template<class CRef>
+ inline void Add(const CRef& key)
+ {
+ BaseType::Add(key);
+ }
+
+ // Hint the bucket count to >= n.
+ void Resize(UPInt n)
+ {
+ BaseType::SetCapacity(n);
+ }
+
+ // Size the HashSet so that it can comfortably contain the given
+ // number of elements. If the HashSet already contains more
+ // elements than newSize, then this may be a no-op.
+ void SetCapacity(UPInt newSize)
+ {
+ BaseType::SetCapacity(newSize);
+ }
+
+};
+
+// HashSet with uncached hash code; declared for convenience.
+template<class C, class HashF = FixedSizeHash<C>,
+ class AltHashF = HashF,
+ class Allocator = ContainerAllocator<C> >
+class HashSetUncached : public HashSet<C, HashF, AltHashF, Allocator, HashsetEntry<C, HashF> >
+{
+public:
+
+ typedef HashSetUncached<C, HashF, AltHashF, Allocator> SelfType;
+ typedef HashSet<C, HashF, AltHashF, Allocator, HashsetEntry<C, HashF> > BaseType;
+
+ // Delegated constructors.
+ HashSetUncached() { }
+ HashSetUncached(int sizeHint) : BaseType(sizeHint) { }
+ HashSetUncached(const SelfType& src) : BaseType(src) { }
+ ~HashSetUncached() { }
+
+ void operator = (const SelfType& src)
+ {
+ BaseType::operator = (src);
+ }
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** Hash hash table implementation
+
+// Node for Hash - necessary so that Hash can delegate its implementation
+// to HashSet.
+template<class C, class U, class HashF>
+struct HashNode
+{
+ typedef HashNode<C, U, HashF> SelfType;
+ typedef C FirstType;
+ typedef U SecondType;
+
+ C First;
+ U Second;
+
+ // NodeRef is used to allow passing of elements into HashSet
+ // without using a temporary object.
+ struct NodeRef
+ {
+ const C* pFirst;
+ const U* pSecond;
+
+ NodeRef(const C& f, const U& s) : pFirst(&f), pSecond(&s) { }
+ NodeRef(const NodeRef& src) : pFirst(src.pFirst), pSecond(src.pSecond) { }
+
+ // Enable computation of ghash_node_hashf.
+ inline UPInt GetHash() const { return HashF()(*pFirst); }
+ // Necessary conversion to allow HashNode::operator == to work.
+ operator const C& () const { return *pFirst; }
+ };
+
+ // Note: No default constructor is necessary.
+ HashNode(const HashNode& src) : First(src.First), Second(src.Second) { }
+ HashNode(const NodeRef& src) : First(*src.pFirst), Second(*src.pSecond) { }
+ void operator = (const NodeRef& src) { First = *src.pFirst; Second = *src.pSecond; }
+
+ template<class K>
+ bool operator == (const K& src) const { return (First == src); }
+
+ template<class K>
+ static UPInt CalcHash(const K& data) { return HashF()(data); }
+ inline UPInt GetHash() const { return HashF()(First); }
+
+ // Hash functors used with this node. A separate functor is used for alternative
+ // key lookup so that it does not need to access the '.First' element.
+ struct NodeHashF
+ {
+ template<class K>
+ UPInt operator()(const K& data) const { return data.GetHash(); }
+ };
+ struct NodeAltHashF
+ {
+ template<class K>
+ UPInt operator()(const K& data) const { return HashNode<C,U,HashF>::CalcHash(data); }
+ };
+};
+
+
+
+// **** Extra hashset_entry types to allow NodeRef construction.
+
+// The big difference between the below types and the ones used in hash_set is that
+// these allow initializing the node with 'typename C::NodeRef& keyRef', which
+// is critical to avoid temporary node allocation on stack when using placement new.
+
+// Compact hash table Entry type that re-computes hash keys during hash traversal.
+// Good to use if the hash function is cheap or the hash value is already cached in C.
+template<class C, class HashF>
+class HashsetNodeEntry
+{
+public:
+ // Internal chaining for collisions.
+ SPInt NextInChain;
+ C Value;
+
+ HashsetNodeEntry()
+ : NextInChain(-2) { }
+ HashsetNodeEntry(const HashsetNodeEntry& e)
+ : NextInChain(e.NextInChain), Value(e.Value) { }
+ HashsetNodeEntry(const C& key, SPInt next)
+ : NextInChain(next), Value(key) { }
+ HashsetNodeEntry(const typename C::NodeRef& keyRef, SPInt next)
+ : NextInChain(next), Value(keyRef) { }
+
+ bool IsEmpty() const { return NextInChain == -2; }
+ bool IsEndOfChain() const { return NextInChain == -1; }
+ UPInt GetCachedHash(UPInt maskValue) const { return HashF()(Value) & maskValue; }
+ void SetCachedHash(UPInt hashValue) { OVR_UNUSED(hashValue); }
+
+ void Clear()
+ {
+ Value.~C(); // placement delete
+ NextInChain = -2;
+ }
+ // Free is only used from dtor of hash; Clear is used during regular operations:
+ // assignment, hash reallocations, value reassignments, so on.
+ void Free() { Clear(); }
+};
+
+// Hash table Entry type that caches the Entry hash value for nodes, so that it
+// does not need to be re-computed during access.
+template<class C, class HashF>
+class HashsetCachedNodeEntry
+{
+public:
+ // Internal chaining for collisions.
+ SPInt NextInChain;
+ UPInt HashValue;
+ C Value;
+
+ HashsetCachedNodeEntry()
+ : NextInChain(-2) { }
+ HashsetCachedNodeEntry(const HashsetCachedNodeEntry& e)
+ : NextInChain(e.NextInChain), HashValue(e.HashValue), Value(e.Value) { }
+ HashsetCachedNodeEntry(const C& key, SPInt next)
+ : NextInChain(next), Value(key) { }
+ HashsetCachedNodeEntry(const typename C::NodeRef& keyRef, SPInt next)
+ : NextInChain(next), Value(keyRef) { }
+
+ bool IsEmpty() const { return NextInChain == -2; }
+ bool IsEndOfChain() const { return NextInChain == -1; }
+ UPInt GetCachedHash(UPInt maskValue) const { OVR_UNUSED(maskValue); return HashValue; }
+ void SetCachedHash(UPInt hashValue) { HashValue = hashValue; }
+
+ void Clear()
+ {
+ Value.~C();
+ NextInChain = -2;
+ }
+ // Free is only used from dtor of hash; Clear is used during regular operations:
+ // assignment, hash reallocations, value reassignments, so on.
+ void Free() { Clear(); }
+};
+
+
+//-----------------------------------------------------------------------------------
+template<class C, class U,
+ class HashF = FixedSizeHash<C>,
+ class Allocator = ContainerAllocator<C>,
+ class HashNode = OVR::HashNode<C,U,HashF>,
+ class Entry = HashsetCachedNodeEntry<HashNode, typename HashNode::NodeHashF>,
+ class Container = HashSet<HashNode, typename HashNode::NodeHashF,
+ typename HashNode::NodeAltHashF, Allocator,
+ Entry> >
+class Hash
+{
+public:
+ OVR_MEMORY_REDEFINE_NEW(Hash)
+
+ // Types used for hash_set.
+ typedef U ValueType;
+ typedef Hash<C, U, HashF, Allocator, HashNode, Entry, Container> SelfType;
+
+ // Actual hash table itself, implemented as hash_set.
+ Container mHash;
+
+public:
+ Hash() { }
+ Hash(int sizeHint) : mHash(sizeHint) { }
+ Hash(const SelfType& src) : mHash(src.mHash) { }
+ ~Hash() { }
+
+ void operator = (const SelfType& src) { mHash = src.mHash; }
+
+ // Remove all entries from the Hash table.
+ inline void Clear() { mHash.Clear(); }
+ // Returns true if the Hash is empty.
+ inline bool IsEmpty() const { return mHash.IsEmpty(); }
+
+ // Access (set).
+ inline void Set(const C& key, const U& value)
+ {
+ typename HashNode::NodeRef e(key, value);
+ mHash.Set(e);
+ }
+ inline void Add(const C& key, const U& value)
+ {
+ typename HashNode::NodeRef e(key, value);
+ mHash.Add(e);
+ }
+
+ // Removes an element by clearing its Entry.
+ inline void Remove(const C& key)
+ {
+ mHash.RemoveAlt(key);
+ }
+ template<class K>
+ inline void RemoveAlt(const K& key)
+ {
+ mHash.RemoveAlt(key);
+ }
+
+ // Retrieve the value under the given key.
+ // - If there's no value under the key, then return false and leave *pvalue alone.
+ // - If there is a value, return true, and Set *Pvalue to the Entry's value.
+ // - If value == NULL, return true or false according to the presence of the key.
+ bool Get(const C& key, U* pvalue) const
+ {
+ const HashNode* p = mHash.GetAlt(key);
+ if (p)
+ {
+ if (pvalue)
+ *pvalue = p->Second;
+ return true;
+ }
+ return false;
+ }
+
+ template<class K>
+ bool GetAlt(const K& key, U* pvalue) const
+ {
+ const HashNode* p = mHash.GetAlt(key);
+ if (p)
+ {
+ if (pvalue)
+ *pvalue = p->Second;
+ return true;
+ }
+ return false;
+ }
+
+ // Retrieve the pointer to a value under the given key.
+ // - If there's no value under the key, then return NULL.
+ // - If there is a value, return the pointer.
+ inline U* Get(const C& key)
+ {
+ HashNode* p = mHash.GetAlt(key);
+ return p ? &p->Second : 0;
+ }
+ inline const U* Get(const C& key) const
+ {
+ const HashNode* p = mHash.GetAlt(key);
+ return p ? &p->Second : 0;
+ }
+
+ template<class K>
+ inline U* GetAlt(const K& key)
+ {
+ HashNode* p = mHash.GetAlt(key);
+ return p ? &p->Second : 0;
+ }
+ template<class K>
+ inline const U* GetAlt(const K& key) const
+ {
+ const HashNode* p = mHash.GetAlt(key);
+ return p ? &p->Second : 0;
+ }
+
+ // Sizing methods - delegate to Hash.
+ inline UPInt GetSize() const { return mHash.GetSize(); }
+ inline void Resize(UPInt n) { mHash.Resize(n); }
+ inline void SetCapacity(UPInt newSize) { mHash.SetCapacity(newSize); }
+
+ // Iterator API, like STL.
+ typedef typename Container::ConstIterator ConstIterator;
+ typedef typename Container::Iterator Iterator;
+
+ inline Iterator Begin() { return mHash.Begin(); }
+ inline Iterator End() { return mHash.End(); }
+ inline ConstIterator Begin() const { return mHash.Begin(); }
+ inline ConstIterator End() const { return mHash.End(); }
+
+ Iterator Find(const C& key) { return mHash.FindAlt(key); }
+ ConstIterator Find(const C& key) const { return mHash.FindAlt(key); }
+
+ template<class K>
+ Iterator FindAlt(const K& key) { return mHash.FindAlt(key); }
+ template<class K>
+ ConstIterator FindAlt(const K& key) const { return mHash.FindAlt(key); }
+};
+
+
+
+// Hash with uncached hash code; declared for convenience.
+template<class C, class U, class HashF = FixedSizeHash<C>, class Allocator = ContainerAllocator<C> >
+class HashUncached
+ : public Hash<C, U, HashF, Allocator, HashNode<C,U,HashF>,
+ HashsetNodeEntry<HashNode<C,U,HashF>, typename HashNode<C,U,HashF>::NodeHashF> >
+{
+public:
+ typedef HashUncached<C, U, HashF, Allocator> SelfType;
+ typedef Hash<C, U, HashF, Allocator, HashNode<C,U,HashF>,
+ HashsetNodeEntry<HashNode<C,U,HashF>,
+ typename HashNode<C,U,HashF>::NodeHashF> > BaseType;
+
+ // Delegated constructors.
+ HashUncached() { }
+ HashUncached(int sizeHint) : BaseType(sizeHint) { }
+ HashUncached(const SelfType& src) : BaseType(src) { }
+ ~HashUncached() { }
+ void operator = (const SelfType& src) { BaseType::operator = (src); }
+};
+
+
+
+// And identity hash in which keys serve as hash value. Can be uncached,
+// since hash computation is assumed cheap.
+template<class C, class U, class Allocator = ContainerAllocator<C>, class HashF = IdentityHash<C> >
+class HashIdentity
+ : public HashUncached<C, U, HashF, Allocator>
+{
+public:
+ typedef HashIdentity<C, U, Allocator, HashF> SelfType;
+ typedef HashUncached<C, U, HashF, Allocator> BaseType;
+
+ // Delegated constructors.
+ HashIdentity() { }
+ HashIdentity(int sizeHint) : BaseType(sizeHint) { }
+ HashIdentity(const SelfType& src) : BaseType(src) { }
+ ~HashIdentity() { }
+ void operator = (const SelfType& src) { BaseType::operator = (src); }
+};
+
+
+} // OVR
+
+
+#ifdef OVR_DEFINE_NEW
+#define new OVR_DEFINE_NEW
+#endif
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_KeyCodes.h b/LibOVR/Src/Kernel/OVR_KeyCodes.h
new file mode 100644
index 0000000..647688a
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_KeyCodes.h
@@ -0,0 +1,240 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_KeyCodes.h
+Content : Common keyboard constants
+Created : September 19, 2012
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#ifndef OVR_KeyCodes_h
+#define OVR_KeyCodes_h
+
+namespace OVR {
+
+//-----------------------------------------------------------------------------------
+// ***** KeyCode
+
+// KeyCode enumeration defines platform-independent keyboard key constants.
+// Note that Key_A through Key_Z are mapped to capital ascii constants.
+
+enum KeyCode
+{
+ // Key_None indicates that no key was specified.
+ Key_None = 0,
+
+ // A through Z and numbers 0 through 9.
+ Key_A = 65,
+ Key_B,
+ Key_C,
+ Key_D,
+ Key_E,
+ Key_F,
+ Key_G,
+ Key_H,
+ Key_I,
+ Key_J,
+ Key_K,
+ Key_L,
+ Key_M,
+ Key_N,
+ Key_O,
+ Key_P,
+ Key_Q,
+ Key_R,
+ Key_S,
+ Key_T,
+ Key_U,
+ Key_V,
+ Key_W,
+ Key_X,
+ Key_Y,
+ Key_Z,
+ Key_Num0 = 48,
+ Key_Num1,
+ Key_Num2,
+ Key_Num3,
+ Key_Num4,
+ Key_Num5,
+ Key_Num6,
+ Key_Num7,
+ Key_Num8,
+ Key_Num9,
+
+ // Numeric keypad.
+ Key_KP_0 = 0xa0,
+ Key_KP_1,
+ Key_KP_2,
+ Key_KP_3,
+ Key_KP_4,
+ Key_KP_5,
+ Key_KP_6,
+ Key_KP_7,
+ Key_KP_8,
+ Key_KP_9,
+ Key_KP_Multiply,
+ Key_KP_Add,
+ Key_KP_Enter,
+ Key_KP_Subtract,
+ Key_KP_Decimal,
+ Key_KP_Divide,
+
+ // Function keys.
+ Key_F1 = 0xb0,
+ Key_F2,
+ Key_F3,
+ Key_F4,
+ Key_F5,
+ Key_F6,
+ Key_F7,
+ Key_F8,
+ Key_F9,
+ Key_F10,
+ Key_F11,
+ Key_F12,
+ Key_F13,
+ Key_F14,
+ Key_F15,
+
+ // Other keys.
+ Key_Backspace = 8,
+ Key_Tab,
+ Key_Clear = 12,
+ Key_Return,
+ Key_Shift = 16,
+ Key_Control,
+ Key_Alt,
+ Key_Pause,
+ Key_CapsLock = 20, // Toggle
+ Key_Escape = 27,
+ Key_Space = 32,
+ Key_Quote = 39,
+ Key_PageUp = 0xc0,
+ Key_PageDown,
+ Key_End,
+ Key_Home,
+ Key_Left,
+ Key_Up,
+ Key_Right,
+ Key_Down,
+ Key_Insert,
+ Key_Delete,
+ Key_Help,
+
+ Key_Comma = 44,
+ Key_Minus,
+ Key_Slash = 47,
+ Key_Period,
+ Key_NumLock = 144, // Toggle
+ Key_ScrollLock = 145, // Toggle
+
+ Key_Semicolon = 59,
+ Key_Equal = 61,
+ Key_Bar = 192,
+ Key_BracketLeft = 91,
+ Key_Backslash,
+ Key_BracketRight,
+
+ Key_OEM_AX = 0xE1, // 'AX' key on Japanese AX keyboard
+ Key_OEM_102 = 0xE2, // "<>" or "\|" on RT 102-key keyboard.
+ Key_ICO_HELP = 0xE3, // Help key on ICO
+ Key_ICO_00 = 0xE4, // 00 key on ICO
+
+ Key_Meta,
+
+ // Total number of keys.
+ Key_CodeCount
+};
+
+
+//-----------------------------------------------------------------------------------
+
+class KeyModifiers
+{
+public:
+ enum
+ {
+ Key_ShiftPressed = 0x01,
+ Key_CtrlPressed = 0x02,
+ Key_AltPressed = 0x04,
+ Key_MetaPressed = 0x08,
+ Key_CapsToggled = 0x10,
+ Key_NumToggled = 0x20,
+ Key_ScrollToggled = 0x40,
+
+ Initialized_Bit = 0x80,
+ Initialized_Mask = 0xFF
+ };
+ unsigned char States;
+
+ KeyModifiers() : States(0) { }
+ KeyModifiers(unsigned char st) : States((unsigned char)(st | Initialized_Bit)) { }
+
+ void Reset() { States = 0; }
+
+ bool IsShiftPressed() const { return (States & Key_ShiftPressed) != 0; }
+ bool IsCtrlPressed() const { return (States & Key_CtrlPressed) != 0; }
+ bool IsAltPressed() const { return (States & Key_AltPressed) != 0; }
+ bool IsMetaPressed() const { return (States & Key_MetaPressed) != 0; }
+ bool IsCapsToggled() const { return (States & Key_CapsToggled) != 0; }
+ bool IsNumToggled() const { return (States & Key_NumToggled) != 0; }
+ bool IsScrollToggled() const{ return (States & Key_ScrollToggled) != 0; }
+
+ void SetShiftPressed(bool v = true) { (v) ? States |= Key_ShiftPressed : States &= ~Key_ShiftPressed; }
+ void SetCtrlPressed(bool v = true) { (v) ? States |= Key_CtrlPressed : States &= ~Key_CtrlPressed; }
+ void SetAltPressed(bool v = true) { (v) ? States |= Key_AltPressed : States &= ~Key_AltPressed; }
+ void SetMetaPressed(bool v = true) { (v) ? States |= Key_MetaPressed : States &= ~Key_MetaPressed; }
+ void SetCapsToggled(bool v = true) { (v) ? States |= Key_CapsToggled : States &= ~Key_CapsToggled; }
+ void SetNumToggled(bool v = true) { (v) ? States |= Key_NumToggled : States &= ~Key_NumToggled; }
+ void SetScrollToggled(bool v = true) { (v) ? States |= Key_ScrollToggled: States &= ~Key_ScrollToggled; }
+
+ bool IsInitialized() const { return (States & Initialized_Mask) != 0; }
+};
+
+
+//-----------------------------------------------------------------------------------
+
+/*
+enum PadKeyCode
+{
+ Pad_None, // Indicates absence of key code.
+ Pad_Back,
+ Pad_Start,
+ Pad_A,
+ Pad_B,
+ Pad_X,
+ Pad_Y,
+ Pad_R1, // RightShoulder;
+ Pad_L1, // LeftShoulder;
+ Pad_R2, // RightTrigger;
+ Pad_L2, // LeftTrigger;
+ Pad_Up,
+ Pad_Down,
+ Pad_Right,
+ Pad_Left,
+ Pad_Plus,
+ Pad_Minus,
+ Pad_1,
+ Pad_2,
+ Pad_H,
+ Pad_C,
+ Pad_Z,
+ Pad_O,
+ Pad_T,
+ Pad_S,
+ Pad_Select,
+ Pad_Home,
+ Pad_RT, // RightThumb;
+ Pad_LT // LeftThumb;
+};
+*/
+
+} // OVR
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_List.h b/LibOVR/Src/Kernel/OVR_List.h
new file mode 100644
index 0000000..0292f51
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_List.h
@@ -0,0 +1,325 @@
+/************************************************************************************
+
+PublicHeader: OVR
+Filename : OVR_List.h
+Content : Template implementation for doubly-connected linked List
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#ifndef OVR_List_h
+#define OVR_List_h
+
+#include "OVR_Types.h"
+
+namespace OVR {
+
+//-----------------------------------------------------------------------------------
+// ***** ListNode
+//
+// Base class for the elements of the intrusive linked list.
+// To store elements in the List do:
+//
+// struct MyData : ListNode<MyData>
+// {
+// . . .
+// };
+
+template<class T>
+struct ListNode
+{
+ union {
+ T* pPrev;
+ void* pVoidPrev;
+ };
+ union {
+ T* pNext;
+ void* pVoidNext;
+ };
+
+ void RemoveNode()
+ {
+ pPrev->pNext = pNext;
+ pNext->pPrev = pPrev;
+ }
+
+ // Removes us from the list and inserts pnew there instead.
+ void ReplaceNodeWith(T* pnew)
+ {
+ pPrev->pNext = pnew;
+ pNext->pPrev = pnew;
+ pnew->pPrev = pPrev;
+ pnew->pNext = pNext;
+ }
+
+ // Inserts the argument linked list node after us in the list.
+ void InsertNodeAfter(T* p)
+ {
+ p->pPrev = pNext->pPrev; // this
+ p->pNext = pNext;
+ pNext->pPrev = p;
+ pNext = p;
+ }
+ // Inserts the argument linked list node before us in the list.
+ void InsertNodeBefore(T* p)
+ {
+ p->pNext = pNext->pPrev; // this
+ p->pPrev = pPrev;
+ pPrev->pNext = p;
+ pPrev = p;
+ }
+
+ void Alloc_MoveTo(ListNode<T>* pdest)
+ {
+ pdest->pNext = pNext;
+ pdest->pPrev = pPrev;
+ pPrev->pNext = (T*)pdest;
+ pNext->pPrev = (T*)pdest;
+ }
+};
+
+
+//------------------------------------------------------------------------
+// ***** List
+//
+// Doubly linked intrusive list.
+// The data type must be derived from ListNode.
+//
+// Adding: PushFront(), PushBack().
+// Removing: Remove() - the element must be in the list!
+// Moving: BringToFront(), SendToBack() - the element must be in the list!
+//
+// Iterating:
+// MyData* data = MyList.GetFirst();
+// while (!MyList.IsNull(data))
+// {
+// . . .
+// data = MyList.GetNext(data);
+// }
+//
+// Removing:
+// MyData* data = MyList.GetFirst();
+// while (!MyList.IsNull(data))
+// {
+// MyData* next = MyList.GetNext(data);
+// if (ToBeRemoved(data))
+// MyList.Remove(data);
+// data = next;
+// }
+//
+
+// List<> represents a doubly-linked list if T, where each T must derive
+// from ListNode<B>. B specifies the base class that was directly
+// derived from ListNode, and is only necessary if there is an intermediate
+// inheritance chain.
+
+template<class T, class B = T> class List
+{
+public:
+ typedef T ValueType;
+
+ List()
+ {
+ Root.pNext = Root.pPrev = (ValueType*)&Root;
+ }
+
+ void Clear()
+ {
+ Root.pNext = Root.pPrev = (ValueType*)&Root;
+ }
+
+ const ValueType* GetFirst() const { return (const ValueType*)Root.pNext; }
+ const ValueType* GetLast () const { return (const ValueType*)Root.pPrev; }
+ ValueType* GetFirst() { return (ValueType*)Root.pNext; }
+ ValueType* GetLast () { return (ValueType*)Root.pPrev; }
+
+ // Determine if list is empty (i.e.) points to itself.
+ // Go through void* access to avoid issues with strict-aliasing optimizing out the
+ // access after RemoveNode(), etc.
+ bool IsEmpty() const { return Root.pVoidNext == (const T*)(const B*)&Root; }
+ bool IsFirst(const ValueType* p) const { return p == Root.pNext; }
+ bool IsLast (const ValueType* p) const { return p == Root.pPrev; }
+ bool IsNull (const ValueType* p) const { return p == (const T*)(const B*)&Root; }
+
+ inline static const ValueType* GetPrev(const ValueType* p) { return (const ValueType*)p->pPrev; }
+ inline static const ValueType* GetNext(const ValueType* p) { return (const ValueType*)p->pNext; }
+ inline static ValueType* GetPrev( ValueType* p) { return (ValueType*)p->pPrev; }
+ inline static ValueType* GetNext( ValueType* p) { return (ValueType*)p->pNext; }
+
+ void PushFront(ValueType* p)
+ {
+ p->pNext = Root.pNext;
+ p->pPrev = (ValueType*)&Root;
+ Root.pNext->pPrev = p;
+ Root.pNext = p;
+ }
+
+ void PushBack(ValueType* p)
+ {
+ p->pPrev = Root.pPrev;
+ p->pNext = (ValueType*)&Root;
+ Root.pPrev->pNext = p;
+ Root.pPrev = p;
+ }
+
+ static void Remove(ValueType* p)
+ {
+ p->pPrev->pNext = p->pNext;
+ p->pNext->pPrev = p->pPrev;
+ }
+
+ void BringToFront(ValueType* p)
+ {
+ Remove(p);
+ PushFront(p);
+ }
+
+ void SendToBack(ValueType* p)
+ {
+ Remove(p);
+ PushBack(p);
+ }
+
+ // Appends the contents of the argument list to the front of this list;
+ // items are removed from the argument list.
+ void PushListToFront(List<T>& src)
+ {
+ if (!src.IsEmpty())
+ {
+ ValueType* pfirst = src.GetFirst();
+ ValueType* plast = src.GetLast();
+ src.Clear();
+ plast->pNext = Root.pNext;
+ pfirst->pPrev = (ValueType*)&Root;
+ Root.pNext->pPrev = plast;
+ Root.pNext = pfirst;
+ }
+ }
+
+ void PushListToBack(List<T>& src)
+ {
+ if (!src.IsEmpty())
+ {
+ ValueType* pfirst = src.GetFirst();
+ ValueType* plast = src.GetLast();
+ src.Clear();
+ plast->pNext = (ValueType*)&Root;
+ pfirst->pPrev = Root.pPrev;
+ Root.pPrev->pNext = pfirst;
+ Root.pPrev = plast;
+ }
+ }
+
+ // Removes all source list items after (and including) the 'pfirst' node from the
+ // source list and adds them to out list.
+ void PushFollowingListItemsToFront(List<T>& src, ValueType *pfirst)
+ {
+ if (pfirst != &src.Root)
+ {
+ ValueType *plast = src.Root.pPrev;
+
+ // Remove list remainder from source.
+ pfirst->pPrev->pNext = (ValueType*)&src.Root;
+ src.Root.pPrev = pfirst->pPrev;
+ // Add the rest of the items to list.
+ plast->pNext = Root.pNext;
+ pfirst->pPrev = (ValueType*)&Root;
+ Root.pNext->pPrev = plast;
+ Root.pNext = pfirst;
+ }
+ }
+
+ // Removes all source list items up to but NOT including the 'pend' node from the
+ // source list and adds them to out list.
+ void PushPrecedingListItemsToFront(List<T>& src, ValueType *ptail)
+ {
+ if (src.GetFirst() != ptail)
+ {
+ ValueType *pfirst = src.Root.pNext;
+ ValueType *plast = ptail->pPrev;
+
+ // Remove list remainder from source.
+ ptail->pPrev = (ValueType*)&src.Root;
+ src.Root.pNext = ptail;
+
+ // Add the rest of the items to list.
+ plast->pNext = Root.pNext;
+ pfirst->pPrev = (ValueType*)&Root;
+ Root.pNext->pPrev = plast;
+ Root.pNext = pfirst;
+ }
+ }
+
+
+ // Removes a range of source list items starting at 'pfirst' and up to, but not including 'pend',
+ // and adds them to out list. Note that source items MUST already be in the list.
+ void PushListItemsToFront(ValueType *pfirst, ValueType *pend)
+ {
+ if (pfirst != pend)
+ {
+ ValueType *plast = pend->pPrev;
+
+ // Remove list remainder from source.
+ pfirst->pPrev->pNext = pend;
+ pend->pPrev = pfirst->pPrev;
+ // Add the rest of the items to list.
+ plast->pNext = Root.pNext;
+ pfirst->pPrev = (ValueType*)&Root;
+ Root.pNext->pPrev = plast;
+ Root.pNext = pfirst;
+ }
+ }
+
+
+ void Alloc_MoveTo(List<T>* pdest)
+ {
+ if (IsEmpty())
+ pdest->Clear();
+ else
+ {
+ pdest->Root.pNext = Root.pNext;
+ pdest->Root.pPrev = Root.pPrev;
+
+ Root.pNext->pPrev = (ValueType*)&pdest->Root;
+ Root.pPrev->pNext = (ValueType*)&pdest->Root;
+ }
+ }
+
+
+private:
+ // Copying is prohibited
+ List(const List<T>&);
+ const List<T>& operator = (const List<T>&);
+
+ ListNode<B> Root;
+};
+
+
+//------------------------------------------------------------------------
+// ***** FreeListElements
+//
+// Remove all elements in the list and free them in the allocator
+
+template<class List, class Allocator>
+void FreeListElements(List& list, Allocator& allocator)
+{
+ typename List::ValueType* self = list.GetFirst();
+ while(!list.IsNull(self))
+ {
+ typename List::ValueType* next = list.GetNext(self);
+ allocator.Free(self);
+ self = next;
+ }
+ list.Clear();
+}
+
+} // OVR
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_Log.cpp b/LibOVR/Src/Kernel/OVR_Log.cpp
new file mode 100644
index 0000000..baede98
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_Log.cpp
@@ -0,0 +1,173 @@
+/************************************************************************************
+
+Filename : OVR_Log.cpp
+Content : Logging support
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#include "OVR_Log.h"
+#include "OVR_Std.h"
+#include <stdarg.h>
+#include <stdio.h>
+
+#if defined(OVR_OS_WIN32)
+#include <windows.h>
+#elif defined(OVR_OS_ANDROID)
+#include <android/log.h>
+#endif
+
+namespace OVR {
+
+// Global Log pointer.
+Log* volatile OVR_GlobalLog = 0;
+
+//-----------------------------------------------------------------------------------
+// ***** Log Implementation
+
+Log::~Log()
+{
+ // Clear out global log
+ if (this == OVR_GlobalLog)
+ {
+ // TBD: perhaps we should ASSERT if this happens before system shutdown?
+ OVR_GlobalLog = 0;
+ }
+}
+
+void Log::LogMessageVarg(LogMessageType messageType, const char* fmt, va_list argList)
+{
+ if ((messageType & LoggingMask) == 0)
+ return;
+#ifndef OVR_BUILD_DEBUG
+ if (IsDebugMessage(messageType))
+ return;
+#endif
+
+ char buffer[MaxLogBufferMessageSize];
+ FormatLog(buffer, MaxLogBufferMessageSize, messageType, fmt, argList);
+ DefaultLogOutput(buffer, IsDebugMessage(messageType));
+}
+
+void OVR::Log::LogMessage(LogMessageType messageType, const char* pfmt, ...)
+{
+ va_list argList;
+ va_start(argList, pfmt);
+ LogMessageVarg(messageType, pfmt, argList);
+ va_end(argList);
+}
+
+
+void Log::FormatLog(char* buffer, unsigned bufferSize, LogMessageType messageType,
+ const char* fmt, va_list argList)
+{
+ bool addNewline = true;
+
+ switch(messageType)
+ {
+ case Log_Error: OVR_strcpy(buffer, bufferSize, "Error: "); break;
+ case Log_Debug: OVR_strcpy(buffer, bufferSize, "Debug: "); break;
+ case Log_Assert: OVR_strcpy(buffer, bufferSize, "Assert: "); break;
+ case Log_Text: buffer[0] = 0; addNewline = false; break;
+ case Log_DebugText: buffer[0] = 0; addNewline = false; break;
+ default:
+ buffer[0] = 0;
+ addNewline = false;
+ break;
+ }
+
+ UPInt prefixLength = OVR_strlen(buffer);
+ char *buffer2 = buffer + prefixLength;
+ OVR_vsprintf(buffer2, bufferSize - prefixLength, fmt, argList);
+
+ if (addNewline)
+ OVR_strcat(buffer, bufferSize, "\n");
+}
+
+
+void Log::DefaultLogOutput(const char* formattedText, bool debug)
+{
+
+#if defined(OVR_OS_WIN32)
+ // Under Win32, output regular messages to console if it exists; debug window otherwise.
+ static DWORD dummyMode;
+ static bool hasConsole = (GetStdHandle(STD_OUTPUT_HANDLE) != INVALID_HANDLE_VALUE) &&
+ (GetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), &dummyMode));
+
+ if (!hasConsole || debug)
+ {
+ ::OutputDebugStringA(formattedText);
+ }
+ else
+ {
+ fputs(formattedText, stdout);
+ }
+
+#elif defined(OVR_OS_ANDROID)
+ __android_log_write(ANDROID_LOG_INFO, "OVR", formattedText);
+
+#else
+ fputs(formattedText, stdout);
+
+#endif
+
+ // Just in case.
+ OVR_UNUSED2(formattedText, debug);
+}
+
+
+//static
+void Log::SetGlobalLog(Log *log)
+{
+ OVR_GlobalLog = log;
+}
+//static
+Log* Log::GetGlobalLog()
+{
+// No global log by default?
+// if (!OVR_GlobalLog)
+// OVR_GlobalLog = GetDefaultLog();
+ return OVR_GlobalLog;
+}
+
+//static
+Log* Log::GetDefaultLog()
+{
+ // Create default log pointer statically so that it can be used
+ // even during startup.
+ static Log defaultLog;
+ return &defaultLog;
+}
+
+
+//-----------------------------------------------------------------------------------
+// ***** Global Logging functions
+
+#define OVR_LOG_FUNCTION_IMPL(Name) \
+ void Log##Name(const char* fmt, ...) \
+ { \
+ if (OVR_GlobalLog) \
+ { \
+ va_list argList; va_start(argList, fmt); \
+ OVR_GlobalLog->LogMessageVarg(Log_##Name, fmt, argList); \
+ va_end(argList); \
+ } \
+ }
+
+OVR_LOG_FUNCTION_IMPL(Text)
+OVR_LOG_FUNCTION_IMPL(Error)
+
+#ifdef OVR_BUILD_DEBUG
+OVR_LOG_FUNCTION_IMPL(DebugText)
+OVR_LOG_FUNCTION_IMPL(Debug)
+OVR_LOG_FUNCTION_IMPL(Assert)
+#endif
+
+} // OVR
diff --git a/LibOVR/Src/Kernel/OVR_Log.h b/LibOVR/Src/Kernel/OVR_Log.h
new file mode 100644
index 0000000..acc298d
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_Log.h
@@ -0,0 +1,193 @@
+/************************************************************************************
+
+PublicHeader: OVR
+Filename : OVR_Log.h
+Content : Logging support
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#ifndef OVR_Log_h
+#define OVR_Log_h
+
+#include "OVR_Types.h"
+#include <stdarg.h>
+
+namespace OVR {
+
+//-----------------------------------------------------------------------------------
+// ***** Logging Constants
+
+// LogMaskConstants defined bit mask constants that describe what log messages
+// should be displayed.
+enum LogMaskConstants
+{
+ LogMask_Regular = 0x100,
+ LogMask_Debug = 0x200,
+ LogMask_None = 0,
+ LogMask_All = LogMask_Regular|LogMask_Debug
+};
+
+
+// LogMessageType describes the type of the log message, controls when it is
+// displayed and what prefix/suffix is given to it. Messages are subdivided into
+// regular and debug logging types. Debug logging is only generated in debug builds.
+//
+// Log_Text - General output text displayed without prefix or new-line.
+// Used in OVR libraries for general log flow messages
+// such as "Device Initialized".
+//
+// Log_Error - Error message output with "Error: %s\n", intended for
+// application/sample-level use only, in cases where an expected
+// operation failed. OVR libraries should not use this internally,
+// reporting status codes instead.
+//
+// Log_DebugText - Message without prefix or new lines; output in Debug build only.
+//
+// Log_Debug - Debug-build only message, formatted with "Debug: %s\n".
+// Intended to comment on incorrect API usage that doesn't lead
+// to crashes but can be avoided with proper use.
+// There is no Debug Error on purpose, since real errors should
+// be handled by API user.
+//
+// Log_Assert - Debug-build only message, formatted with "Assert: %s\n".
+// Intended for severe unrecoverable conditions in library
+// source code. Generated though OVR_ASSERT_MSG(c, "Text").
+
+enum LogMessageType
+{
+ // General Logging
+ Log_Text = LogMask_Regular | 0,
+ Log_Error = LogMask_Regular | 1, // "Error: %s\n".
+
+ // Debug-only messages (not generated in release build)
+ Log_DebugText = LogMask_Debug | 0,
+ Log_Debug = LogMask_Debug | 1, // "Debug: %s\n".
+ Log_Assert = LogMask_Debug | 2, // "Assert: %s\n".
+};
+
+
+// LOG_VAARG_ATTRIBUTE macro, enforces printf-style fromatting for message types
+#ifdef __GNUC__
+# define OVR_LOG_VAARG_ATTRIBUTE(a,b) __attribute__((format (printf, a, b)))
+#else
+# define OVR_LOG_VAARG_ATTRIBUTE(a,b)
+#endif
+
+
+//-----------------------------------------------------------------------------------
+// ***** Log
+
+// Log defines a base class interface that can be implemented to catch both
+// debug and runtime messages.
+// Debug logging can be overridden by calling Log::SetGlobalLog.
+
+class Log
+{
+ friend class System;
+public:
+ Log(unsigned logMask = LogMask_Debug) : LoggingMask(logMask) { }
+ virtual ~Log();
+
+ // Log formating buffer size used by default LogMessageVarg. Longer strings are truncated.
+ enum { MaxLogBufferMessageSize = 2048 };
+
+ unsigned GetLoggingMask() const { return LoggingMask; }
+ void SetLoggingMask(unsigned logMask) { LoggingMask = logMask; }
+
+ // This virtual function receives all the messages,
+ // developers should override this function in order to do custom logging
+ virtual void LogMessageVarg(LogMessageType messageType, const char* fmt, va_list argList);
+
+ // Call the logging function with specific message type, with no type filtering.
+ void LogMessage(LogMessageType messageType,
+ const char* fmt, ...) OVR_LOG_VAARG_ATTRIBUTE(3,4);
+
+
+ // Helper used by LogMessageVarg to format the log message, writing the resulting
+ // string into buffer. It formats text based on fmt and appends prefix/new line
+ // based on LogMessageType.
+ static void FormatLog(char* buffer, unsigned bufferSize, LogMessageType messageType,
+ const char* fmt, va_list argList);
+
+ // Default log output implementation used by by LogMessageVarg.
+ // Debug flag may be used to re-direct output on some platforms, but doesn't
+ // necessarily disable it in release builds; that is the job of the called.
+ static void DefaultLogOutput(const char* textBuffer, bool debug);
+
+ // Determines if the specified message type is for debugging only.
+ static bool IsDebugMessage(LogMessageType messageType)
+ {
+ return (messageType & LogMask_Debug) != 0;
+ }
+
+ // *** Global APIs
+
+ // Global Log registration APIs.
+ // - Global log is used for OVR_DEBUG messages. Set global log to null (0)
+ // to disable all logging.
+ static void SetGlobalLog(Log *log);
+ static Log* GetGlobalLog();
+
+ // Returns default log singleton instance.
+ static Log* GetDefaultLog();
+
+ // Applies logMask to the default log and returns a pointer to it.
+ // By default, only Debug logging is enabled, so to avoid SDK generating console
+ // messages in user app (those are always disabled in release build,
+ // even if the flag is set). This function is useful in System constructor.
+ static Log* ConfigureDefaultLog(unsigned logMask = LogMask_Debug)
+ {
+ Log* log = GetDefaultLog();
+ log->SetLoggingMask(logMask);
+ return log;
+ }
+
+private:
+ // Logging mask described by LogMaskConstants.
+ unsigned LoggingMask;
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** Global Logging Functions and Debug Macros
+
+// These functions will output text to global log with semantics described by
+// their LogMessageType.
+void LogText(const char* fmt, ...) OVR_LOG_VAARG_ATTRIBUTE(1,2);
+void LogError(const char* fmt, ...) OVR_LOG_VAARG_ATTRIBUTE(1,2);
+
+#ifdef OVR_BUILD_DEBUG
+
+ // Debug build only logging.
+ void LogDebugText(const char* fmt, ...) OVR_LOG_VAARG_ATTRIBUTE(1,2);
+ void LogDebug(const char* fmt, ...) OVR_LOG_VAARG_ATTRIBUTE(1,2);
+ void LogAssert(const char* fmt, ...) OVR_LOG_VAARG_ATTRIBUTE(1,2);
+
+ // Macro to do debug logging, printf-style.
+ // An extra set of set of parenthesis must be used around arguments,
+ // as in: OVR_LOG_DEBUG(("Value %d", 2)).
+ #define OVR_DEBUG_LOG(args) do { OVR::LogDebug args; } while(0)
+ #define OVR_DEBUG_LOG_TEXT(args) do { OVR::LogDebugText args; } while(0)
+
+ #define OVR_ASSERT_LOG(c, args) do { if (!(c)) { OVR::LogAssert args; OVR_DEBUG_BREAK; } } while(0)
+
+#else
+
+ // If not in debug build, macros do nothing.
+ #define OVR_DEBUG_LOG(args) ((void)0)
+ #define OVR_DEBUG_LOG_TEXT(args) ((void)0)
+ #define OVR_ASSERT_LOG(c, args) ((void)0)
+
+#endif
+
+} // OVR
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_Math.cpp b/LibOVR/Src/Kernel/OVR_Math.cpp
new file mode 100644
index 0000000..eaf23fe
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_Math.cpp
@@ -0,0 +1,153 @@
+/************************************************************************************
+
+Filename : OVR_Math.h
+Content : Implementation of 3D primitives such as vectors, matrices.
+Created : September 4, 2012
+Authors : Andrew Reisse, Michael Antonov
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#include "OVR_Math.h"
+
+#include <float.h>
+
+namespace OVR {
+
+
+//-------------------------------------------------------------------------------------
+// ***** Math
+
+
+// Single-precision Math constants class.
+const float Math<float>::Pi = 3.1415926f;
+const float Math<float>::TwoPi = 3.1415926f * 2;
+const float Math<float>::PiOver2 = 3.1415926f / 2.0f;
+const float Math<float>::PiOver4 = 3.1415926f / 4.0f;
+const float Math<float>::E = 2.7182818f;
+
+const float Math<float>::MaxValue = FLT_MAX;
+const float Math<float>::MinPositiveValue = FLT_MIN;
+
+const float Math<float>::RadToDegreeFactor = 360.0f / Math<float>::TwoPi;
+const float Math<float>::DegreeToRadFactor = Math<float>::TwoPi / 360.0f;
+
+const float Math<float>::Tolerance = 0.00001f;
+const float Math<float>::SingularityRadius = 0.0000001f; // Use for Gimbal lock numerical problems
+
+
+// Double-precision Math constants class.
+const double Math<double>::Pi = 3.14159265358979;
+const double Math<double>::TwoPi = 3.14159265358979 * 2;
+const double Math<double>::PiOver2 = 3.14159265358979 / 2.0;
+const double Math<double>::PiOver4 = 3.14159265358979 / 4.0;
+const double Math<double>::E = 2.71828182845905;
+
+const double Math<double>::MaxValue = DBL_MAX;
+const double Math<double>::MinPositiveValue = DBL_MIN;
+
+const double Math<double>::RadToDegreeFactor = 360.0 / Math<double>::TwoPi;
+const double Math<double>::DegreeToRadFactor = Math<double>::TwoPi / 360.0;
+
+const double Math<double>::Tolerance = 0.00001;
+const double Math<double>::SingularityRadius = 0.000000000001; // Use for Gimbal lock numerical problems
+
+
+
+//-------------------------------------------------------------------------------------
+// ***** Matrix4f
+
+
+Matrix4f Matrix4f::LookAtRH(const Vector3f& eye, const Vector3f& at, const Vector3f& up)
+{
+ Vector3f z = (eye - at).Normalized(); // Forward
+ Vector3f x = up.Cross(z).Normalized(); // Right
+ Vector3f y = z.Cross(x);
+
+ Matrix4f m(x.x, x.y, x.z, -(x * eye),
+ y.x, y.y, y.z, -(y * eye),
+ z.x, z.y, z.z, -(z * eye),
+ 0, 0, 0, 1 );
+ return m;
+}
+
+Matrix4f Matrix4f::LookAtLH(const Vector3f& eye, const Vector3f& at, const Vector3f& up)
+{
+ Vector3f z = (at - eye).Normalized(); // Forward
+ Vector3f x = up.Cross(z).Normalized(); // Right
+ Vector3f y = z.Cross(x);
+
+ Matrix4f m(x.x, x.y, x.z, -(x * eye),
+ y.x, y.y, y.z, -(y * eye),
+ z.x, z.y, z.z, -(z * eye),
+ 0, 0, 0, 1 );
+ return m;
+}
+
+
+Matrix4f Matrix4f::PerspectiveLH(float yfov, float aspect, float znear, float zfar)
+{
+ Matrix4f m;
+ float tanHalfFov = tan(yfov * 0.5f);
+
+ m.M[0][0] = 1.0f / (aspect * tanHalfFov);
+ m.M[1][1] = 1.0f / tanHalfFov;
+ m.M[2][2] = zfar / (zfar - znear);
+ m.M[3][2] = 1.0f;
+ m.M[2][3] = (zfar * znear) / (znear - zfar);
+ m.M[3][3] = 0.0f;
+
+ // Note: Post-projection matrix result assumes Left-Handed coordinate system,
+ // with Y up, X right and Z forward. This supports positive z-buffer values.
+ return m;
+}
+
+
+Matrix4f Matrix4f::PerspectiveRH(float yfov, float aspect, float znear, float zfar)
+{
+ Matrix4f m;
+ float tanHalfFov = tan(yfov * 0.5f);
+
+ m.M[0][0] = 1.0f / (aspect * tanHalfFov);
+ m.M[1][1] = 1.0f / tanHalfFov;
+ m.M[2][2] = zfar / (znear - zfar);
+ // m.M[2][2] = zfar / (zfar - znear);
+ m.M[3][2] = -1.0f;
+ m.M[2][3] = (zfar * znear) / (znear - zfar);
+ m.M[3][3] = 0.0f;
+
+ // Note: Post-projection matrix result assumes Left-Handed coordinate system,
+ // with Y up, X right and Z forward. This supports positive z-buffer values.
+ // This is the case even for RHS cooridnate input.
+ return m;
+}
+
+
+/*
+OffCenterLH
+
+2*zn/(r-l) 0 0 0
+0 2*zn/(t-b) 0 0
+(l+r)/(l-r) (t+b)/(b-t) zf/(zf-zn) 1
+0 0 zn*zf/(zn-zf) 0
+
+*/
+
+
+Matrix4f Matrix4f::Ortho2D(float w, float h)
+{
+ Matrix4f m;
+ m.M[0][0] = 2.0f/w;
+ m.M[1][1] = -2.0f/h;
+ m.M[0][3] = -1.0;
+ m.M[1][3] = 1.0;
+ m.M[2][2] = 0;
+ return m;
+}
+
+}
diff --git a/LibOVR/Src/Kernel/OVR_Math.h b/LibOVR/Src/Kernel/OVR_Math.h
new file mode 100644
index 0000000..8c5a7ba
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_Math.h
@@ -0,0 +1,1070 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_Math.h
+Content : Implementation of 3D primitives such as vectors, matrices.
+Created : September 4, 2012
+Authors : Andrew Reisse, Michael Antonov, Steve LaValle, Anna Yershova
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_Math_h
+#define OVR_Math_h
+
+#include <assert.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include "OVR_Types.h"
+#include "OVR_RefCount.h"
+
+namespace OVR {
+
+//-------------------------------------------------------------------------------------
+// Constants for 3D world/axis definitions.
+
+// Definitions of axes for coordinate and rotation conversions.
+enum Axis
+{
+ Axis_X = 0, Axis_Y = 1, Axis_Z = 2
+};
+
+// RotateDirection describes the rotation direction around an axis, interpreted as follows:
+// CW - Clockwise while looking "down" from positive axis towards the origin.
+// CCW - Counter-clockwise while looking from the positive axis towards the origin,
+// which is in the negative axis direction.
+// CCW is the default for the RHS coordinate system. Oculus standard RHS coordinate
+// system defines Y up, X right, and Z back (pointing out from the screen). In this
+// system Rotate_CCW around Z will specifies counter-clockwise rotation in XY plane.
+enum RotateDirection
+{
+ Rotate_CCW = 1,
+ Rotate_CW = -1
+};
+
+enum HandedSystem
+{
+ Handed_R = 1, Handed_L = -1
+};
+
+// AxisDirection describes which way the axis points. Used by WorldAxes.
+enum AxisDirection
+{
+ Axis_Up = 2,
+ Axis_Down = -2,
+ Axis_Right = 1,
+ Axis_Left = -1,
+ Axis_In = 3,
+ Axis_Out = -3
+};
+
+struct WorldAxes
+{
+ AxisDirection XAxis, YAxis, ZAxis;
+
+ WorldAxes(AxisDirection x, AxisDirection y, AxisDirection z)
+ : XAxis(x), YAxis(y), ZAxis(z)
+ { OVR_ASSERT(abs(x) != abs(y) && abs(y) != abs(z) && abs(z) != abs(x));}
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** Math
+
+// Math class contains constants and functions. This class is a template specialized
+// per type, with Math<float> and Math<double> being distinct.
+template<class Type>
+class Math
+{
+};
+
+// Single-precision Math constants class.
+template<>
+class Math<float>
+{
+public:
+ static const float Pi;
+ static const float TwoPi;
+ static const float PiOver2;
+ static const float PiOver4;
+ static const float E;
+
+ static const float MaxValue; // Largest positive float Value
+ static const float MinPositiveValue; // Smallest possible positive value
+
+ static const float RadToDegreeFactor;
+ static const float DegreeToRadFactor;
+
+ static const float Tolerance; // 0.00001f;
+ static const float SingularityRadius; //0.00000000001f for Gimbal lock numerical problems
+};
+
+// Double-precision Math constants class.
+template<>
+class Math<double>
+{
+public:
+ static const double Pi;
+ static const double TwoPi;
+ static const double PiOver2;
+ static const double PiOver4;
+ static const double E;
+
+ static const double MaxValue; // Largest positive double Value
+ static const double MinPositiveValue; // Smallest possible positive value
+
+ static const double RadToDegreeFactor;
+ static const double DegreeToRadFactor;
+
+ static const double Tolerance; // 0.00001f;
+ static const double SingularityRadius; //0.00000000001 for Gimbal lock numerical problems
+};
+
+typedef Math<float> Mathf;
+typedef Math<double> Mathd;
+
+// Conversion functions between degrees and radians
+template<class FT>
+FT RadToDegree(FT rads) { return rads * Math<FT>::RadToDegreeFactor; }
+template<class FT>
+FT DegreeToRad(FT rads) { return rads * Math<FT>::DegreeToRadFactor; }
+
+template<class T>
+class Quat;
+
+//-------------------------------------------------------------------------------------
+// ***** Vector2f - 2D Vector2f
+
+// Vector2f represents a 2-dimensional vector or point in space,
+// consisting of coordinates x and y,
+
+template<class T>
+class Vector2
+{
+public:
+ T x, y;
+
+ Vector2() : x(0), y(0) { }
+ Vector2(T x_, T y_) : x(x_), y(y_) { }
+ explicit Vector2(T s) : x(s), y(s) { }
+
+ bool operator== (const Vector2& b) const { return x == b.x && y == b.y; }
+ bool operator!= (const Vector2& b) const { return x != b.x || y != b.y; }
+
+ Vector2 operator+ (const Vector2& b) const { return Vector2(x + b.x, y + b.y); }
+ Vector2& operator+= (const Vector2& b) { x += b.x; y += b.y; return *this; }
+ Vector2 operator- (const Vector2& b) const { return Vector2(x - b.x, y - b.y); }
+ Vector2& operator-= (const Vector2& b) { x -= b.x; y -= b.y; return *this; }
+ Vector2 operator- () const { return Vector2(-x, -y); }
+
+ // Scalar multiplication/division scales vector.
+ Vector2 operator* (T s) const { return Vector2(x*s, y*s); }
+ Vector2& operator*= (T s) { x *= s; y *= s; return *this; }
+
+ Vector2 operator/ (T s) const { T rcp = T(1)/s;
+ return Vector2(x*rcp, y*rcp); }
+ Vector2& operator/= (T s) { T rcp = T(1)/s;
+ x *= rcp; y *= rcp;
+ return *this; }
+
+ // Compare two vectors for equality with tolerance. Returns true if vectors match withing tolerance.
+ bool Compare(const Vector2&b, T tolerance = Mathf::Tolerance)
+ {
+ return (fabs(b.x-x) < tolerance) && (fabs(b.y-y) < tolerance);
+ }
+
+ // Dot product overload.
+ // Used to calculate angle q between two vectors among other things,
+ // as (A dot B) = |a||b|cos(q).
+ T operator* (const Vector2& b) const { return x*b.x + y*b.y; }
+
+ // Returns the angle from this vector to b, in radians.
+ T Angle(const Vector2& b) const { return acos((*this * b)/(Length()*b.Length())); }
+
+ // Return Length of the vector squared.
+ T LengthSq() const { return (x * x + y * y); }
+ // Return vector length.
+ T Length() const { return sqrt(LengthSq()); }
+
+ // Returns distance between two points represented by vectors.
+ T Distance(Vector2& b) const { return (*this - b).Length(); }
+
+ // Determine if this a unit vector.
+ bool IsNormalized() const { return fabs(LengthSq() - T(1)) < Math<T>::Tolerance; }
+ // Normalize, convention vector length to 1.
+ void Normalize() { *this /= Length(); }
+ // Returns normalized (unit) version of the vector without modifying itself.
+ Vector2 Normalized() const { return *this / Length(); }
+
+ // Linearly interpolates from this vector to another.
+ // Factor should be between 0.0 and 1.0, with 0 giving full value to this.
+ Vector2 Lerp(const Vector2& b, T f) const { return *this*(T(1) - f) + b*f; }
+
+ // Projects this vector onto the argument; in other words,
+ // A.Project(B) returns projection of vector A onto B.
+ Vector2 ProjectTo(const Vector2& b) const { return b * ((*this * b) / b.LengthSq()); }
+};
+
+
+typedef Vector2<float> Vector2f;
+typedef Vector2<double> Vector2d;
+
+//-------------------------------------------------------------------------------------
+// ***** Vector3f - 3D Vector3f
+
+// Vector3f represents a 3-dimensional vector or point in space,
+// consisting of coordinates x, y and z.
+
+template<class T>
+class Vector3
+{
+public:
+ T x, y, z;
+
+ Vector3() : x(0), y(0), z(0) { }
+ Vector3(T x_, T y_, T z_ = 0) : x(x_), y(y_), z(z_) { }
+ explicit Vector3(T s) : x(s), y(s), z(s) { }
+
+ bool operator== (const Vector3& b) const { return x == b.x && y == b.y && z == b.z; }
+ bool operator!= (const Vector3& b) const { return x != b.x || y != b.y || z != b.z; }
+
+ Vector3 operator+ (const Vector3& b) const { return Vector3(x + b.x, y + b.y, z + b.z); }
+ Vector3& operator+= (const Vector3& b) { x += b.x; y += b.y; z += b.z; return *this; }
+ Vector3 operator- (const Vector3& b) const { return Vector3(x - b.x, y - b.y, z - b.z); }
+ Vector3& operator-= (const Vector3& b) { x -= b.x; y -= b.y; z -= b.z; return *this; }
+ Vector3 operator- () const { return Vector3(-x, -y, -z); }
+
+ // Scalar multiplication/division scales vector.
+ Vector3 operator* (T s) const { return Vector3(x*s, y*s, z*s); }
+ Vector3& operator*= (T s) { x *= s; y *= s; z *= s; return *this; }
+
+ Vector3 operator/ (T s) const { T rcp = T(1)/s;
+ return Vector3(x*rcp, y*rcp, z*rcp); }
+ Vector3& operator/= (T s) { T rcp = T(1)/s;
+ x *= rcp; y *= rcp; z *= rcp;
+ return *this; }
+
+ // Compare two vectors for equality with tolerance. Returns true if vectors match withing tolerance.
+ bool Compare(const Vector3&b, T tolerance = Mathf::Tolerance)
+ {
+ return (fabs(b.x-x) < tolerance) && (fabs(b.y-y) < tolerance) && (fabs(b.z-z) < tolerance);
+ }
+
+ // Dot product overload.
+ // Used to calculate angle q between two vectors among other things,
+ // as (A dot B) = |a||b|cos(q).
+ T operator* (const Vector3& b) const { return x*b.x + y*b.y + z*b.z; }
+
+ // Compute cross product, which generates a normal vector.
+ // Direction vector can be determined by right-hand rule: Pointing index finder in
+ // direction a and middle finger in direction b, thumb will point in a.Cross(b).
+ Vector3 Cross(const Vector3& b) const { return Vector3(y*b.z - z*b.y,
+ z*b.x - x*b.z,
+ x*b.y - y*b.x); }
+
+ // Returns the angle from this vector to b, in radians.
+ T Angle(const Vector3& b) const { return acos((*this * b)/(Length()*b.Length())); }
+
+ // Return Length of the vector squared.
+ T LengthSq() const { return (x * x + y * y + z * z); }
+ // Return vector length.
+ T Length() const { return sqrt(LengthSq()); }
+
+ // Returns distance between two points represented by vectors.
+ T Distance(Vector3& b) const { return (*this - b).Length(); }
+
+ // Determine if this a unit vector.
+ bool IsNormalized() const { return fabs(LengthSq() - T(1)) < Math<T>::Tolerance; }
+ // Normalize, convention vector length to 1.
+ void Normalize() { *this /= Length(); }
+ // Returns normalized (unit) version of the vector without modifying itself.
+ Vector3 Normalized() const { return *this / Length(); }
+
+ // Linearly interpolates from this vector to another.
+ // Factor should be between 0.0 and 1.0, with 0 giving full value to this.
+ Vector3 Lerp(const Vector3& b, T f) const { return *this*(T(1) - f) + b*f; }
+
+ // Projects this vector onto the argument; in other words,
+ // A.Project(B) returns projection of vector A onto B.
+ Vector3 ProjectTo(const Vector3& b) const { return b * ((*this * b) / b.LengthSq()); }
+};
+
+
+typedef Vector3<float> Vector3f;
+typedef Vector3<double> Vector3d;
+
+
+//-------------------------------------------------------------------------------------
+// ***** Matrix4f
+
+// Matrix4f is a 4x4 matrix used for 3d transformations and projections.
+// Translation stored in the last column.
+// The matrix is stored in row-major order in memory, meaning that values
+// of the first row are stored before the next one.
+//
+// The arrangement of the matrix is chosen to be in Right-Handed
+// coordinate system and counterclockwise rotations when looking down
+// the axis
+//
+// Transformation Order:
+// - Transformations are applied from right to left, so the expression
+// M1 * M2 * M3 * V means that the vector V is transformed by M3 first,
+// followed by M2 and M1.
+//
+// Coordinate system: Right Handed
+//
+// Rotations: Counterclockwise when looking down the axis. All angles are in radians.
+//
+// | sx 01 02 tx | // First column (sx, 10, 20): Axis X basis vector.
+// | 10 sy 12 ty | // Second column (01, sy, 21): Axis Y basis vector.
+// | 20 21 sz tz | // Third columnt (02, 12, sz): Axis Z basis vector.
+// | 30 31 32 33 |
+//
+// The basis vectors are first three columns.
+
+class Matrix4f
+{
+ static Matrix4f IdentityValue;
+
+public:
+ float M[4][4];
+
+ enum NoInitType { NoInit };
+
+ // Construct with no memory initialization.
+ Matrix4f(NoInitType) { }
+
+ // By default, we construct identity matrix.
+ Matrix4f()
+ {
+ SetIdentity();
+ }
+
+ Matrix4f(float m11, float m12, float m13, float m14,
+ float m21, float m22, float m23, float m24,
+ float m31, float m32, float m33, float m34,
+ float m41, float m42, float m43, float m44)
+ {
+ M[0][0] = m11; M[0][1] = m12; M[0][2] = m13; M[0][3] = m14;
+ M[1][0] = m21; M[1][1] = m22; M[1][2] = m23; M[1][3] = m24;
+ M[2][0] = m31; M[2][1] = m32; M[2][2] = m33; M[2][3] = m34;
+ M[3][0] = m41; M[3][1] = m42; M[3][2] = m43; M[3][3] = m44;
+ }
+
+ Matrix4f(float m11, float m12, float m13,
+ float m21, float m22, float m23,
+ float m31, float m32, float m33)
+ {
+ M[0][0] = m11; M[0][1] = m12; M[0][2] = m13; M[0][3] = 0;
+ M[1][0] = m21; M[1][1] = m22; M[1][2] = m23; M[1][3] = 0;
+ M[2][0] = m31; M[2][1] = m32; M[2][2] = m33; M[2][3] = 0;
+ M[3][0] = 0; M[3][1] = 0; M[3][2] = 0; M[3][3] = 1;
+ }
+
+ static const Matrix4f& Identity() { return IdentityValue; }
+
+ void SetIdentity()
+ {
+ M[0][0] = M[1][1] = M[2][2] = M[3][3] = 1;
+ M[0][1] = M[1][0] = M[2][3] = M[3][1] = 0;
+ M[0][2] = M[1][2] = M[2][0] = M[3][2] = 0;
+ M[0][3] = M[1][3] = M[2][1] = M[3][0] = 0;
+ }
+
+ // Multiplies two matrices into destination with minimum copying.
+ static Matrix4f& Multiply(Matrix4f* d, const Matrix4f& a, const Matrix4f& b)
+ {
+ OVR_ASSERT((d != &a) && (d != &b));
+ int i = 0;
+ do {
+ d->M[i][0] = a.M[i][0] * b.M[0][0] + a.M[i][1] * b.M[1][0] + a.M[i][2] * b.M[2][0] + a.M[i][3] * b.M[3][0];
+ d->M[i][1] = a.M[i][0] * b.M[0][1] + a.M[i][1] * b.M[1][1] + a.M[i][2] * b.M[2][1] + a.M[i][3] * b.M[3][1];
+ d->M[i][2] = a.M[i][0] * b.M[0][2] + a.M[i][1] * b.M[1][2] + a.M[i][2] * b.M[2][2] + a.M[i][3] * b.M[3][2];
+ d->M[i][3] = a.M[i][0] * b.M[0][3] + a.M[i][1] * b.M[1][3] + a.M[i][2] * b.M[2][3] + a.M[i][3] * b.M[3][3];
+ } while((++i) < 4);
+
+ return *d;
+ }
+
+ Matrix4f operator* (const Matrix4f& b) const
+ {
+ Matrix4f result(Matrix4f::NoInit);
+ Multiply(&result, *this, b);
+ return result;
+ }
+
+ Matrix4f& operator*= (const Matrix4f& b)
+ {
+ return Multiply(this, Matrix4f(*this), b);
+ }
+
+ Matrix4f operator* (float s) const
+ {
+ return Matrix4f(M[0][0] * s, M[0][1] * s, M[0][2] * s, M[0][3] * s,
+ M[1][0] * s, M[1][1] * s, M[1][2] * s, M[1][3] * s,
+ M[2][0] * s, M[2][1] * s, M[2][2] * s, M[2][3] * s,
+ M[3][0] * s, M[3][1] * s, M[3][2] * s, M[3][3] * s);
+ }
+
+ Matrix4f& operator*= (float s)
+ {
+ M[0][0] *= s; M[0][1] *= s; M[0][2] *= s; M[0][3] *= s;
+ M[1][0] *= s; M[1][1] *= s; M[1][2] *= s; M[1][3] *= s;
+ M[2][0] *= s; M[2][1] *= s; M[2][2] *= s; M[2][3] *= s;
+ M[3][0] *= s; M[3][1] *= s; M[3][2] *= s; M[3][3] *= s;
+ return *this;
+ }
+
+ Vector3f Transform(const Vector3f& v) const
+ {
+ return Vector3f(M[0][0] * v.x + M[0][1] * v.y + M[0][2] * v.z + M[0][3],
+ M[1][0] * v.x + M[1][1] * v.y + M[1][2] * v.z + M[1][3],
+ M[2][0] * v.x + M[2][1] * v.y + M[2][2] * v.z + M[2][3]);
+ }
+
+ Matrix4f Transposed() const
+ {
+ return Matrix4f(M[0][0], M[1][0], M[2][0], M[3][0],
+ M[0][1], M[1][1], M[2][1], M[3][1],
+ M[0][2], M[1][2], M[2][2], M[3][2],
+ M[0][3], M[1][3], M[2][3], M[3][3]);
+ }
+
+ void Transpose()
+ {
+ *this = Transposed();
+ }
+
+
+ float SubDet (const int* rows, const int* cols) const
+ {
+ return M[rows[0]][cols[0]] * (M[rows[1]][cols[1]] * M[rows[2]][cols[2]] - M[rows[1]][cols[2]] * M[rows[2]][cols[1]])
+ - M[rows[0]][cols[1]] * (M[rows[1]][cols[0]] * M[rows[2]][cols[2]] - M[rows[1]][cols[2]] * M[rows[2]][cols[0]])
+ + M[rows[0]][cols[2]] * (M[rows[1]][cols[0]] * M[rows[2]][cols[1]] - M[rows[1]][cols[1]] * M[rows[2]][cols[0]]);
+ }
+
+ float Cofactor(int I, int J) const
+ {
+ const int indices[4][3] = {{1,2,3},{0,2,3},{0,1,3},{0,1,2}};
+ return ((I+J)&1) ? -SubDet(indices[I],indices[J]) : SubDet(indices[I],indices[J]);
+ }
+
+ float Determinant() const
+ {
+ return M[0][0] * Cofactor(0,0) + M[0][1] * Cofactor(0,1) + M[0][2] * Cofactor(0,2) + M[0][3] * Cofactor(0,3);
+ }
+
+ Matrix4f Adjugated() const
+ {
+ return Matrix4f(Cofactor(0,0), Cofactor(1,0), Cofactor(2,0), Cofactor(3,0),
+ Cofactor(0,1), Cofactor(1,1), Cofactor(2,1), Cofactor(3,1),
+ Cofactor(0,2), Cofactor(1,2), Cofactor(2,2), Cofactor(3,2),
+ Cofactor(0,3), Cofactor(1,3), Cofactor(2,3), Cofactor(3,3));
+ }
+
+ Matrix4f Inverted() const
+ {
+ float det = Determinant();
+ assert(det != 0);
+ return Adjugated() * (1.0f/det);
+ }
+
+ void Invert()
+ {
+ *this = Inverted();
+ }
+
+ //AnnaSteve:
+ // a,b,c, are the YawPitchRoll angles to be returned
+ // rotation a around axis A1
+ // is followed by rotation b around axis A2
+ // is followed by rotation c around axis A3
+ // rotations are CCW or CW (D) in LH or RH coordinate system (S)
+ template <Axis A1, Axis A2, Axis A3, RotateDirection D, HandedSystem S>
+ void ToEulerAngles(float *a, float *b, float *c)
+ {
+ OVR_COMPILER_ASSERT((A1 != A2) && (A2 != A3) && (A1 != A3));
+
+ float psign = -1.0f;
+ if (((A1 + 1) % 3 == A2) && ((A2 + 1) % 3 == A3)) // Determine whether even permutation
+ psign = 1.0f;
+
+ float pm = psign*M[A1][A3];
+ if (pm < -1.0f + Math<float>::SingularityRadius)
+ { // South pole singularity
+ *a = 0.0f;
+ *b = -S*D*Math<float>::PiOver2;
+ *c = S*D*atan2( psign*M[A2][A1], M[A2][A2] );
+ }
+ else if (pm > 1.0 - Math<float>::SingularityRadius)
+ { // North pole singularity
+ *a = 0.0f;
+ *b = S*D*Math<float>::PiOver2;
+ *c = S*D*atan2( psign*M[A2][A1], M[A2][A2] );
+ }
+ else
+ { // Normal case (nonsingular)
+ *a = S*D*atan2( -psign*M[A2][A3], M[A3][A3] );
+ *b = S*D*asin(pm);
+ *c = S*D*atan2( -psign*M[A1][A2], M[A1][A1] );
+ }
+
+ return;
+ }
+
+ //AnnaSteve:
+ // a,b,c, are the YawPitchRoll angles to be returned
+ // rotation a around axis A1
+ // is followed by rotation b around axis A2
+ // is followed by rotation c around axis A1
+ // rotations are CCW or CW (D) in LH or RH coordinate system (S)
+ template <Axis A1, Axis A2, RotateDirection D, HandedSystem S>
+ void ToEulerAnglesABA(float *a, float *b, float *c)
+ {
+ OVR_COMPILER_ASSERT(A1 != A2);
+
+ // Determine the axis that was not supplied
+ int m = 3 - A1 - A2;
+
+ float psign = -1.0f;
+ if ((A1 + 1) % 3 == A2) // Determine whether even permutation
+ psign = 1.0f;
+
+ float c2 = M[A1][A1];
+ if (c2 < -1.0 + Math<float>::SingularityRadius)
+ { // South pole singularity
+ *a = 0.0f;
+ *b = S*D*Math<float>::Pi;
+ *c = S*D*atan2( -psign*M[A2][m],M[A2][A2]);
+ }
+ else if (c2 > 1.0 - Math<float>::SingularityRadius)
+ { // North pole singularity
+ *a = 0.0f;
+ *b = 0.0f;
+ *c = S*D*atan2( -psign*M[A2][m],M[A2][A2]);
+ }
+ else
+ { // Normal case (nonsingular)
+ *a = S*D*atan2( M[A2][A1],-psign*M[m][A1]);
+ *b = S*D*acos(c2);
+ *c = S*D*atan2( M[A1][A2],psign*M[A1][m]);
+ }
+ return;
+ }
+
+ // Creates a matrix that converts the vertices from one coordinate system
+ // to another.
+ //
+ static Matrix4f AxisConversion(const WorldAxes& to, const WorldAxes& from)
+ {
+ // Holds axis values from the 'to' structure
+ int toArray[3] = { to.XAxis, to.YAxis, to.ZAxis };
+
+ // The inverse of the toArray
+ int inv[4];
+ inv[0] = inv[abs(to.XAxis)] = 0;
+ inv[abs(to.YAxis)] = 1;
+ inv[abs(to.ZAxis)] = 2;
+
+ Matrix4f m(0, 0, 0,
+ 0, 0, 0,
+ 0, 0, 0);
+
+ // Only three values in the matrix need to be changed to 1 or -1.
+ m.M[inv[abs(from.XAxis)]][0] = float(from.XAxis/toArray[inv[abs(from.XAxis)]]);
+ m.M[inv[abs(from.YAxis)]][1] = float(from.YAxis/toArray[inv[abs(from.YAxis)]]);
+ m.M[inv[abs(from.ZAxis)]][2] = float(from.ZAxis/toArray[inv[abs(from.ZAxis)]]);
+ return m;
+ }
+
+
+
+ static Matrix4f Translation(const Vector3f& v)
+ {
+ Matrix4f t;
+ t.M[0][3] = v.x;
+ t.M[1][3] = v.y;
+ t.M[2][3] = v.z;
+ return t;
+ }
+
+ static Matrix4f Translation(float x, float y, float z = 0.0f)
+ {
+ Matrix4f t;
+ t.M[0][3] = x;
+ t.M[1][3] = y;
+ t.M[2][3] = z;
+ return t;
+ }
+
+ static Matrix4f Scaling(const Vector3f& v)
+ {
+ Matrix4f t;
+ t.M[0][0] = v.x;
+ t.M[1][1] = v.y;
+ t.M[2][2] = v.z;
+ return t;
+ }
+
+ static Matrix4f Scaling(float x, float y, float z)
+ {
+ Matrix4f t;
+ t.M[0][0] = x;
+ t.M[1][1] = y;
+ t.M[2][2] = z;
+ return t;
+ }
+
+ static Matrix4f Scaling(float s)
+ {
+ Matrix4f t;
+ t.M[0][0] = s;
+ t.M[1][1] = s;
+ t.M[2][2] = s;
+ return t;
+ }
+
+
+
+ //AnnaSteve : Just for quick testing. Not for final API. Need to remove case.
+ static Matrix4f RotationAxis(Axis A, float angle, RotateDirection d, HandedSystem s)
+ {
+ float sina = s * d *sin(angle);
+ float cosa = cos(angle);
+
+ switch(A)
+ {
+ case Axis_X:
+ return Matrix4f(1, 0, 0,
+ 0, cosa, -sina,
+ 0, sina, cosa);
+ case Axis_Y:
+ return Matrix4f(cosa, 0, sina,
+ 0, 1, 0,
+ -sina, 0, cosa);
+ case Axis_Z:
+ return Matrix4f(cosa, -sina, 0,
+ sina, cosa, 0,
+ 0, 0, 1);
+ }
+ }
+
+
+ // Creates a rotation matrix rotating around the X axis by 'angle' radians.
+ // Rotation direction is depends on the coordinate system:
+ // RHS (Oculus default): Positive angle values rotate Counter-clockwise (CCW),
+ // while looking in the negative axis direction. This is the
+ // same as looking down from positive axis values towards origin.
+ // LHS: Positive angle values rotate clock-wise (CW), while looking in the
+ // negative axis direction.
+ static Matrix4f RotationX(float angle)
+ {
+ float sina = sin(angle);
+ float cosa = cos(angle);
+ return Matrix4f(1, 0, 0,
+ 0, cosa, -sina,
+ 0, sina, cosa);
+ }
+
+ // Creates a rotation matrix rotating around the Y axis by 'angle' radians.
+ // Rotation direction is depends on the coordinate system:
+ // RHS (Oculus default): Positive angle values rotate Counter-clockwise (CCW),
+ // while looking in the negative axis direction. This is the
+ // same as looking down from positive axis values towards origin.
+ // LHS: Positive angle values rotate clock-wise (CW), while looking in the
+ // negative axis direction.
+ static Matrix4f RotationY(float angle)
+ {
+ float sina = sin(angle);
+ float cosa = cos(angle);
+ return Matrix4f(cosa, 0, sina,
+ 0, 1, 0,
+ -sina, 0, cosa);
+ }
+
+ // Creates a rotation matrix rotating around the Z axis by 'angle' radians.
+ // Rotation direction is depends on the coordinate system:
+ // RHS (Oculus default): Positive angle values rotate Counter-clockwise (CCW),
+ // while looking in the negative axis direction. This is the
+ // same as looking down from positive axis values towards origin.
+ // LHS: Positive angle values rotate clock-wise (CW), while looking in the
+ // negative axis direction.
+ static Matrix4f RotationZ(float angle)
+ {
+ float sina = sin(angle);
+ float cosa = cos(angle);
+ return Matrix4f(cosa, -sina, 0,
+ sina, cosa, 0,
+ 0, 0, 1);
+ }
+
+
+ // LookAtRH creates a View transformation matrix for right-handed coordinate system.
+ // The resulting matrix points camera from 'eye' towards 'at' direction, with 'up'
+ // specifying the up vector. The resulting matrix should be used with PerspectiveRH
+ // projection.
+ static Matrix4f LookAtRH(const Vector3f& eye, const Vector3f& at, const Vector3f& up);
+
+ // LookAtLH creates a View transformation matrix for left-handed coordinate system.
+ // The resulting matrix points camera from 'eye' towards 'at' direction, with 'up'
+ // specifying the up vector.
+ static Matrix4f LookAtLH(const Vector3f& eye, const Vector3f& at, const Vector3f& up);
+
+
+ // PerspectiveRH creates a right-handed perspective projection matrix that can be
+ // used with the Oculus sample renderer.
+ // yfov - Specifies vertical field of view in radians.
+ // aspect - Screen aspect ration, which is usually width/height for square pixels.
+ // Note that xfov = yfov * aspect.
+ // znear - Absolute value of near Z clipping clipping range.
+ // zfar - Absolute value of far Z clipping clipping range (larger then near).
+ // Even though RHS usually looks in the direction of negative Z, positive values
+ // are expected for znear and zfar.
+ static Matrix4f PerspectiveRH(float yfov, float aspect, float znear, float zfar);
+
+
+ // PerspectiveRH creates a left-handed perspective projection matrix that can be
+ // used with the Oculus sample renderer.
+ // yfov - Specifies vertical field of view in radians.
+ // aspect - Screen aspect ration, which is usually width/height for square pixels.
+ // Note that xfov = yfov * aspect.
+ // znear - Absolute value of near Z clipping clipping range.
+ // zfar - Absolute value of far Z clipping clipping range (larger then near).
+ static Matrix4f PerspectiveLH(float yfov, float aspect, float znear, float zfar);
+
+
+ static Matrix4f Ortho2D(float w, float h);
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** Quat
+
+// Quatf represents a quaternion class used for rotations.
+//
+// Quaternion multiplications are done in right-to-left order, to match the
+// behavior of matrices.
+
+
+template<class T>
+class Quat
+{
+public:
+ // w + Xi + Yj + Zk
+ T x, y, z, w;
+
+ Quat() : x(0), y(0), z(0), w(1) {}
+ Quat(T x_, T y_, T z_, T w_) : x(x_), y(y_), z(z_), w(w_) {}
+
+
+ // Constructs rotation quaternion around the axis.
+ Quat(const Vector3<T>& axis, T angle)
+ {
+ Vector3<T> unitAxis = axis.Normalized();
+ T sinHalfAngle = sin(angle * T(0.5));
+
+ w = cos(angle * T(0.5));
+ x = unitAxis.x * sinHalfAngle;
+ y = unitAxis.y * sinHalfAngle;
+ z = unitAxis.z * sinHalfAngle;
+ }
+
+ //AnnaSteve:
+ void AxisAngle(Axis A, T angle, RotateDirection d, HandedSystem s)
+ {
+ T sinHalfAngle = s * d *sin(angle * (T)0.5);
+ T v[3];
+ v[0] = v[1] = v[2] = (T)0;
+ v[A] = sinHalfAngle;
+ //return Quat(v[0], v[1], v[2], cos(angle * (T)0.5));
+ w = cos(angle * (T)0.5);
+ x = v[0];
+ y = v[1];
+ z = v[2];
+ }
+
+
+ void GetAxisAngle(Vector3<T>* axis, T* angle) const
+ {
+ if (LengthSq() > Math<T>::Tolerance * Math<T>::Tolerance)
+ {
+ *axis = Vector3<T>(x, y, z).Normalized();
+ *angle = 2 * acos(w);
+ }
+ else
+ {
+ *axis = Vector3<T>(1, 0, 0);
+ *angle= 0;
+ }
+ }
+
+ bool operator== (const Quat& b) const { return x == b.x && y == b.y && z == b.z && w == b.w; }
+ bool operator!= (const Quat& b) const { return x != b.x || y != b.y || z != b.z || w != b.w; }
+
+ Quat operator+ (const Quat& b) const { return Quat(x + b.x, y + b.y, z + b.z, w + b.w); }
+ Quat& operator+= (const Quat& b) { w += b.w; x += b.x; y += b.y; z += b.z; return *this; }
+ Quat operator- (const Quat& b) const { return Quat(x - b.x, y - b.y, z - b.z, w - b.w); }
+ Quat& operator-= (const Quat& b) { w -= b.w; x -= b.x; y -= b.y; z -= b.z; return *this; }
+
+ Quat operator* (T s) const { return Quat(x * s, y * s, z * s, w * s); }
+ Quat& operator*= (T s) { w *= s; x *= s; y *= s; z *= s; return *this; }
+ Quat operator/ (T s) const { T rcp = T(1)/s; return Quat(x * rcp, y * rcp, z * rcp, w *rcp); }
+ Quat& operator/= (T s) { T rcp = T(1)/s; w *= rcp; x *= rcp; y *= rcp; z *= rcp; return *this; }
+
+ // Get Imaginary part vector
+ Vector3<T> Imag() const { return Vector3<T>(x,y,z); }
+
+ // Get quaternion length.
+ T Length() const { return sqrt(x * x + y * y + z * z + w * w); }
+ // Get quaternion length squared.
+ T LengthSq() const { return (x * x + y * y + z * z + w * w); }
+ // Simple Eulidean distance in R^4 (not SLERP distance, but at least respects Haar measure)
+ T Distance(const Quat& q) const
+ {
+ T d1 = (*this - q).Length();
+ T d2 = (*this + q).Length(); // Antipoldal point check
+ return (d1 < d2) ? d1 : d2;
+ }
+ T DistanceSq(const Quat& q) const
+ {
+ T d1 = (*this - q).LengthSq();
+ T d2 = (*this + q).LengthSq(); // Antipoldal point check
+ return (d1 < d2) ? d1 : d2;
+ }
+
+ // Normalize
+ bool IsNormalized() const { return fabs(LengthSq() - 1) < Math<T>::Tolerance; }
+ void Normalize() { *this /= Length(); }
+ Quat Normalized() const { return *this / Length(); }
+
+ // Returns conjugate of the quaternion. Produces inverse rotation if quaternion is normalized.
+ Quat Conj() const { return Quat(-x, -y, -z, w); }
+
+ // AnnaSteve fixed: order of quaternion multiplication
+ // Quaternion multiplication. Combines quaternion rotations, performing the one on the
+ // right hand side first.
+ Quat operator* (const Quat& b) const { return Quat(w * b.x + x * b.w + y * b.z - z * b.y,
+ w * b.y - x * b.z + y * b.w + z * b.x,
+ w * b.z + x * b.y - y * b.x + z * b.w,
+ w * b.w - x * b.x - y * b.y - z * b.z); }
+
+ //
+ // this^p normalized; same as rotating by this p times.
+ Quat PowNormalized(T p) const
+ {
+ Vector3<T> v;
+ T a;
+ GetAxisAngle(&v, &a);
+ return Quat(v, a * p);
+ }
+
+ // Rotate transforms vector in a manner that matches Matrix rotations (counter-clockwise,
+ // assuming negative direction of the axis). Standard formula: q(t) * V * q(t)^-1.
+ Vector3<T> Rotate(const Vector3<T>& v) const
+ {
+ return ((*this * Quat<T>(v.x, v.y, v.z, 0)) * Inverted()).Imag();
+ }
+
+
+ // Inversed quaternion rotates in the opposite direction.
+ Quat Inverted() const
+ {
+ return Quat(-x, -y, -z, w);
+ }
+
+ // Sets this quaternion to the one rotates in the opposite direction.
+ void Invert() const
+ {
+ *this = Quat(-x, -y, -z, w);
+ }
+
+ // Converting quaternion to matrix.
+ operator Matrix4f() const
+ {
+ T ww = w*w;
+ T xx = x*x;
+ T yy = y*y;
+ T zz = z*z;
+
+ return Matrix4f(float(ww + xx - yy - zz), float(T(2) * (x*y - w*z)), float(T(2) * (x*z + w*y)),
+ float(T(2) * (x*y + w*z)), float(ww - xx + yy - zz), float(T(2) * (y*z - w*x)),
+ float(T(2) * (x*z - w*y)), float(T(2) * (y*z + w*x)), float(ww - xx - yy + zz) );
+ }
+
+
+ // GetEulerAngles extracts Euler angles from the quaternion, in the specified order of
+ // axis rotations and the specified coordinate system. Right-handed coordinate system
+ // is the default, with CCW rotations while looking in the negative axis direction.
+ // Here a,b,c, are the Yaw/Pitch/Roll angles to be returned.
+ // rotation a around axis A1
+ // is followed by rotation b around axis A2
+ // is followed by rotation c around axis A3
+ // rotations are CCW or CW (D) in LH or RH coordinate system (S)
+ template <Axis A1, Axis A2, Axis A3, RotateDirection D, HandedSystem S>
+ void GetEulerAngles(T *a, T *b, T *c)
+ {
+ OVR_COMPILER_ASSERT((A1 != A2) && (A2 != A3) && (A1 != A3));
+
+ T Q[3] = { x, y, z }; //Quaternion components x,y,z
+
+ T ww = w*w;
+ T Q11 = Q[A1]*Q[A1];
+ T Q22 = Q[A2]*Q[A2];
+ T Q33 = Q[A3]*Q[A3];
+
+ T psign = T(-1.0);
+ // Determine whether even permutation
+ if (((A1 + 1) % 3 == A2) && ((A2 + 1) % 3 == A3))
+ psign = T(1.0);
+
+ T s2 = psign * T(2.0) * (psign*w*Q[A2] + Q[A1]*Q[A3]);
+
+ if (s2 < (T)-1.0 + Math<T>::SingularityRadius)
+ { // South pole singularity
+ *a = T(0.0);
+ *b = -S*D*Math<T>::PiOver2;
+ *c = S*D*atan2((T)2.0*(psign*Q[A1]*Q[A2] + w*Q[A3]),
+ ww + Q22 - Q11 - Q33 );
+ }
+ else if (s2 > (T)1.0 - Math<T>::SingularityRadius)
+ { // North pole singularity
+ *a = (T)0.0;
+ *b = S*D*Math<T>::PiOver2;
+ *c = S*D*atan2((T)2.0*(psign*Q[A1]*Q[A2] + w*Q[A3]),
+ ww + Q22 - Q11 - Q33);
+ }
+ else
+ {
+ *a = -S*D*atan2((T)-2.0*(w*Q[A1] - psign*Q[A2]*Q[A3]),
+ ww + Q33 - Q11 - Q22);
+ *b = S*D*asin(s2);
+ *c = S*D*atan2((T)2.0*(w*Q[A3] - psign*Q[A1]*Q[A2]),
+ ww + Q11 - Q22 - Q33);
+ }
+ return;
+ }
+
+ template <Axis A1, Axis A2, Axis A3, RotateDirection D>
+ void GetEulerAngles(T *a, T *b, T *c)
+ { GetEulerAngles<A1, A2, A3, D, Handed_R>(a, b, c); }
+
+ template <Axis A1, Axis A2, Axis A3>
+ void GetEulerAngles(T *a, T *b, T *c)
+ { GetEulerAngles<A1, A2, A3, Rotate_CCW, Handed_R>(a, b, c); }
+
+
+ // GetEulerAnglesABA extracts Euler angles from the quaternion, in the specified order of
+ // axis rotations and the specified coordinate system. Right-handed coordinate system
+ // is the default, with CCW rotations while looking in the negative axis direction.
+ // Here a,b,c, are the Yaw/Pitch/Roll angles to be returned.
+ // rotation a around axis A1
+ // is followed by rotation b around axis A2
+ // is followed by rotation c around axis A1
+ // Rotations are CCW or CW (D) in LH or RH coordinate system (S)
+ template <Axis A1, Axis A2, RotateDirection D, HandedSystem S>
+ void GetEulerAnglesABA(T *a, T *b, T *c)
+ {
+ OVR_COMPILER_ASSERT(A1 != A2);
+
+ T Q[3] = {x, y, z}; // Quaternion components
+
+ // Determine the missing axis that was not supplied
+ int m = 3 - A1 - A2;
+
+ T ww = w*w;
+ T Q11 = Q[A1]*Q[A1];
+ T Q22 = Q[A2]*Q[A2];
+ T Qmm = Q[m]*Q[m];
+
+ T psign = T(-1.0);
+ if ((A1 + 1) % 3 == A2) // Determine whether even permutation
+ {
+ psign = (T)1.0;
+ }
+
+ T c2 = ww + Q11 - Q22 - Qmm;
+ if (c2 < (T)-1.0 + Math<T>::SingularityRadius)
+ { // South pole singularity
+ *a = (T)0.0;
+ *b = S*D*Math<T>::Pi;
+ *c = S*D*atan2( (T)2.0*(w*Q[A1] - psign*Q[A2]*Q[m]),
+ ww + Q22 - Q11 - Qmm);
+ }
+ else if (c2 > (T)1.0 - Math<T>::SingularityRadius)
+ { // North pole singularity
+ *a = (T)0.0;
+ *b = (T)0.0;
+ *c = S*D*atan2( (T)2.0*(w*Q[A1] - psign*Q[A2]*Q[m]),
+ ww + Q22 - Q11 - Qmm);
+ }
+ else
+ {
+ *a = S*D*atan2( psign*w*Q[m] + Q[A1]*Q[A2],
+ w*Q[A2] -psign*Q[A1]*Q[m]);
+ *b = S*D*acos(c2);
+ *c = S*D*atan2( -psign*w*Q[m] + Q[A1]*Q[A2],
+ w*Q[A2] + psign*Q[A1]*Q[m]);
+ }
+ return;
+ }
+};
+
+
+typedef Quat<float> Quatf;
+typedef Quat<double> Quatd;
+
+//-------------------------------------------------------------------------------------
+// ***** Plane
+
+// Consists of a normal vector and distance from the origin where the plane is located.
+
+template<class T>
+class Plane : public RefCountBase<Plane<T> >
+{
+public:
+ Vector3<T> N;
+ T D;
+
+ Plane() : D(0) {}
+
+ // Normals must already be normalized
+ Plane(const Vector3<T>& n, T d) : N(n), D(d) {}
+ Plane(T x, T y, T z, T d) : N(x,y,z), D(d) {}
+
+ // construct from a point on the plane and the normal
+ Plane(const Vector3<T>& p, const Vector3<T>& n) : N(n), D(-(p * n)) {}
+
+ // Find the point to plane distance. The sign indicates what side of the plane the point is on (0 = point on plane).
+ T TestSide(const Vector3<T>& p) const
+ {
+ return (N * p) + D;
+ }
+
+ Plane<T> Flipped() const
+ {
+ return Plane(-N, -D);
+ }
+
+ void Flip()
+ {
+ N = -N;
+ D = -D;
+ }
+
+ bool operator==(const Plane<T>& rhs) const
+ {
+ return (this->D == rhs.D && this->N == rhs.N);
+ }
+};
+
+typedef Plane<float> Planef;
+
+}
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_RefCount.cpp b/LibOVR/Src/Kernel/OVR_RefCount.cpp
new file mode 100644
index 0000000..a1171fa
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_RefCount.cpp
@@ -0,0 +1,100 @@
+/************************************************************************************
+
+Filename : OVR_RefCount.cpp
+Content : Reference counting implementation
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#include "OVR_RefCount.h"
+#include "OVR_Atomic.h"
+#include "OVR_Log.h"
+
+namespace OVR {
+
+#ifdef OVR_CC_ARM
+void* ReturnArg0(void* p)
+{
+ return p;
+}
+#endif
+
+// ***** Reference Count Base implementation
+
+RefCountImplCore::~RefCountImplCore()
+{
+ // RefCount can be either 1 or 0 here.
+ // 0 if Release() was properly called.
+ // 1 if the object was declared on stack or as an aggregate.
+ OVR_ASSERT(RefCount <= 1);
+}
+
+#ifdef OVR_BUILD_DEBUG
+void RefCountImplCore::reportInvalidDelete(void *pmem)
+{
+ OVR_DEBUG_LOG(
+ ("Invalid delete call on ref-counted object at %p. Please use Release()", pmem));
+ OVR_ASSERT(0);
+}
+#endif
+
+RefCountNTSImplCore::~RefCountNTSImplCore()
+{
+ // RefCount can be either 1 or 0 here.
+ // 0 if Release() was properly called.
+ // 1 if the object was declared on stack or as an aggregate.
+ OVR_ASSERT(RefCount <= 1);
+}
+
+#ifdef OVR_BUILD_DEBUG
+void RefCountNTSImplCore::reportInvalidDelete(void *pmem)
+{
+ OVR_DEBUG_LOG(
+ ("Invalid delete call on ref-counted object at %p. Please use Release()", pmem));
+ OVR_ASSERT(0);
+}
+#endif
+
+
+// *** Thread-Safe RefCountImpl
+
+void RefCountImpl::AddRef()
+{
+ AtomicOps<int>::ExchangeAdd_NoSync(&RefCount, 1);
+}
+void RefCountImpl::Release()
+{
+ if ((AtomicOps<int>::ExchangeAdd_NoSync(&RefCount, -1) - 1) == 0)
+ delete this;
+}
+
+// *** Thread-Safe RefCountVImpl w/virtual AddRef/Release
+
+void RefCountVImpl::AddRef()
+{
+ AtomicOps<int>::ExchangeAdd_NoSync(&RefCount, 1);
+}
+void RefCountVImpl::Release()
+{
+ if ((AtomicOps<int>::ExchangeAdd_NoSync(&RefCount, -1) - 1) == 0)
+ delete this;
+}
+
+// *** NON-Thread-Safe RefCountImpl
+
+void RefCountNTSImpl::Release() const
+{
+ RefCount--;
+ if (RefCount == 0)
+ delete this;
+}
+
+
+} // OVR
diff --git a/LibOVR/Src/Kernel/OVR_RefCount.h b/LibOVR/Src/Kernel/OVR_RefCount.h
new file mode 100644
index 0000000..8622050
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_RefCount.h
@@ -0,0 +1,522 @@
+/************************************************************************************
+
+PublicHeader: Kernel
+Filename : OVR_RefCount.h
+Content : Reference counting implementation headers
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#ifndef OVR_RefCount_h
+#define OVR_RefCount_h
+
+#include "OVR_Types.h"
+#include "OVR_Allocator.h"
+
+namespace OVR {
+
+//-----------------------------------------------------------------------------------
+// ***** Reference Counting
+
+// There are three types of reference counting base classes:
+//
+// RefCountBase - Provides thread-safe reference counting (Default).
+// RefCountBaseNTS - Non Thread Safe version of reference counting.
+
+
+// ***** Declared classes
+
+template<class C>
+class RefCountBase;
+template<class C>
+class RefCountBaseNTS;
+
+class RefCountImpl;
+class RefCountNTSImpl;
+
+
+//-----------------------------------------------------------------------------------
+// ***** Implementation For Reference Counting
+
+// RefCountImplCore holds RefCount value and defines a few utility
+// functions shared by all implementations.
+
+class RefCountImplCore
+{
+protected:
+ volatile int RefCount;
+
+public:
+ // RefCountImpl constructor always initializes RefCount to 1 by default.
+ OVR_FORCE_INLINE RefCountImplCore() : RefCount(1) { }
+
+ // Need virtual destructor
+ // This: 1. Makes sure the right destructor's called.
+ // 2. Makes us have VTable, necessary if we are going to have format needed by InitNewMem()
+ virtual ~RefCountImplCore();
+
+ // Debug method only.
+ int GetRefCount() const { return RefCount; }
+
+ // This logic is used to detect invalid 'delete' calls of reference counted
+ // objects. Direct delete calls are not allowed on them unless they come in
+ // internally from Release.
+#ifdef OVR_BUILD_DEBUG
+ static void OVR_CDECL reportInvalidDelete(void *pmem);
+ inline static void checkInvalidDelete(RefCountImplCore *pmem)
+ {
+ if (pmem->RefCount != 0)
+ reportInvalidDelete(pmem);
+ }
+#else
+ inline static void checkInvalidDelete(RefCountImplCore *) { }
+#endif
+
+ // Base class ref-count content should not be copied.
+ void operator = (const RefCountImplCore &) { }
+};
+
+class RefCountNTSImplCore
+{
+protected:
+ mutable int RefCount;
+
+public:
+ // RefCountImpl constructor always initializes RefCount to 1 by default.
+ OVR_FORCE_INLINE RefCountNTSImplCore() : RefCount(1) { }
+
+ // Need virtual destructor
+ // This: 1. Makes sure the right destructor's called.
+ // 2. Makes us have VTable, necessary if we are going to have format needed by InitNewMem()
+ virtual ~RefCountNTSImplCore();
+
+ // Debug method only.
+ int GetRefCount() const { return RefCount; }
+
+ // This logic is used to detect invalid 'delete' calls of reference counted
+ // objects. Direct delete calls are not allowed on them unless they come in
+ // internally from Release.
+#ifdef OVR_BUILD_DEBUG
+ static void OVR_CDECL reportInvalidDelete(void *pmem);
+ OVR_FORCE_INLINE static void checkInvalidDelete(RefCountNTSImplCore *pmem)
+ {
+ if (pmem->RefCount != 0)
+ reportInvalidDelete(pmem);
+ }
+#else
+ OVR_FORCE_INLINE static void checkInvalidDelete(RefCountNTSImplCore *) { }
+#endif
+
+ // Base class ref-count content should not be copied.
+ void operator = (const RefCountNTSImplCore &) { }
+};
+
+
+
+// RefCountImpl provides Thread-Safe implementation of reference counting, so
+// it should be used by default in most places.
+
+class RefCountImpl : public RefCountImplCore
+{
+public:
+ // Thread-Safe Ref-Count Implementation.
+ void AddRef();
+ void Release();
+};
+
+// RefCountVImpl provides Thread-Safe implementation of reference counting, plus,
+// virtual AddRef and Release.
+
+class RefCountVImpl : public RefCountImplCore
+{
+public:
+ // Thread-Safe Ref-Count Implementation.
+ virtual void AddRef();
+ virtual void Release();
+};
+
+
+// RefCountImplNTS provides Non-Thread-Safe implementation of reference counting,
+// which is slightly more efficient since it doesn't use atomics.
+
+class RefCountNTSImpl : public RefCountNTSImplCore
+{
+public:
+ OVR_FORCE_INLINE void AddRef() const { RefCount++; }
+ void Release() const;
+};
+
+
+
+// RefCountBaseStatImpl<> is a common class that adds new/delete override with Stat tracking
+// to the reference counting implementation. Base must be one of the RefCountImpl classes.
+
+template<class Base>
+class RefCountBaseStatImpl : public Base
+{
+public:
+ RefCountBaseStatImpl() { }
+
+ // *** Override New and Delete
+
+ // DOM-IGNORE-BEGIN
+ // Undef new temporarily if it is being redefined
+#ifdef OVR_DEFINE_NEW
+#undef new
+#endif
+
+#ifdef OVR_BUILD_DEBUG
+ // Custom check used to detect incorrect calls of 'delete' on ref-counted objects.
+ #define OVR_REFCOUNTALLOC_CHECK_DELETE(class_name, p) \
+ do {if (p) Base::checkInvalidDelete((class_name*)p); } while(0)
+#else
+ #define OVR_REFCOUNTALLOC_CHECK_DELETE(class_name, p)
+#endif
+
+ // Redefine all new & delete operators.
+ OVR_MEMORY_REDEFINE_NEW_IMPL(Base, OVR_REFCOUNTALLOC_CHECK_DELETE)
+
+#ifdef OVR_DEFINE_NEW
+#define new OVR_DEFINE_NEW
+#endif
+ // OVR_BUILD_DEFINE_NEW
+ // DOM-IGNORE-END
+};
+
+
+
+//-----------------------------------------------------------------------------------
+// *** End user RefCountBase<> classes
+
+
+// RefCountBase is a base class for classes that require thread-safe reference
+// counting; it also overrides the new and delete operators to use MemoryHeap.
+//
+// Reference counted objects start out with RefCount value of 1. Further lifetime
+// management is done through the AddRef() and Release() methods, typically
+// hidden by Ptr<>.
+
+template<class C>
+class RefCountBase : public RefCountBaseStatImpl<RefCountImpl>
+{
+public:
+ // Constructor.
+ OVR_FORCE_INLINE RefCountBase() : RefCountBaseStatImpl<RefCountImpl>() { }
+};
+
+// RefCountBaseV is the same as RefCountBase but with virtual AddRef/Release
+
+template<class C>
+class RefCountBaseV : public RefCountBaseStatImpl<RefCountVImpl>
+{
+public:
+ // Constructor.
+ OVR_FORCE_INLINE RefCountBaseV() : RefCountBaseStatImpl<RefCountVImpl>() { }
+};
+
+
+// RefCountBaseNTS is a base class for classes that require Non-Thread-Safe reference
+// counting; it also overrides the new and delete operators to use MemoryHeap.
+// This class should only be used if all pointers to it are known to be assigned,
+// destroyed and manipulated within one thread.
+//
+// Reference counted objects start out with RefCount value of 1. Further lifetime
+// management is done through the AddRef() and Release() methods, typically
+// hidden by Ptr<>.
+
+template<class C>
+class RefCountBaseNTS : public RefCountBaseStatImpl<RefCountNTSImpl>
+{
+public:
+ // Constructor.
+ OVR_FORCE_INLINE RefCountBaseNTS() : RefCountBaseStatImpl<RefCountNTSImpl>() { }
+};
+
+//-----------------------------------------------------------------------------------
+// ***** Pickable template pointer
+enum PickType { PickValue };
+
+template <typename T>
+class Pickable
+{
+public:
+ Pickable() : pV(NULL) {}
+ explicit Pickable(T* p) : pV(p) {}
+ Pickable(T* p, PickType) : pV(p)
+ {
+ OVR_ASSERT(pV);
+ if (pV)
+ pV->AddRef();
+ }
+ template <typename OT>
+ Pickable(const Pickable<OT>& other) : pV(other.GetPtr()) {}
+
+public:
+ Pickable& operator =(const Pickable& other)
+ {
+ OVR_ASSERT(pV == NULL);
+ pV = other.pV;
+ // Extra check.
+ //other.pV = NULL;
+ return *this;
+ }
+
+public:
+ T* GetPtr() const { return pV; }
+ T* operator->() const
+ {
+ return pV;
+ }
+ T& operator*() const
+ {
+ OVR_ASSERT(pV);
+ return *pV;
+ }
+
+private:
+ T* pV;
+};
+
+template <typename T>
+OVR_FORCE_INLINE
+Pickable<T> MakePickable(T* p)
+{
+ return Pickable<T>(p);
+}
+
+//-----------------------------------------------------------------------------------
+// ***** Ref-Counted template pointer
+
+// Automatically AddRefs and Releases interfaces
+
+void* ReturnArg0(void* p);
+
+template<class C>
+class Ptr
+{
+#ifdef OVR_CC_ARM
+ static C* ReturnArg(void* p) { return (C*)ReturnArg0(p); }
+#endif
+
+protected:
+ C *pObject;
+
+public:
+
+ // Constructors
+ OVR_FORCE_INLINE Ptr() : pObject(0)
+ { }
+#ifdef OVR_CC_ARM
+ OVR_FORCE_INLINE Ptr(C &robj) : pObject(ReturnArg(&robj))
+#else
+ OVR_FORCE_INLINE Ptr(C &robj) : pObject(&robj)
+#endif
+ { }
+ OVR_FORCE_INLINE Ptr(Pickable<C> v) : pObject(v.GetPtr())
+ {
+ // No AddRef() on purpose.
+ }
+ OVR_FORCE_INLINE Ptr(Ptr<C>& other, PickType) : pObject(other.pObject)
+ {
+ other.pObject = NULL;
+ // No AddRef() on purpose.
+ }
+ OVR_FORCE_INLINE Ptr(C *pobj)
+ {
+ if (pobj) pobj->AddRef();
+ pObject = pobj;
+ }
+ OVR_FORCE_INLINE Ptr(const Ptr<C> &src)
+ {
+ if (src.pObject) src.pObject->AddRef();
+ pObject = src.pObject;
+ }
+
+ template<class R>
+ OVR_FORCE_INLINE Ptr(Ptr<R> &src)
+ {
+ if (src) src->AddRef();
+ pObject = src;
+ }
+ template<class R>
+ OVR_FORCE_INLINE Ptr(Pickable<R> v) : pObject(v.GetPtr())
+ {
+ // No AddRef() on purpose.
+ }
+
+ // Destructor
+ OVR_FORCE_INLINE ~Ptr()
+ {
+ if (pObject) pObject->Release();
+ }
+
+ // Compares
+ OVR_FORCE_INLINE bool operator == (const Ptr &other) const { return pObject == other.pObject; }
+ OVR_FORCE_INLINE bool operator != (const Ptr &other) const { return pObject != other.pObject; }
+
+ OVR_FORCE_INLINE bool operator == (C *pother) const { return pObject == pother; }
+ OVR_FORCE_INLINE bool operator != (C *pother) const { return pObject != pother; }
+
+
+ OVR_FORCE_INLINE bool operator < (const Ptr &other) const { return pObject < other.pObject; }
+
+ // Assignment
+ template<class R>
+ OVR_FORCE_INLINE const Ptr<C>& operator = (const Ptr<R> &src)
+ {
+ if (src) src->AddRef();
+ if (pObject) pObject->Release();
+ pObject = src;
+ return *this;
+ }
+ // Specialization
+ OVR_FORCE_INLINE const Ptr<C>& operator = (const Ptr<C> &src)
+ {
+ if (src) src->AddRef();
+ if (pObject) pObject->Release();
+ pObject = src;
+ return *this;
+ }
+
+ OVR_FORCE_INLINE const Ptr<C>& operator = (C *psrc)
+ {
+ if (psrc) psrc->AddRef();
+ if (pObject) pObject->Release();
+ pObject = psrc;
+ return *this;
+ }
+ OVR_FORCE_INLINE const Ptr<C>& operator = (C &src)
+ {
+ if (pObject) pObject->Release();
+ pObject = &src;
+ return *this;
+ }
+ OVR_FORCE_INLINE Ptr<C>& operator = (Pickable<C> src)
+ {
+ return Pick(src);
+ }
+ template<class R>
+ OVR_FORCE_INLINE Ptr<C>& operator = (Pickable<R> src)
+ {
+ return Pick(src);
+ }
+
+ // Set Assignment
+ template<class R>
+ OVR_FORCE_INLINE Ptr<C>& SetPtr(const Ptr<R> &src)
+ {
+ if (src) src->AddRef();
+ if (pObject) pObject->Release();
+ pObject = src;
+ return *this;
+ }
+ // Specialization
+ OVR_FORCE_INLINE Ptr<C>& SetPtr(const Ptr<C> &src)
+ {
+ if (src) src->AddRef();
+ if (pObject) pObject->Release();
+ pObject = src;
+ return *this;
+ }
+
+ OVR_FORCE_INLINE Ptr<C>& SetPtr(C *psrc)
+ {
+ if (psrc) psrc->AddRef();
+ if (pObject) pObject->Release();
+ pObject = psrc;
+ return *this;
+ }
+ OVR_FORCE_INLINE Ptr<C>& SetPtr(C &src)
+ {
+ if (pObject) pObject->Release();
+ pObject = &src;
+ return *this;
+ }
+ OVR_FORCE_INLINE Ptr<C>& SetPtr(Pickable<C> src)
+ {
+ return Pick(src);
+ }
+
+ // Nulls ref-counted pointer without decrement
+ OVR_FORCE_INLINE void NullWithoutRelease()
+ {
+ pObject = 0;
+ }
+
+ // Clears the pointer to the object
+ OVR_FORCE_INLINE void Clear()
+ {
+ if (pObject) pObject->Release();
+ pObject = 0;
+ }
+
+ // Obtain pointer reference directly, for D3D interfaces
+ OVR_FORCE_INLINE C*& GetRawRef() { return pObject; }
+
+ // Access Operators
+ OVR_FORCE_INLINE C* GetPtr() const { return pObject; }
+ OVR_FORCE_INLINE C& operator * () const { return *pObject; }
+ OVR_FORCE_INLINE C* operator -> () const { return pObject; }
+ // Conversion
+ OVR_FORCE_INLINE operator C* () const { return pObject; }
+
+ // Pickers.
+
+ // Pick a value.
+ OVR_FORCE_INLINE Ptr<C>& Pick(Ptr<C>& other)
+ {
+ if (&other != this)
+ {
+ if (pObject) pObject->Release();
+ pObject = other.pObject;
+ other.pObject = 0;
+ }
+
+ return *this;
+ }
+
+ OVR_FORCE_INLINE Ptr<C>& Pick(Pickable<C> v)
+ {
+ if (v.GetPtr() != pObject)
+ {
+ if (pObject) pObject->Release();
+ pObject = v.GetPtr();
+ }
+
+ return *this;
+ }
+
+ template<class R>
+ OVR_FORCE_INLINE Ptr<C>& Pick(Pickable<R> v)
+ {
+ if (v.GetPtr() != pObject)
+ {
+ if (pObject) pObject->Release();
+ pObject = v.GetPtr();
+ }
+
+ return *this;
+ }
+
+ OVR_FORCE_INLINE Ptr<C>& Pick(C* p)
+ {
+ if (p != pObject)
+ {
+ if (pObject) pObject->Release();
+ pObject = p;
+ }
+
+ return *this;
+ }
+};
+
+} // OVR
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_Std.cpp b/LibOVR/Src/Kernel/OVR_Std.cpp
new file mode 100644
index 0000000..088f9e9
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_Std.cpp
@@ -0,0 +1,1025 @@
+/************************************************************************************
+
+Filename : OVR_Std.cpp
+Content : Standard C function implementation
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#include "OVR_Std.h"
+#include "OVR_Alg.h"
+
+// localeconv() call in OVR_strtod()
+#include <locale.h>
+
+namespace OVR {
+
+// Source for functions not available on all platforms is included here.
+
+// Case insensitive compare implemented in platform-specific way.
+int OVR_CDECL OVR_stricmp(const char* a, const char* b)
+{
+#if defined(OVR_OS_WIN32)
+ #if defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400)
+ return ::_stricmp(a, b);
+ #else
+ return ::stricmp(a, b);
+ #endif
+
+#else
+ return strcasecmp(a, b);
+#endif
+}
+
+int OVR_CDECL OVR_strnicmp(const char* a, const char* b, UPInt count)
+{
+#if defined(OVR_OS_WIN32)
+#if defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400)
+ return ::_strnicmp(a, b, count);
+#else
+ return ::strnicmp(a, b, count);
+#endif
+
+#else
+ return strncasecmp(a, b, count);
+#endif
+}
+
+wchar_t* OVR_CDECL OVR_wcscpy(wchar_t* dest, UPInt destsize, const wchar_t* src)
+{
+#if defined(OVR_MSVC_SAFESTRING)
+ wcscpy_s(dest, destsize, src);
+ return dest;
+#elif defined(OVR_OS_WIN32)
+ OVR_UNUSED(destsize);
+ wcscpy(dest, src);
+ return dest;
+#else
+ UPInt l = OVR_wcslen(src) + 1; // incl term null
+ l = (l < destsize) ? l : destsize;
+ memcpy(dest, src, l * sizeof(wchar_t));
+ return dest;
+#endif
+}
+
+wchar_t* OVR_CDECL OVR_wcsncpy(wchar_t* dest, UPInt destsize, const wchar_t* src, UPInt count)
+{
+#if defined(OVR_MSVC_SAFESTRING)
+ wcsncpy_s(dest, destsize, src, count);
+ return dest;
+#else
+ UPInt srclen = OVR_wcslen(src);
+ UPInt l = Alg::Min(srclen, count);
+ l = (l < destsize) ? l : destsize;
+ memcpy(dest, src, l * sizeof(wchar_t));
+ if (count > srclen)
+ {
+ UPInt remLen = Alg::Min(destsize - l, (count - srclen));
+ memset(&dest[l], 0, sizeof(wchar_t)*remLen);
+ }
+ else if (l < destsize)
+ dest[l] = 0;
+ return dest;
+#endif
+}
+
+
+wchar_t* OVR_CDECL OVR_wcscat(wchar_t* dest, UPInt destsize, const wchar_t* src)
+{
+#if defined(OVR_MSVC_SAFESTRING)
+ wcscat_s(dest, destsize, src);
+ return dest;
+#elif defined(OVR_OS_WIN32)
+ OVR_UNUSED(destsize);
+ wcscat(dest, src);
+ return dest;
+#else
+ UPInt dstlen = OVR_wcslen(dest); // do not incl term null
+ UPInt srclen = OVR_wcslen(src) + 1; // incl term null
+ UPInt copylen = (dstlen + srclen < destsize) ? srclen : destsize - dstlen;
+ memcpy(dest + dstlen, src, copylen * sizeof(wchar_t));
+ return dest;
+#endif
+}
+
+UPInt OVR_CDECL OVR_wcslen(const wchar_t* str)
+{
+#if defined(OVR_OS_WIN32)
+ return wcslen(str);
+#else
+ UPInt i = 0;
+ while(str[i] != '\0')
+ ++i;
+ return i;
+#endif
+}
+
+int OVR_CDECL OVR_wcscmp(const wchar_t* a, const wchar_t* b)
+{
+#if defined(OVR_OS_WIN32) || defined(OVR_OS_LINUX)
+ return wcscmp(a, b);
+#else
+ // not supported, use custom implementation
+ const wchar_t *pa = a, *pb = b;
+ while (*pa && *pb)
+ {
+ wchar_t ca = *pa;
+ wchar_t cb = *pb;
+ if (ca < cb)
+ return -1;
+ else if (ca > cb)
+ return 1;
+ pa++;
+ pb++;
+ }
+ if (*pa)
+ return 1;
+ else if (*pb)
+ return -1;
+ else
+ return 0;
+#endif
+}
+
+int OVR_CDECL OVR_wcsicmp(const wchar_t* a, const wchar_t* b)
+{
+#if defined(OVR_OS_WIN32)
+#if defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400)
+ return ::_wcsicmp(a, b);
+#else
+ return ::wcsicmp(a, b);
+#endif
+#elif defined(OVR_OS_MAC) || defined(__CYGWIN__) || defined(OVR_OS_ANDROID) || defined(OVR_OS_IPHONE)
+ // not supported, use custom implementation
+ const wchar_t *pa = a, *pb = b;
+ while (*pa && *pb)
+ {
+ wchar_t ca = OVR_towlower(*pa);
+ wchar_t cb = OVR_towlower(*pb);
+ if (ca < cb)
+ return -1;
+ else if (ca > cb)
+ return 1;
+ pa++;
+ pb++;
+ }
+ if (*pa)
+ return 1;
+ else if (*pb)
+ return -1;
+ else
+ return 0;
+#else
+ return wcscasecmp(a, b);
+#endif
+}
+
+// This function is not inline because of dependency on <locale.h>
+double OVR_CDECL OVR_strtod(const char* string, char** tailptr)
+{
+#if !defined(OVR_OS_ANDROID)
+ const char s = *localeconv()->decimal_point;
+
+ if (s != '.')
+ {
+ char buffer[347 + 1];
+
+ OVR_strcpy(buffer, sizeof(buffer), string);
+
+ for (char* c = buffer; *c != '\0'; ++c)
+ {
+ if (*c == '.')
+ {
+ *c = s;
+ break;
+ }
+ }
+
+ return strtod(buffer, tailptr);
+ }
+#endif
+
+ return strtod(string, tailptr);
+}
+
+
+#ifndef OVR_NO_WCTYPE
+
+//// Use this class to generate Unicode bitsets. For example:
+////
+//// UnicodeBitSet bitSet;
+//// for(unsigned i = 0; i < 65536; ++i)
+//// {
+//// if (iswalpha(i))
+//// bitSet.Set(i);
+//// }
+//// bitSet.Dump();
+////
+////---------------------------------------------------------------
+//class UnicodeBitSet
+//{
+//public:
+// UnicodeBitSet()
+// {
+// memset(Offsets, 0, sizeof(Offsets));
+// memset(Bits, 0, sizeof(Bits));
+// }
+//
+// void Set(unsigned bit) { Bits[bit >> 8][(bit >> 4) & 15] |= 1 << (bit & 15); }
+//
+// void Dump()
+// {
+// unsigned i, j;
+// unsigned offsetCount = 0;
+// for(i = 0; i < 256; ++i)
+// {
+// if (isNull(i)) Offsets[i] = 0;
+// else
+// if (isFull(i)) Offsets[i] = 1;
+// else Offsets[i] = UInt16(offsetCount++ * 16 + 256);
+// }
+// for(i = 0; i < 16; ++i)
+// {
+// for(j = 0; j < 16; ++j)
+// {
+// printf("%5u,", Offsets[i*16+j]);
+// }
+// printf("\n");
+// }
+// for(i = 0; i < 256; ++i)
+// {
+// if (Offsets[i] > 255)
+// {
+// for(j = 0; j < 16; j++)
+// {
+// printf("%5u,", Bits[i][j]);
+// }
+// printf("\n");
+// }
+// }
+// }
+//
+//private:
+// bool isNull(unsigned n) const
+// {
+// const UInt16* p = Bits[n];
+// for(unsigned i = 0; i < 16; ++i)
+// if (p[i] != 0) return false;
+// return true;
+// }
+//
+// bool isFull(unsigned n) const
+// {
+// const UInt16* p = Bits[n];
+// for(unsigned i = 0; i < 16; ++i)
+// if (p[i] != 0xFFFF) return false;
+// return true;
+// }
+//
+// UInt16 Offsets[256];
+// UInt16 Bits[256][16];
+//};
+
+
+const UInt16 UnicodeAlnumBits[] = {
+ 256, 1, 272, 288, 304, 320, 336, 352, 0, 368, 384, 400, 416, 432, 448, 464,
+ 480, 496, 512, 528, 544, 1, 560, 576, 592, 0, 0, 0, 0, 0, 608, 624,
+ 640, 656, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 672, 688, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 704, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 720,
+ 1, 1, 1, 1, 736, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 752, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 768, 784, 1, 800, 816, 832,
+ 0, 0, 0, 1023,65534, 2047,65534, 2047, 0, 0, 0, 524,65535,65407,65535,65407,
+65535,65535,65532, 15, 0,65535,65535,65535,65535,65535,16383,63999, 3, 0,16415, 0,
+ 0, 0, 0, 0, 32, 0, 0, 1024,55104,65535,65531,65535,32767,64767,65535, 15,
+65535,65535,65535,65535,65535,65535,65535,65535,61443,65535,65535,65535, 6559,65535,65535, 831,
+ 0, 0, 0,65534,65535, 639,65534,65535, 255, 0, 0, 0, 0,65535, 2047, 7,
+ 0, 0,65534, 2047,65534, 63, 1023,65535,65535,65535,65535,65535,65535, 8175, 8702, 8191,
+ 0,65535, 8191,65535, 0, 0, 0, 0,65535,65535,65535, 1, 0, 0, 0, 0,
+65518,65535,65535,58367, 8191,65281,65487, 0,40942,65529,65023,50117, 6559,45184,65487, 3,
+34788,65529,65023,50029, 6535,24064,65472, 31,45038,65531,65023,58349, 7103, 1,65473, 0,
+40942,65529,65023,58317, 6543,45248,65475, 0,51180,54845,50968,50111, 7623, 128,65408, 0,
+57326,65533,65023,50159, 7647, 96,65475, 0,57324,65533,65023,50159, 7647,16480,65475, 0,
+57324,65533,65023,50175, 7631, 128,65475, 0,65516,64639,65535,12283,32895,65375, 0, 12,
+65534,65535,65535, 2047,32767, 1023, 0, 0, 9622,65264,60590,15359, 8223,13311, 0, 0,
+ 1, 0, 1023, 0,65279,65535, 2047,65534, 3843,65279,65535, 8191, 0, 0, 0, 0,
+65535,65535,63227, 327, 1023, 1023, 0, 0, 0, 0,65535,65535, 63,65535,65535, 127,
+65535,65535,65535,65535,65535,33791,65535,65535,65535,65535,65287,65535,65535,65535,65535, 1023,
+65407,65535,65535,65535,15743,15743,65535,65535,15743,65535,32767,32573,32573,65407,32767,65535,
+32767,32573,65535,65535,65407, 2047,65024, 3, 0, 0,65535,65535,65535,65535,65535, 31,
+65534,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,
+65535,65535,65535,65535,65535,65535,40959, 127,65534, 2047,65535,65535,65535,65535, 2047, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,65535,65535,65535,65535, 511, 0, 1023, 0,
+ 0, 1023,65535,65535,65527,65535,65535, 255,65535,65535, 1023, 0, 0, 0, 0, 0,
+65535,65535,65535,65535,65535,65535,65535,65535,65535, 4095,65535,65535,65535,65535,65535, 1023,
+65535,16191,65535,65535,16191,43775,65535,16383,65535,65535,65535,24543, 8156, 4047, 8191, 8156,
+ 0, 0, 0, 0, 0, 0, 0,32768, 0, 0, 0, 0, 0, 0, 0, 0,
+64644,15919,48464, 1019, 0, 0,65535,65535, 15, 0, 0, 0, 0, 0, 0, 0,
+ 192, 0, 1022, 1792,65534,65535,65535,65535,65535, 31,65534,65535,65535,65535,65535, 2047,
+65504,65535, 8191,65534,65535,65535,65535,65535,32767, 0,65535, 255, 0, 0, 0, 0,
+65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0,
+65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, 0,
+65535,65535,65535,65535,65535,65535,65535,65535, 8191, 0, 0, 0, 0, 0, 0, 0,
+65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 15, 0, 0, 0, 0, 0,
+65535,65535,16383, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 127,41208,65023,24447,65499,65535,65535,65535,65535,65535,65535, 3, 0,65528,65535,65535,
+65535,65535,65535,16383, 0,65535,65535,65535,65535,65532,65535,65535, 255, 0, 0, 4095,
+ 0, 0, 0, 0, 0, 0, 0,65495,65535,65535,65535,65535,65535,65535,65535, 8191,
+ 0, 1023,65534, 2047,65534, 2047,65472,65534,65535,16383,65535,32767,64764, 7420, 0, 0};
+
+const UInt16 UnicodeAlphaBits[] = {
+ 256, 1, 272, 288, 304, 320, 336, 352, 0, 368, 384, 400, 416, 432, 448, 464,
+ 480, 496, 512, 528, 544, 1, 560, 576, 592, 0, 0, 0, 0, 0, 608, 624,
+ 640, 656, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 672, 688, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 704, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 720,
+ 1, 1, 1, 1, 736, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 752, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 768, 784, 1, 800, 816, 832,
+ 0, 0, 0, 0,65534, 2047,65534, 2047, 0, 0, 0, 0,65535,65407,65535,65407,
+65535,65535,65532, 15, 0,65535,65535,65535,65535,65535,16383,63999, 3, 0,16415, 0,
+ 0, 0, 0, 0, 32, 0, 0, 1024,55104,65535,65531,65535,32767,64767,65535, 15,
+65535,65535,65535,65535,65535,65535,65535,65535,61443,65535,65535,65535, 6559,65535,65535, 831,
+ 0, 0, 0,65534,65535, 639,65534,65535, 255, 0, 0, 0, 0,65535, 2047, 7,
+ 0, 0,65534, 2047,65534, 63, 0,65535,65535,65535,65535,65535,65535, 8175, 8702, 7168,
+ 0,65535, 8191,65535, 0, 0, 0, 0,65535,65535,65535, 1, 0, 0, 0, 0,
+65518,65535,65535,58367, 8191,65281, 15, 0,40942,65529,65023,50117, 6559,45184, 15, 3,
+34788,65529,65023,50029, 6535,24064, 0, 31,45038,65531,65023,58349, 7103, 1, 1, 0,
+40942,65529,65023,58317, 6543,45248, 3, 0,51180,54845,50968,50111, 7623, 128, 0, 0,
+57326,65533,65023,50159, 7647, 96, 3, 0,57324,65533,65023,50159, 7647,16480, 3, 0,
+57324,65533,65023,50175, 7631, 128, 3, 0,65516,64639,65535,12283,32895,65375, 0, 12,
+65534,65535,65535, 2047,32767, 0, 0, 0, 9622,65264,60590,15359, 8223,12288, 0, 0,
+ 1, 0, 0, 0,65279,65535, 2047,65534, 3843,65279,65535, 8191, 0, 0, 0, 0,
+65535,65535,63227, 327, 0, 1023, 0, 0, 0, 0,65535,65535, 63,65535,65535, 127,
+65535,65535,65535,65535,65535,33791,65535,65535,65535,65535,65287,65535,65535,65535,65535, 1023,
+65407,65535,65535,65535,15743,15743,65535,65535,15743,65535,32767,32573,32573,65407,32767,65535,
+32767,32573,65535,65535,65407, 2047, 0, 0, 0, 0,65535,65535,65535,65535,65535, 31,
+65534,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,
+65535,65535,65535,65535,65535,65535,40959, 127,65534, 2047,65535,65535,65535,65535, 2047, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,65535,65535,65535,65535, 511, 0, 0, 0,
+ 0, 0,65535,65535,65527,65535,65535, 255,65535,65535, 1023, 0, 0, 0, 0, 0,
+65535,65535,65535,65535,65535,65535,65535,65535,65535, 4095,65535,65535,65535,65535,65535, 1023,
+65535,16191,65535,65535,16191,43775,65535,16383,65535,65535,65535,24543, 8156, 4047, 8191, 8156,
+ 0, 0, 0, 0, 0, 0, 0,32768, 0, 0, 0, 0, 0, 0, 0, 0,
+64644,15919,48464, 1019, 0, 0,65535,65535, 15, 0, 0, 0, 0, 0, 0, 0,
+ 192, 0, 1022, 1792,65534,65535,65535,65535,65535, 31,65534,65535,65535,65535,65535, 2047,
+65504,65535, 8191,65534,65535,65535,65535,65535,32767, 0,65535, 255, 0, 0, 0, 0,
+65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0,
+65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, 0,
+65535,65535,65535,65535,65535,65535,65535,65535, 8191, 0, 0, 0, 0, 0, 0, 0,
+65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 15, 0, 0, 0, 0, 0,
+65535,65535,16383, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 127,41208,65023,24447,65499,65535,65535,65535,65535,65535,65535, 3, 0,65528,65535,65535,
+65535,65535,65535,16383, 0,65535,65535,65535,65535,65532,65535,65535, 255, 0, 0, 4095,
+ 0, 0, 0, 0, 0, 0, 0,65495,65535,65535,65535,65535,65535,65535,65535, 8191,
+ 0, 0,65534, 2047,65534, 2047,65472,65534,65535,16383,65535,32767,64764, 7420, 0, 0};
+
+const UInt16 UnicodeDigitBits[] = {
+ 256, 0, 0, 0, 0, 0, 272, 0, 0, 288, 304, 320, 336, 352, 368, 384,
+ 400, 0, 0, 416, 0, 0, 0, 432, 448, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 464,
+ 0, 0, 0, 1023, 0, 0, 0, 0, 0, 0, 0, 524, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1023, 0, 0, 0, 0, 0, 0, 0, 0, 1023,
+ 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0,65472, 0,
+ 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0,65472, 0,
+ 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0,65408, 0,
+ 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0,65472, 0,
+ 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1023, 0, 0, 0, 0, 0, 0, 0, 1023, 0, 0,
+ 0, 0, 1023, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1023, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,65024, 3, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1023, 0,
+ 0, 1023, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1023, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+const UInt16 UnicodeSpaceBits[] = {
+ 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 272, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 288, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 304, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+15872, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 4095, 0,33536, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+const UInt16 UnicodeXDigitBits[] = {
+ 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 272,
+ 0, 0, 0, 1023, 126, 0, 126, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1023, 126, 0, 126, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+// Uncomment if necessary
+//const UInt16 UnicodeCntrlBits[] = {
+// 256, 0, 0, 0, 0, 0, 0, 272, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 288, 0, 0, 0, 0, 0, 0, 0,
+// 304, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 320, 336,
+//65535,65535, 0, 0, 0, 0, 0,32768,65535,65535, 0, 0, 0, 0, 0, 0,
+//32768, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+//30720, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+//61440, 0,31744, 0, 0, 0,64512, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,32768,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3584};
+//
+//const UInt16 UnicodeGraphBits[] = {
+// 256, 1, 272, 288, 304, 320, 336, 352, 0, 368, 384, 400, 416, 432, 448, 464,
+// 480, 496, 512, 528, 544, 1, 560, 576, 592, 0, 0, 0, 0, 0, 608, 624,
+// 640, 656, 0, 672, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 688, 704, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 720, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 736,
+// 1, 1, 1, 1, 752, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 768, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 784, 800, 1, 816, 832, 848,
+// 0, 0,65534,65535,65535,65535,65535,32767, 0, 0,65534,65535,65535,65535,65535,65535,
+//65535,65535,65532, 15, 0,65535,65535,65535,65535,65535,16383,63999, 3, 0,16415, 0,
+// 0, 0, 0, 0, 32, 0, 0,17408,55232,65535,65531,65535,32767,64767,65535, 15,
+//65535,65535,65535,65535,65535,65535,65535,65535,61443,65535,65535,65535, 6559,65535,65535, 831,
+// 0, 0, 0,65534,65535,65151,65534,65535, 1791, 0, 0,16384, 9,65535, 2047, 31,
+// 4096,34816,65534, 2047,65534, 63,16383,65535,65535,65535,65535,65535,65535, 8191, 8702, 8191,
+//16383,65535, 8191,65535, 0, 0, 0, 0,65535,65535,65535, 1, 0, 0, 0, 0,
+//65518,65535,65535,58367, 8191,65281,65535, 1,40942,65529,65023,50117, 6559,45184,65487, 3,
+//34788,65529,65023,50029, 6535,24064,65472, 31,45038,65531,65023,58349, 7103, 1,65473, 0,
+//40942,65529,65023,58317, 6543,45248,65475, 0,51180,54845,50968,50111, 7623, 128,65408, 0,
+//57326,65533,65023,50159, 7647, 96,65475, 0,57324,65533,65023,50159, 7647,16480,65475, 0,
+//57324,65533,65023,50175, 7631, 128,65475, 0,65516,64639,65535,12283,32895,65375, 0, 28,
+//65534,65535,65535, 2047,65535, 4095, 0, 0, 9622,65264,60590,15359, 8223,13311, 0, 0,
+//65521, 7, 1023,15360,65279,65535, 2047,65534, 3875,65279,65535, 8191, 0, 0, 0, 0,
+//65535,65535,63227, 327,65535, 1023, 0, 0, 0, 0,65535,65535, 63,65535,65535, 2175,
+//65535,65535,65535,65535,65535,33791,65535,65535,65535,65535,65287,65535,65535,65535,65535, 1023,
+//65407,65535,65535,65535,15743,15743,65535,65535,15743,65535,32767,32573,32573,65407,32767,65535,
+//32767,32573,65535,65535,65407, 2047,65534, 3, 0, 0,65535,65535,65535,65535,65535, 31,
+//65534,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,
+//65535,65535,65535,65535,65535,65535,65535, 127,65534, 8191,65535,65535,65535,65535,16383, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0,65535,65535,65535,65535, 511, 6128, 1023, 0,
+// 2047, 1023,65535,65535,65527,65535,65535, 255,65535,65535, 1023, 0, 0, 0, 0, 0,
+//65535,65535,65535,65535,65535,65535,65535,65535,65535, 4095,65535,65535,65535,65535,65535, 1023,
+//65535,16191,65535,65535,16191,43775,65535,16383,65535,65535,65535,24543, 8156, 4047, 8191, 8156,
+// 0,65535, 255,65535,16239, 0, 0,57344,24576, 0, 0, 0, 0, 0, 0, 0,
+//64644,15919,48464, 1019, 0, 0,65535,65535, 15, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 1536, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+//65486,65523, 1022, 1793,65534,65535,65535,65535,65535, 31,65534,65535,65535,65535,65535, 4095,
+//65504,65535, 8191,65534,65535,65535,65535,65535,32767, 0,65535, 255, 0, 0, 0, 0,
+//65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0,
+//65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, 0,
+//65535,65535,65535,65535,65535,65535,65535,65535, 8191, 0, 0, 0, 0, 0, 0, 0,
+//65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 15, 0, 0, 0, 0, 0,
+//65535,65535,16383, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 127,41208,65023,24447,65499,65535,65535,65535,65535,65535,65535, 3, 0,65528,65535,65535,
+//65535,65535,65535,65535, 0,65535,65535,65535,65535,65532,65535,65535, 255, 0, 0, 4095,
+// 0, 0, 0,65535,65055,65527, 3339,65495,65535,65535,65535,65535,65535,65535,65535, 8191,
+//63470,36863,65535,49151,65534,12287,65534,65534,65535,16383,65535,32767,64764, 7420, 0, 0};
+//
+//const UInt16 UnicodePrintBits[] = {
+// 256, 1, 272, 288, 304, 320, 336, 352, 0, 368, 384, 400, 416, 432, 448, 464,
+// 480, 496, 512, 528, 544, 1, 560, 576, 592, 0, 0, 0, 0, 0, 608, 624,
+// 640, 656, 0, 672, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 688, 704, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 720, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 736,
+// 1, 1, 1, 1, 752, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 768, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 784, 800, 1, 816, 832, 848,
+// 512, 0,65535,65535,65535,65535,65535,32767, 0, 0,65535,65535,65535,65535,65535,65535,
+//65535,65535,65532, 15, 0,65535,65535,65535,65535,65535,16383,63999, 3, 0,16415, 0,
+// 0, 0, 0, 0, 32, 0, 0,17408,55232,65535,65531,65535,32767,64767,65535, 15,
+//65535,65535,65535,65535,65535,65535,65535,65535,61443,65535,65535,65535, 6559,65535,65535, 831,
+// 0, 0, 0,65534,65535,65151,65534,65535, 1791, 0, 0,16384, 9,65535, 2047, 31,
+// 4096,34816,65534, 2047,65534, 63,16383,65535,65535,65535,65535,65535,65535, 8191, 8702, 8191,
+//16383,65535, 8191,65535, 0, 0, 0, 0,65535,65535,65535, 1, 0, 0, 0, 0,
+//65518,65535,65535,58367, 8191,65281,65535, 1,40942,65529,65023,50117, 6559,45184,65487, 3,
+//34788,65529,65023,50029, 6535,24064,65472, 31,45038,65531,65023,58349, 7103, 1,65473, 0,
+//40942,65529,65023,58317, 6543,45248,65475, 0,51180,54845,50968,50111, 7623, 128,65408, 0,
+//57326,65533,65023,50159, 7647, 96,65475, 0,57324,65533,65023,50159, 7647,16480,65475, 0,
+//57324,65533,65023,50175, 7631, 128,65475, 0,65516,64639,65535,12283,32895,65375, 0, 28,
+//65534,65535,65535, 2047,65535, 4095, 0, 0, 9622,65264,60590,15359, 8223,13311, 0, 0,
+//65521, 7, 1023,15360,65279,65535, 2047,65534, 3875,65279,65535, 8191, 0, 0, 0, 0,
+//65535,65535,63227, 327,65535, 1023, 0, 0, 0, 0,65535,65535, 63,65535,65535, 2175,
+//65535,65535,65535,65535,65535,33791,65535,65535,65535,65535,65287,65535,65535,65535,65535, 1023,
+//65407,65535,65535,65535,15743,15743,65535,65535,15743,65535,32767,32573,32573,65407,32767,65535,
+//32767,32573,65535,65535,65407, 2047,65534, 3, 0, 0,65535,65535,65535,65535,65535, 31,
+//65534,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,
+//65535,65535,65535,65535,65535,65535,65535, 127,65534, 8191,65535,65535,65535,65535,16383, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0,65535,65535,65535,65535, 511, 6128, 1023, 0,
+// 2047, 1023,65535,65535,65527,65535,65535, 255,65535,65535, 1023, 0, 0, 0, 0, 0,
+//65535,65535,65535,65535,65535,65535,65535,65535,65535, 4095,65535,65535,65535,65535,65535, 1023,
+//65535,16191,65535,65535,16191,43775,65535,16383,65535,65535,65535,24543, 8156, 4047, 8191, 8156,
+// 0,65535, 255,65535,16239, 0, 0,57344,24576, 0, 0, 0, 0, 0, 0, 0,
+//64644,15919,48464, 1019, 0, 0,65535,65535, 15, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 1536, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+//65487,65523, 1022, 1793,65534,65535,65535,65535,65535, 31,65534,65535,65535,65535,65535, 4095,
+//65504,65535, 8191,65534,65535,65535,65535,65535,32767, 0,65535, 255, 0, 0, 0, 0,
+//65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0,
+//65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, 0,
+//65535,65535,65535,65535,65535,65535,65535,65535, 8191, 0, 0, 0, 0, 0, 0, 0,
+//65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 15, 0, 0, 0, 0, 0,
+//65535,65535,16383, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 127,41208,65023,24447,65499,65535,65535,65535,65535,65535,65535, 3, 0,65528,65535,65535,
+//65535,65535,65535,65535, 0,65535,65535,65535,65535,65532,65535,65535, 255, 0, 0, 4095,
+// 0, 0, 0,65535,65055,65527, 3339,65495,65535,65535,65535,65535,65535,65535,65535,40959,
+//63470,36863,65535,49151,65534,12287,65534,65534,65535,16383,65535,32767,64764, 7420, 0, 0};
+//
+//const UInt16 UnicodePunctBits[] = {
+// 256, 0, 0, 272, 0, 288, 304, 320, 0, 336, 0, 0, 0, 352, 368, 384,
+// 400, 0, 0, 416, 0, 0, 432, 448, 464, 0, 0, 0, 0, 0, 0, 0,
+// 480, 0, 0, 496, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 512, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 528, 544, 560,
+// 0, 0,65534,64512, 1,63488, 1,30720, 0, 0,65534,65535, 0, 128, 0, 128,
+// 0, 0, 0, 0, 0, 0, 0,16384, 128, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0,64512, 0, 0, 1536, 0, 0,16384, 9, 0, 0, 24,
+// 4096,34816, 0, 0, 0, 0,15360, 0, 0, 0, 0, 0, 0, 16, 0, 0,
+//16383, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16,
+// 0, 0, 0, 0,32768, 3072, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+//65520, 7, 0,15360, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0,64512, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2048,
+// 0, 0, 0, 0, 0, 0, 510, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0,24576, 0, 0, 6144, 0, 0, 0, 0,14336, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6128, 0, 0,
+// 2047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0,65535, 255,65535,16239, 0, 0,24576,24576, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 1536, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+//65294,65523, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2048,
+// 0, 0, 0,49152, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0,65535,65055,65527, 3339, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+//63470,35840, 1,47104, 0,10240, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+//
+//const UInt16 UnicodeLowerBits[] = {
+// 256, 272, 288, 304, 320, 336, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 352, 368,
+// 384, 400, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 416, 0, 0, 0, 432,
+// 0, 0, 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0,32768,65535,65407,
+//43690,43690,43690,21930,43861,43690,43690,54442,12585,20004,11562,58961,23392,46421,43690,43565,
+//43690,43690,43688, 10, 0,65535,65535,65535,65535,65535,16383, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,61440,65535,32767,43235,43690, 15,
+// 0, 0, 0,65535,65535,65535,43690,43690,40962,43690,43690,43690, 4372,43690,43690, 554,
+// 0, 0, 0, 0, 0, 0,65534,65535, 255, 0, 0, 0, 0, 0, 0, 0,
+//43690,43690,43690,43690,43690,43690,43690,43690,43690, 4074,43690,43690,43690,43690,43690, 682,
+// 255, 63, 255, 255, 63, 255, 255,16383,65535,65535,65535,20703, 4316, 207, 255, 4316,
+// 0, 0, 0, 0, 0, 0, 0,32768, 0, 0, 0, 0, 0, 0, 0, 0,
+//50176, 8,32768, 528, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 127, 248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+//
+//const UInt16 UnicodeUpperBits[] = {
+// 256, 272, 288, 304, 320, 336, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 352, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 368, 384,
+// 0, 400, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 416,
+// 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0,65535,32639, 0, 0,
+//21845,21845,21845,43605,21674,21845,21845,11093,52950,45531,53973, 4526,44464,19114,21845,21974,
+//21845,21845,21844, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0,55104,65534, 4091, 0, 0,21532,21845, 0,
+//65535,65535,65535, 0, 0, 0,21845,21845,20481,21845,21845,21845, 2187,21845,21845, 277,
+// 0, 0, 0,65534,65535, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,65535,65535, 63, 0, 0, 0,
+//21845,21845,21845,21845,21845,21845,21845,21845,21845, 21,21845,21845,21845,21845,21845, 341,
+//65280,16128,65280,65280,16128,43520,65280, 0,65280,65280,65280, 7936, 7936, 3840, 7936, 7936,
+//14468,15911,15696, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+
+// MA: March 19, 2010
+// Modified ToUpper and ToLower tables to match values expected by AS3 tests.
+// ToLower modifications:
+// 304 -> 105
+// 1024 -> 1104 *
+// 1037 -> 1117 *
+// UoUpper modifications:
+// 255 -> 376
+// 305 -> 73
+// 383 -> 83
+// 1104 -> 1024 *
+// 1117 -> 1037 *
+// Entries marked with a '*' don't make complete sense based on Unicode manual, although
+// they match AS3.
+
+
+static const UInt16 UnicodeToUpperBits[] = {
+ 256, 272, 288, 304, 320, 336, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 352, 368,
+ 0, 384, 0, 0, 400, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 416,
+ 0, 0, 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0,65535,65407,
+43690,43690,43690,21674,43349,43690,43690,54442, 4392, 516, 8490, 8785,21056,46421,43690,43048, // MA: Modified for AS3.
+43690, 170, 0, 0, 0, 2776,33545, 36, 3336, 4, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,61440,65534,32767, 0,43688, 0,
+ 0, 0, 0,65535,65535,65535,43690,43690, 2,43690,43690,43690, 4372,43690,35498, 554, // MA: Modified for AS3.
+ 0, 0, 0, 0, 0, 0,65534,65535, 127, 0, 0, 0, 0, 0, 0, 0,
+43690,43690,43690,43690,43690,43690,43690,43690,43690, 42,43690,43690,43690,43690,43690, 682,
+ 255, 63, 255, 255, 63, 170, 255,16383, 0, 0, 0, 3, 0, 3, 35, 0,
+ 0, 0, 0, 0, 0, 0, 0,65535, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,65535, 1023, 0,
+ 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+static const UInt16 UnicodeToLowerBits[] = {
+ 256, 272, 288, 304, 320, 336, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 352, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 368, 384,
+ 0, 400, 0, 0, 416, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 432,
+ 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0,65535,32639, 0, 0,
+21845,21845,21845,43605,21674,21845,21845,11093,52950,45531,53909, 4526,42128,19114,21845,21522,// MA: Modidied for AS3.
+21845, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,55104,65534, 4091, 0, 0, 0,21844, 0,
+65535,65535,65535, 0, 0, 0,21845,21845, 1,21845,21845,21845, 2186,21845,17749, 277,
+ 0, 0, 0,65534,65535, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,65535,65535, 63, 0, 0, 0,
+21845,21845,21845,21845,21845,21845,21845,21845,21845, 21,21845,21845,21845,21845,21845, 341,
+65280,16128,65280,65280,16128,43520,65280, 0, 0, 0, 0, 3840, 3840, 3840, 7936, 3840,
+ 0, 0, 0, 0, 0, 0,65535, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,65472,65535, 0, 0, 0,
+ 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+struct GUnicodePairType
+{
+ UInt16 Key, Value;
+};
+
+static inline bool CmpUnicodeKey(const GUnicodePairType& a, UInt16 key)
+{
+ return a.Key < key;
+}
+
+static const GUnicodePairType UnicodeToUpperTable[] = {
+{ 97, 65}, { 98, 66}, { 99, 67}, { 100, 68}, { 101, 69}, { 102, 70}, { 103, 71},
+{ 104, 72}, { 105, 73}, { 106, 74}, { 107, 75}, { 108, 76}, { 109, 77}, { 110, 78},
+{ 111, 79}, { 112, 80}, { 113, 81}, { 114, 82}, { 115, 83}, { 116, 84}, { 117, 85},
+{ 118, 86}, { 119, 87}, { 120, 88}, { 121, 89}, { 122, 90}, { 224, 192}, { 225, 193},
+{ 226, 194}, { 227, 195}, { 228, 196}, { 229, 197}, { 230, 198}, { 231, 199}, { 232, 200},
+{ 233, 201}, { 234, 202}, { 235, 203}, { 236, 204}, { 237, 205}, { 238, 206}, { 239, 207},
+{ 240, 208}, { 241, 209}, { 242, 210}, { 243, 211}, { 244, 212}, { 245, 213}, { 246, 214},
+{ 248, 216}, { 249, 217}, { 250, 218}, { 251, 219}, { 252, 220}, { 253, 221}, { 254, 222},
+{ 255, 376}, { 257, 256}, { 259, 258}, { 261, 260}, { 263, 262}, { 265, 264}, { 267, 266},
+{ 269, 268}, { 271, 270}, { 273, 272}, { 275, 274}, { 277, 276}, { 279, 278}, { 281, 280},
+{ 283, 282}, { 285, 284}, { 287, 286}, { 289, 288}, { 291, 290}, { 293, 292}, { 295, 294},
+{ 297, 296}, { 299, 298}, { 301, 300}, { 303, 302}, { 305, 73}, { 307, 306}, { 309, 308}, { 311, 310},
+{ 314, 313}, { 316, 315}, { 318, 317}, { 320, 319}, { 322, 321}, { 324, 323}, { 326, 325},
+{ 328, 327}, { 331, 330}, { 333, 332}, { 335, 334}, { 337, 336}, { 339, 338}, { 341, 340},
+{ 343, 342}, { 345, 344}, { 347, 346}, { 349, 348}, { 351, 350}, { 353, 352}, { 355, 354},
+{ 357, 356}, { 359, 358}, { 361, 360}, { 363, 362}, { 365, 364}, { 367, 366}, { 369, 368},
+{ 371, 370}, { 373, 372}, { 375, 374}, { 378, 377}, { 380, 379}, { 382, 381}, { 383, 83}, { 387, 386},
+{ 389, 388}, { 392, 391}, { 396, 395}, { 402, 401}, { 409, 408}, { 417, 416}, { 419, 418},
+{ 421, 420}, { 424, 423}, { 429, 428}, { 432, 431}, { 436, 435}, { 438, 437}, { 441, 440},
+{ 445, 444}, { 454, 452}, { 457, 455}, { 460, 458}, { 462, 461}, { 464, 463}, { 466, 465},
+{ 468, 467}, { 470, 469}, { 472, 471}, { 474, 473}, { 476, 475}, { 477, 398}, { 479, 478},
+{ 481, 480}, { 483, 482}, { 485, 484}, { 487, 486}, { 489, 488}, { 491, 490}, { 493, 492},
+{ 495, 494}, { 499, 497}, { 501, 500}, { 507, 506}, { 509, 508}, { 511, 510}, { 513, 512},
+{ 515, 514}, { 517, 516}, { 519, 518}, { 521, 520}, { 523, 522}, { 525, 524}, { 527, 526},
+{ 529, 528}, { 531, 530}, { 533, 532}, { 535, 534}, { 595, 385}, { 596, 390}, { 598, 393},
+{ 599, 394}, { 601, 399}, { 603, 400}, { 608, 403}, { 611, 404}, { 616, 407}, { 617, 406},
+{ 623, 412}, { 626, 413}, { 629, 415}, { 643, 425}, { 648, 430}, { 650, 433}, { 651, 434},
+{ 658, 439}, { 940, 902}, { 941, 904}, { 942, 905}, { 943, 906}, { 945, 913}, { 946, 914},
+{ 947, 915}, { 948, 916}, { 949, 917}, { 950, 918}, { 951, 919}, { 952, 920}, { 953, 921},
+{ 954, 922}, { 955, 923}, { 956, 924}, { 957, 925}, { 958, 926}, { 959, 927}, { 960, 928},
+{ 961, 929}, { 962, 931}, { 963, 931}, { 964, 932}, { 965, 933}, { 966, 934}, { 967, 935},
+{ 968, 936}, { 969, 937}, { 970, 938}, { 971, 939}, { 972, 908}, { 973, 910}, { 974, 911},
+{ 995, 994}, { 997, 996}, { 999, 998}, { 1001, 1000}, { 1003, 1002}, { 1005, 1004}, { 1007, 1006},
+{ 1072, 1040}, { 1073, 1041}, { 1074, 1042}, { 1075, 1043}, { 1076, 1044}, { 1077, 1045}, { 1078, 1046},
+{ 1079, 1047}, { 1080, 1048}, { 1081, 1049}, { 1082, 1050}, { 1083, 1051}, { 1084, 1052}, { 1085, 1053},
+{ 1086, 1054}, { 1087, 1055}, { 1088, 1056}, { 1089, 1057}, { 1090, 1058}, { 1091, 1059}, { 1092, 1060},
+{ 1093, 1061}, { 1094, 1062}, { 1095, 1063}, { 1096, 1064}, { 1097, 1065}, { 1098, 1066}, { 1099, 1067},
+{ 1100, 1068}, { 1101, 1069}, { 1102, 1070}, { 1103, 1071}, { 1104, 1024}, { 1105, 1025}, { 1106, 1026}, { 1107, 1027},
+{ 1108, 1028}, { 1109, 1029}, { 1110, 1030}, { 1111, 1031}, { 1112, 1032}, { 1113, 1033}, { 1114, 1034},
+{ 1115, 1035}, { 1116, 1036}, { 1117, 1037}, { 1118, 1038}, { 1119, 1039}, { 1121, 1120}, { 1123, 1122}, { 1125, 1124},
+{ 1127, 1126}, { 1129, 1128}, { 1131, 1130}, { 1133, 1132}, { 1135, 1134}, { 1137, 1136}, { 1139, 1138},
+{ 1141, 1140}, { 1143, 1142}, { 1145, 1144}, { 1147, 1146}, { 1149, 1148}, { 1151, 1150}, { 1153, 1152},
+{ 1169, 1168}, { 1171, 1170}, { 1173, 1172}, { 1175, 1174}, { 1177, 1176}, { 1179, 1178}, { 1181, 1180},
+{ 1183, 1182}, { 1185, 1184}, { 1187, 1186}, { 1189, 1188}, { 1191, 1190}, { 1193, 1192}, { 1195, 1194},
+{ 1197, 1196}, { 1199, 1198}, { 1201, 1200}, { 1203, 1202}, { 1205, 1204}, { 1207, 1206}, { 1209, 1208},
+{ 1211, 1210}, { 1213, 1212}, { 1215, 1214}, { 1218, 1217}, { 1220, 1219}, { 1224, 1223}, { 1228, 1227},
+{ 1233, 1232}, { 1235, 1234}, { 1237, 1236}, { 1239, 1238}, { 1241, 1240}, { 1243, 1242}, { 1245, 1244},
+{ 1247, 1246}, { 1249, 1248}, { 1251, 1250}, { 1253, 1252}, { 1255, 1254}, { 1257, 1256}, { 1259, 1258},
+{ 1263, 1262}, { 1265, 1264}, { 1267, 1266}, { 1269, 1268}, { 1273, 1272}, { 1377, 1329}, { 1378, 1330},
+{ 1379, 1331}, { 1380, 1332}, { 1381, 1333}, { 1382, 1334}, { 1383, 1335}, { 1384, 1336}, { 1385, 1337},
+{ 1386, 1338}, { 1387, 1339}, { 1388, 1340}, { 1389, 1341}, { 1390, 1342}, { 1391, 1343}, { 1392, 1344},
+{ 1393, 1345}, { 1394, 1346}, { 1395, 1347}, { 1396, 1348}, { 1397, 1349}, { 1398, 1350}, { 1399, 1351},
+{ 1400, 1352}, { 1401, 1353}, { 1402, 1354}, { 1403, 1355}, { 1404, 1356}, { 1405, 1357}, { 1406, 1358},
+{ 1407, 1359}, { 1408, 1360}, { 1409, 1361}, { 1410, 1362}, { 1411, 1363}, { 1412, 1364}, { 1413, 1365},
+{ 1414, 1366}, { 7681, 7680}, { 7683, 7682}, { 7685, 7684}, { 7687, 7686}, { 7689, 7688}, { 7691, 7690},
+{ 7693, 7692}, { 7695, 7694}, { 7697, 7696}, { 7699, 7698}, { 7701, 7700}, { 7703, 7702}, { 7705, 7704},
+{ 7707, 7706}, { 7709, 7708}, { 7711, 7710}, { 7713, 7712}, { 7715, 7714}, { 7717, 7716}, { 7719, 7718},
+{ 7721, 7720}, { 7723, 7722}, { 7725, 7724}, { 7727, 7726}, { 7729, 7728}, { 7731, 7730}, { 7733, 7732},
+{ 7735, 7734}, { 7737, 7736}, { 7739, 7738}, { 7741, 7740}, { 7743, 7742}, { 7745, 7744}, { 7747, 7746},
+{ 7749, 7748}, { 7751, 7750}, { 7753, 7752}, { 7755, 7754}, { 7757, 7756}, { 7759, 7758}, { 7761, 7760},
+{ 7763, 7762}, { 7765, 7764}, { 7767, 7766}, { 7769, 7768}, { 7771, 7770}, { 7773, 7772}, { 7775, 7774},
+{ 7777, 7776}, { 7779, 7778}, { 7781, 7780}, { 7783, 7782}, { 7785, 7784}, { 7787, 7786}, { 7789, 7788},
+{ 7791, 7790}, { 7793, 7792}, { 7795, 7794}, { 7797, 7796}, { 7799, 7798}, { 7801, 7800}, { 7803, 7802},
+{ 7805, 7804}, { 7807, 7806}, { 7809, 7808}, { 7811, 7810}, { 7813, 7812}, { 7815, 7814}, { 7817, 7816},
+{ 7819, 7818}, { 7821, 7820}, { 7823, 7822}, { 7825, 7824}, { 7827, 7826}, { 7829, 7828}, { 7841, 7840},
+{ 7843, 7842}, { 7845, 7844}, { 7847, 7846}, { 7849, 7848}, { 7851, 7850}, { 7853, 7852}, { 7855, 7854},
+{ 7857, 7856}, { 7859, 7858}, { 7861, 7860}, { 7863, 7862}, { 7865, 7864}, { 7867, 7866}, { 7869, 7868},
+{ 7871, 7870}, { 7873, 7872}, { 7875, 7874}, { 7877, 7876}, { 7879, 7878}, { 7881, 7880}, { 7883, 7882},
+{ 7885, 7884}, { 7887, 7886}, { 7889, 7888}, { 7891, 7890}, { 7893, 7892}, { 7895, 7894}, { 7897, 7896},
+{ 7899, 7898}, { 7901, 7900}, { 7903, 7902}, { 7905, 7904}, { 7907, 7906}, { 7909, 7908}, { 7911, 7910},
+{ 7913, 7912}, { 7915, 7914}, { 7917, 7916}, { 7919, 7918}, { 7921, 7920}, { 7923, 7922}, { 7925, 7924},
+{ 7927, 7926}, { 7929, 7928}, { 7936, 7944}, { 7937, 7945}, { 7938, 7946}, { 7939, 7947}, { 7940, 7948},
+{ 7941, 7949}, { 7942, 7950}, { 7943, 7951}, { 7952, 7960}, { 7953, 7961}, { 7954, 7962}, { 7955, 7963},
+{ 7956, 7964}, { 7957, 7965}, { 7968, 7976}, { 7969, 7977}, { 7970, 7978}, { 7971, 7979}, { 7972, 7980},
+{ 7973, 7981}, { 7974, 7982}, { 7975, 7983}, { 7984, 7992}, { 7985, 7993}, { 7986, 7994}, { 7987, 7995},
+{ 7988, 7996}, { 7989, 7997}, { 7990, 7998}, { 7991, 7999}, { 8000, 8008}, { 8001, 8009}, { 8002, 8010},
+{ 8003, 8011}, { 8004, 8012}, { 8005, 8013}, { 8017, 8025}, { 8019, 8027}, { 8021, 8029}, { 8023, 8031},
+{ 8032, 8040}, { 8033, 8041}, { 8034, 8042}, { 8035, 8043}, { 8036, 8044}, { 8037, 8045}, { 8038, 8046},
+{ 8039, 8047}, { 8048, 8122}, { 8049, 8123}, { 8050, 8136}, { 8051, 8137}, { 8052, 8138}, { 8053, 8139},
+{ 8054, 8154}, { 8055, 8155}, { 8056, 8184}, { 8057, 8185}, { 8058, 8170}, { 8059, 8171}, { 8060, 8186},
+{ 8061, 8187}, { 8112, 8120}, { 8113, 8121}, { 8144, 8152}, { 8145, 8153}, { 8160, 8168}, { 8161, 8169},
+{ 8165, 8172}, { 8560, 8544}, { 8561, 8545}, { 8562, 8546}, { 8563, 8547}, { 8564, 8548}, { 8565, 8549},
+{ 8566, 8550}, { 8567, 8551}, { 8568, 8552}, { 8569, 8553}, { 8570, 8554}, { 8571, 8555}, { 8572, 8556},
+{ 8573, 8557}, { 8574, 8558}, { 8575, 8559}, { 9424, 9398}, { 9425, 9399}, { 9426, 9400}, { 9427, 9401},
+{ 9428, 9402}, { 9429, 9403}, { 9430, 9404}, { 9431, 9405}, { 9432, 9406}, { 9433, 9407}, { 9434, 9408},
+{ 9435, 9409}, { 9436, 9410}, { 9437, 9411}, { 9438, 9412}, { 9439, 9413}, { 9440, 9414}, { 9441, 9415},
+{ 9442, 9416}, { 9443, 9417}, { 9444, 9418}, { 9445, 9419}, { 9446, 9420}, { 9447, 9421}, { 9448, 9422},
+{ 9449, 9423}, {65345,65313}, {65346,65314}, {65347,65315}, {65348,65316}, {65349,65317}, {65350,65318},
+{65351,65319}, {65352,65320}, {65353,65321}, {65354,65322}, {65355,65323}, {65356,65324}, {65357,65325},
+{65358,65326}, {65359,65327}, {65360,65328}, {65361,65329}, {65362,65330}, {65363,65331}, {65364,65332},
+{65365,65333}, {65366,65334}, {65367,65335}, {65368,65336}, {65369,65337}, {65370,65338}, {65535, 0}};
+
+static const GUnicodePairType UnicodeToLowerTable[] = {
+{ 65, 97}, { 66, 98}, { 67, 99}, { 68, 100}, { 69, 101}, { 70, 102}, { 71, 103},
+{ 72, 104}, { 73, 105}, { 74, 106}, { 75, 107}, { 76, 108}, { 77, 109}, { 78, 110},
+{ 79, 111}, { 80, 112}, { 81, 113}, { 82, 114}, { 83, 115}, { 84, 116}, { 85, 117},
+{ 86, 118}, { 87, 119}, { 88, 120}, { 89, 121}, { 90, 122}, { 192, 224}, { 193, 225},
+{ 194, 226}, { 195, 227}, { 196, 228}, { 197, 229}, { 198, 230}, { 199, 231}, { 200, 232},
+{ 201, 233}, { 202, 234}, { 203, 235}, { 204, 236}, { 205, 237}, { 206, 238}, { 207, 239},
+{ 208, 240}, { 209, 241}, { 210, 242}, { 211, 243}, { 212, 244}, { 213, 245}, { 214, 246},
+{ 216, 248}, { 217, 249}, { 218, 250}, { 219, 251}, { 220, 252}, { 221, 253}, { 222, 254},
+{ 256, 257}, { 258, 259}, { 260, 261}, { 262, 263}, { 264, 265}, { 266, 267}, { 268, 269},
+{ 270, 271}, { 272, 273}, { 274, 275}, { 276, 277}, { 278, 279}, { 280, 281}, { 282, 283},
+{ 284, 285}, { 286, 287}, { 288, 289}, { 290, 291}, { 292, 293}, { 294, 295}, { 296, 297},
+{ 298, 299}, { 300, 301}, { 302, 303}, { 304, 105}, { 306, 307}, { 308, 309}, { 310, 311}, { 313, 314},
+{ 315, 316}, { 317, 318}, { 319, 320}, { 321, 322}, { 323, 324}, { 325, 326}, { 327, 328},
+{ 330, 331}, { 332, 333}, { 334, 335}, { 336, 337}, { 338, 339}, { 340, 341}, { 342, 343},
+{ 344, 345}, { 346, 347}, { 348, 349}, { 350, 351}, { 352, 353}, { 354, 355}, { 356, 357},
+{ 358, 359}, { 360, 361}, { 362, 363}, { 364, 365}, { 366, 367}, { 368, 369}, { 370, 371},
+{ 372, 373}, { 374, 375}, { 376, 255}, { 377, 378}, { 379, 380}, { 381, 382}, { 385, 595},
+{ 386, 387}, { 388, 389}, { 390, 596}, { 391, 392}, { 393, 598}, { 394, 599}, { 395, 396},
+{ 398, 477}, { 399, 601}, { 400, 603}, { 401, 402}, { 403, 608}, { 404, 611}, { 406, 617},
+{ 407, 616}, { 408, 409}, { 412, 623}, { 413, 626}, { 415, 629}, { 416, 417}, { 418, 419},
+{ 420, 421}, { 423, 424}, { 425, 643}, { 428, 429}, { 430, 648}, { 431, 432}, { 433, 650},
+{ 434, 651}, { 435, 436}, { 437, 438}, { 439, 658}, { 440, 441}, { 444, 445}, { 452, 454},
+{ 455, 457}, { 458, 460}, { 461, 462}, { 463, 464}, { 465, 466}, { 467, 468}, { 469, 470},
+{ 471, 472}, { 473, 474}, { 475, 476}, { 478, 479}, { 480, 481}, { 482, 483}, { 484, 485},
+{ 486, 487}, { 488, 489}, { 490, 491}, { 492, 493}, { 494, 495}, { 497, 499}, { 500, 501},
+{ 506, 507}, { 508, 509}, { 510, 511}, { 512, 513}, { 514, 515}, { 516, 517}, { 518, 519},
+{ 520, 521}, { 522, 523}, { 524, 525}, { 526, 527}, { 528, 529}, { 530, 531}, { 532, 533},
+{ 534, 535}, { 902, 940}, { 904, 941}, { 905, 942}, { 906, 943}, { 908, 972}, { 910, 973},
+{ 911, 974}, { 913, 945}, { 914, 946}, { 915, 947}, { 916, 948}, { 917, 949}, { 918, 950},
+{ 919, 951}, { 920, 952}, { 921, 953}, { 922, 954}, { 923, 955}, { 924, 956}, { 925, 957},
+{ 926, 958}, { 927, 959}, { 928, 960}, { 929, 961}, { 931, 963}, { 932, 964}, { 933, 965},
+{ 934, 966}, { 935, 967}, { 936, 968}, { 937, 969}, { 938, 970}, { 939, 971}, { 994, 995},
+{ 996, 997}, { 998, 999}, { 1000, 1001}, { 1002, 1003}, { 1004, 1005}, { 1006, 1007}, { 1024, 1104}, { 1025, 1105},
+{ 1026, 1106}, { 1027, 1107}, { 1028, 1108}, { 1029, 1109}, { 1030, 1110}, { 1031, 1111}, { 1032, 1112},
+{ 1033, 1113}, { 1034, 1114}, { 1035, 1115}, { 1036, 1116}, { 1037, 1117}, { 1038, 1118}, { 1039, 1119}, { 1040, 1072},
+{ 1041, 1073}, { 1042, 1074}, { 1043, 1075}, { 1044, 1076}, { 1045, 1077}, { 1046, 1078}, { 1047, 1079},
+{ 1048, 1080}, { 1049, 1081}, { 1050, 1082}, { 1051, 1083}, { 1052, 1084}, { 1053, 1085}, { 1054, 1086},
+{ 1055, 1087}, { 1056, 1088}, { 1057, 1089}, { 1058, 1090}, { 1059, 1091}, { 1060, 1092}, { 1061, 1093},
+{ 1062, 1094}, { 1063, 1095}, { 1064, 1096}, { 1065, 1097}, { 1066, 1098}, { 1067, 1099}, { 1068, 1100},
+{ 1069, 1101}, { 1070, 1102}, { 1071, 1103}, { 1120, 1121}, { 1122, 1123}, { 1124, 1125}, { 1126, 1127},
+{ 1128, 1129}, { 1130, 1131}, { 1132, 1133}, { 1134, 1135}, { 1136, 1137}, { 1138, 1139}, { 1140, 1141},
+{ 1142, 1143}, { 1144, 1145}, { 1146, 1147}, { 1148, 1149}, { 1150, 1151}, { 1152, 1153}, { 1168, 1169},
+{ 1170, 1171}, { 1172, 1173}, { 1174, 1175}, { 1176, 1177}, { 1178, 1179}, { 1180, 1181}, { 1182, 1183},
+{ 1184, 1185}, { 1186, 1187}, { 1188, 1189}, { 1190, 1191}, { 1192, 1193}, { 1194, 1195}, { 1196, 1197},
+{ 1198, 1199}, { 1200, 1201}, { 1202, 1203}, { 1204, 1205}, { 1206, 1207}, { 1208, 1209}, { 1210, 1211},
+{ 1212, 1213}, { 1214, 1215}, { 1217, 1218}, { 1219, 1220}, { 1223, 1224}, { 1227, 1228}, { 1232, 1233},
+{ 1234, 1235}, { 1236, 1237}, { 1238, 1239}, { 1240, 1241}, { 1242, 1243}, { 1244, 1245}, { 1246, 1247},
+{ 1248, 1249}, { 1250, 1251}, { 1252, 1253}, { 1254, 1255}, { 1256, 1257}, { 1258, 1259}, { 1262, 1263},
+{ 1264, 1265}, { 1266, 1267}, { 1268, 1269}, { 1272, 1273}, { 1329, 1377}, { 1330, 1378}, { 1331, 1379},
+{ 1332, 1380}, { 1333, 1381}, { 1334, 1382}, { 1335, 1383}, { 1336, 1384}, { 1337, 1385}, { 1338, 1386},
+{ 1339, 1387}, { 1340, 1388}, { 1341, 1389}, { 1342, 1390}, { 1343, 1391}, { 1344, 1392}, { 1345, 1393},
+{ 1346, 1394}, { 1347, 1395}, { 1348, 1396}, { 1349, 1397}, { 1350, 1398}, { 1351, 1399}, { 1352, 1400},
+{ 1353, 1401}, { 1354, 1402}, { 1355, 1403}, { 1356, 1404}, { 1357, 1405}, { 1358, 1406}, { 1359, 1407},
+{ 1360, 1408}, { 1361, 1409}, { 1362, 1410}, { 1363, 1411}, { 1364, 1412}, { 1365, 1413}, { 1366, 1414},
+{ 4256, 4304}, { 4257, 4305}, { 4258, 4306}, { 4259, 4307}, { 4260, 4308}, { 4261, 4309}, { 4262, 4310},
+{ 4263, 4311}, { 4264, 4312}, { 4265, 4313}, { 4266, 4314}, { 4267, 4315}, { 4268, 4316}, { 4269, 4317},
+{ 4270, 4318}, { 4271, 4319}, { 4272, 4320}, { 4273, 4321}, { 4274, 4322}, { 4275, 4323}, { 4276, 4324},
+{ 4277, 4325}, { 4278, 4326}, { 4279, 4327}, { 4280, 4328}, { 4281, 4329}, { 4282, 4330}, { 4283, 4331},
+{ 4284, 4332}, { 4285, 4333}, { 4286, 4334}, { 4287, 4335}, { 4288, 4336}, { 4289, 4337}, { 4290, 4338},
+{ 4291, 4339}, { 4292, 4340}, { 4293, 4341}, { 7680, 7681}, { 7682, 7683}, { 7684, 7685}, { 7686, 7687},
+{ 7688, 7689}, { 7690, 7691}, { 7692, 7693}, { 7694, 7695}, { 7696, 7697}, { 7698, 7699}, { 7700, 7701},
+{ 7702, 7703}, { 7704, 7705}, { 7706, 7707}, { 7708, 7709}, { 7710, 7711}, { 7712, 7713}, { 7714, 7715},
+{ 7716, 7717}, { 7718, 7719}, { 7720, 7721}, { 7722, 7723}, { 7724, 7725}, { 7726, 7727}, { 7728, 7729},
+{ 7730, 7731}, { 7732, 7733}, { 7734, 7735}, { 7736, 7737}, { 7738, 7739}, { 7740, 7741}, { 7742, 7743},
+{ 7744, 7745}, { 7746, 7747}, { 7748, 7749}, { 7750, 7751}, { 7752, 7753}, { 7754, 7755}, { 7756, 7757},
+{ 7758, 7759}, { 7760, 7761}, { 7762, 7763}, { 7764, 7765}, { 7766, 7767}, { 7768, 7769}, { 7770, 7771},
+{ 7772, 7773}, { 7774, 7775}, { 7776, 7777}, { 7778, 7779}, { 7780, 7781}, { 7782, 7783}, { 7784, 7785},
+{ 7786, 7787}, { 7788, 7789}, { 7790, 7791}, { 7792, 7793}, { 7794, 7795}, { 7796, 7797}, { 7798, 7799},
+{ 7800, 7801}, { 7802, 7803}, { 7804, 7805}, { 7806, 7807}, { 7808, 7809}, { 7810, 7811}, { 7812, 7813},
+{ 7814, 7815}, { 7816, 7817}, { 7818, 7819}, { 7820, 7821}, { 7822, 7823}, { 7824, 7825}, { 7826, 7827},
+{ 7828, 7829}, { 7840, 7841}, { 7842, 7843}, { 7844, 7845}, { 7846, 7847}, { 7848, 7849}, { 7850, 7851},
+{ 7852, 7853}, { 7854, 7855}, { 7856, 7857}, { 7858, 7859}, { 7860, 7861}, { 7862, 7863}, { 7864, 7865},
+{ 7866, 7867}, { 7868, 7869}, { 7870, 7871}, { 7872, 7873}, { 7874, 7875}, { 7876, 7877}, { 7878, 7879},
+{ 7880, 7881}, { 7882, 7883}, { 7884, 7885}, { 7886, 7887}, { 7888, 7889}, { 7890, 7891}, { 7892, 7893},
+{ 7894, 7895}, { 7896, 7897}, { 7898, 7899}, { 7900, 7901}, { 7902, 7903}, { 7904, 7905}, { 7906, 7907},
+{ 7908, 7909}, { 7910, 7911}, { 7912, 7913}, { 7914, 7915}, { 7916, 7917}, { 7918, 7919}, { 7920, 7921},
+{ 7922, 7923}, { 7924, 7925}, { 7926, 7927}, { 7928, 7929}, { 7944, 7936}, { 7945, 7937}, { 7946, 7938},
+{ 7947, 7939}, { 7948, 7940}, { 7949, 7941}, { 7950, 7942}, { 7951, 7943}, { 7960, 7952}, { 7961, 7953},
+{ 7962, 7954}, { 7963, 7955}, { 7964, 7956}, { 7965, 7957}, { 7976, 7968}, { 7977, 7969}, { 7978, 7970},
+{ 7979, 7971}, { 7980, 7972}, { 7981, 7973}, { 7982, 7974}, { 7983, 7975}, { 7992, 7984}, { 7993, 7985},
+{ 7994, 7986}, { 7995, 7987}, { 7996, 7988}, { 7997, 7989}, { 7998, 7990}, { 7999, 7991}, { 8008, 8000},
+{ 8009, 8001}, { 8010, 8002}, { 8011, 8003}, { 8012, 8004}, { 8013, 8005}, { 8025, 8017}, { 8027, 8019},
+{ 8029, 8021}, { 8031, 8023}, { 8040, 8032}, { 8041, 8033}, { 8042, 8034}, { 8043, 8035}, { 8044, 8036},
+{ 8045, 8037}, { 8046, 8038}, { 8047, 8039}, { 8120, 8112}, { 8121, 8113}, { 8122, 8048}, { 8123, 8049},
+{ 8136, 8050}, { 8137, 8051}, { 8138, 8052}, { 8139, 8053}, { 8152, 8144}, { 8153, 8145}, { 8154, 8054},
+{ 8155, 8055}, { 8168, 8160}, { 8169, 8161}, { 8170, 8058}, { 8171, 8059}, { 8172, 8165}, { 8184, 8056},
+{ 8185, 8057}, { 8186, 8060}, { 8187, 8061}, { 8544, 8560}, { 8545, 8561}, { 8546, 8562}, { 8547, 8563},
+{ 8548, 8564}, { 8549, 8565}, { 8550, 8566}, { 8551, 8567}, { 8552, 8568}, { 8553, 8569}, { 8554, 8570},
+{ 8555, 8571}, { 8556, 8572}, { 8557, 8573}, { 8558, 8574}, { 8559, 8575}, { 9398, 9424}, { 9399, 9425},
+{ 9400, 9426}, { 9401, 9427}, { 9402, 9428}, { 9403, 9429}, { 9404, 9430}, { 9405, 9431}, { 9406, 9432},
+{ 9407, 9433}, { 9408, 9434}, { 9409, 9435}, { 9410, 9436}, { 9411, 9437}, { 9412, 9438}, { 9413, 9439},
+{ 9414, 9440}, { 9415, 9441}, { 9416, 9442}, { 9417, 9443}, { 9418, 9444}, { 9419, 9445}, { 9420, 9446},
+{ 9421, 9447}, { 9422, 9448}, { 9423, 9449}, {65313,65345}, {65314,65346}, {65315,65347}, {65316,65348},
+{65317,65349}, {65318,65350}, {65319,65351}, {65320,65352}, {65321,65353}, {65322,65354}, {65323,65355},
+{65324,65356}, {65325,65357}, {65326,65358}, {65327,65359}, {65328,65360}, {65329,65361}, {65330,65362},
+{65331,65363}, {65332,65364}, {65333,65365}, {65334,65366}, {65335,65367}, {65336,65368}, {65337,65369},
+{65338,65370}, {65535, 0}};
+
+int OVR_CDECL OVR_towupper(wchar_t charCode)
+{
+ // Don't use UnicodeUpperBits! It differs from UnicodeToUpperBits.
+ if (UnicodeCharIs(UnicodeToUpperBits, charCode))
+ {
+ // To protect from memory overrun in case the character is not found
+ // we use one extra fake element in the table {65536, 0}.
+ UPInt idx = Alg::LowerBoundSliced(
+ UnicodeToUpperTable,
+ 0,
+ sizeof(UnicodeToUpperTable) / sizeof(UnicodeToUpperTable[0]) - 1,
+ (UInt16)charCode,
+ CmpUnicodeKey);
+ return UnicodeToUpperTable[idx].Value;
+ }
+ return charCode;
+}
+
+int OVR_CDECL OVR_towlower(wchar_t charCode)
+{
+ // Don't use UnicodeLowerBits! It differs from UnicodeToLowerBits.
+ if (UnicodeCharIs(UnicodeToLowerBits, charCode))
+ {
+ // To protect from memory overrun in case the character is not found
+ // we use one extra fake element in the table {65536, 0}.
+ UPInt idx = Alg::LowerBoundSliced(
+ UnicodeToLowerTable,
+ 0,
+ sizeof(UnicodeToLowerTable) / sizeof(UnicodeToLowerTable[0]) - 1,
+ (UInt16)charCode,
+ CmpUnicodeKey);
+ return UnicodeToLowerTable[idx].Value;
+ }
+ return charCode;
+}
+
+#endif //OVR_NO_WCTYPE
+
+} // OVR
diff --git a/LibOVR/Src/Kernel/OVR_Std.h b/LibOVR/Src/Kernel/OVR_Std.h
new file mode 100644
index 0000000..b20a376
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_Std.h
@@ -0,0 +1,503 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_Std.h
+Content : Standard C function interface
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#ifndef OVR_Std_h
+#define OVR_Std_h
+
+#include "OVR_Types.h"
+#include <stdarg.h> // for va_list args
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#if !defined(OVR_OS_WINCE) && defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400)
+#define OVR_MSVC_SAFESTRING
+#include <errno.h>
+#endif
+
+// Wide-char funcs
+#include <wchar.h>
+#include <wctype.h>
+
+namespace OVR {
+
+#if defined(OVR_OS_WIN32)
+inline char* OVR_CDECL OVR_itoa(int val, char *dest, UPInt destsize, int radix)
+{
+#if defined(OVR_MSVC_SAFESTRING)
+ _itoa_s(val, dest, destsize, radix);
+ return dest;
+#else
+ OVR_UNUSED(destsize);
+ return itoa(val, dest, radix);
+#endif
+}
+#else // OVR_OS_WIN32
+inline char* OVR_itoa(int val, char* dest, unsigned int len, int radix)
+{
+ if (val == 0)
+ {
+ if (len > 1)
+ {
+ dest[0] = '0';
+ dest[1] = '\0';
+ }
+ return dest;
+ }
+
+ int cur = val;
+ unsigned int i = 0;
+ unsigned int sign = 0;
+
+ if (val < 0)
+ {
+ val = -val;
+ sign = 1;
+ }
+
+ while ((val != 0) && (i < (len - 1 - sign)))
+ {
+ cur = val % radix;
+ val /= radix;
+
+ if (radix == 16)
+ {
+ switch(cur)
+ {
+ case 10:
+ dest[i] = 'a';
+ break;
+ case 11:
+ dest[i] = 'b';
+ break;
+ case 12:
+ dest[i] = 'c';
+ break;
+ case 13:
+ dest[i] = 'd';
+ break;
+ case 14:
+ dest[i] = 'e';
+ break;
+ case 15:
+ dest[i] = 'f';
+ break;
+ default:
+ dest[i] = (char)('0' + cur);
+ break;
+ }
+ }
+ else
+ {
+ dest[i] = (char)('0' + cur);
+ }
+ ++i;
+ }
+
+ if (sign)
+ {
+ dest[i++] = '-';
+ }
+
+ for (unsigned int j = 0; j < i / 2; ++j)
+ {
+ char tmp = dest[j];
+ dest[j] = dest[i - 1 - j];
+ dest[i - 1 - j] = tmp;
+ }
+ dest[i] = '\0';
+
+ return dest;
+}
+
+#endif
+
+
+// String functions
+
+inline UPInt OVR_CDECL OVR_strlen(const char* str)
+{
+ return strlen(str);
+}
+
+inline char* OVR_CDECL OVR_strcpy(char* dest, UPInt destsize, const char* src)
+{
+#if defined(OVR_MSVC_SAFESTRING)
+ strcpy_s(dest, destsize, src);
+ return dest;
+#else
+ OVR_UNUSED(destsize);
+ return strcpy(dest, src);
+#endif
+}
+
+inline char* OVR_CDECL OVR_strncpy(char* dest, UPInt destsize, const char* src, UPInt count)
+{
+#if defined(OVR_MSVC_SAFESTRING)
+ strncpy_s(dest, destsize, src, count);
+ return dest;
+#else
+ OVR_UNUSED(destsize);
+ return strncpy(dest, src, count);
+#endif
+}
+
+inline char * OVR_CDECL OVR_strcat(char* dest, UPInt destsize, const char* src)
+{
+#if defined(OVR_MSVC_SAFESTRING)
+ strcat_s(dest, destsize, src);
+ return dest;
+#else
+ OVR_UNUSED(destsize);
+ return strcat(dest, src);
+#endif
+}
+
+inline int OVR_CDECL OVR_strcmp(const char* dest, const char* src)
+{
+ return strcmp(dest, src);
+}
+
+inline const char* OVR_CDECL OVR_strchr(const char* str, char c)
+{
+ return strchr(str, c);
+}
+
+inline char* OVR_CDECL OVR_strchr(char* str, char c)
+{
+ return strchr(str, c);
+}
+
+inline const char* OVR_strrchr(const char* str, char c)
+{
+ UPInt len = OVR_strlen(str);
+ for (UPInt i=len; i>0; i--)
+ if (str[i]==c)
+ return str+i;
+ return 0;
+}
+
+inline const UByte* OVR_CDECL OVR_memrchr(const UByte* str, UPInt size, UByte c)
+{
+ for (SPInt i = (SPInt)size - 1; i >= 0; i--)
+ {
+ if (str[i] == c)
+ return str + i;
+ }
+ return 0;
+}
+
+inline char* OVR_CDECL OVR_strrchr(char* str, char c)
+{
+ UPInt len = OVR_strlen(str);
+ for (UPInt i=len; i>0; i--)
+ if (str[i]==c)
+ return str+i;
+ return 0;
+}
+
+
+double OVR_CDECL OVR_strtod(const char* string, char** tailptr);
+
+inline long OVR_CDECL OVR_strtol(const char* string, char** tailptr, int radix)
+{
+ return strtol(string, tailptr, radix);
+}
+
+inline long OVR_CDECL OVR_strtoul(const char* string, char** tailptr, int radix)
+{
+ return strtoul(string, tailptr, radix);
+}
+
+inline int OVR_CDECL OVR_strncmp(const char* ws1, const char* ws2, UPInt size)
+{
+ return strncmp(ws1, ws2, size);
+}
+
+inline UInt64 OVR_CDECL OVR_strtouq(const char *nptr, char **endptr, int base)
+{
+#if defined(OVR_CC_MSVC) && !defined(OVR_OS_WINCE)
+ return _strtoui64(nptr, endptr, base);
+#else
+ return strtoull(nptr, endptr, base);
+#endif
+}
+
+inline SInt64 OVR_CDECL OVR_strtoq(const char *nptr, char **endptr, int base)
+{
+#if defined(OVR_CC_MSVC) && !defined(OVR_OS_WINCE)
+ return _strtoi64(nptr, endptr, base);
+#else
+ return strtoll(nptr, endptr, base);
+#endif
+}
+
+
+inline SInt64 OVR_CDECL OVR_atoq(const char* string)
+{
+#if defined(OVR_CC_MSVC) && !defined(OVR_OS_WINCE)
+ return _atoi64(string);
+#else
+ return atoll(string);
+#endif
+}
+
+inline UInt64 OVR_CDECL OVR_atouq(const char* string)
+{
+ return OVR_strtouq(string, NULL, 10);
+}
+
+
+// Implemented in GStd.cpp in platform-specific manner.
+int OVR_CDECL OVR_stricmp(const char* dest, const char* src);
+int OVR_CDECL OVR_strnicmp(const char* dest, const char* src, UPInt count);
+
+inline UPInt OVR_CDECL OVR_sprintf(char *dest, UPInt destsize, const char* format, ...)
+{
+ va_list argList;
+ va_start(argList,format);
+ UPInt ret;
+#if defined(OVR_CC_MSVC)
+ #if defined(OVR_MSVC_SAFESTRING)
+ ret = _vsnprintf_s(dest, destsize, _TRUNCATE, format, argList);
+ OVR_ASSERT(ret != -1);
+ #else
+ OVR_UNUSED(destsize);
+ ret = _vsnprintf(dest, destsize - 1, format, argList); // -1 for space for the null character
+ OVR_ASSERT(ret != -1);
+ dest[destsize-1] = 0;
+ #endif
+#else
+ OVR_UNUSED(destsize);
+ ret = vsprintf(dest, format, argList);
+ OVR_ASSERT(ret < destsize);
+#endif
+ va_end(argList);
+ return ret;
+}
+
+inline UPInt OVR_CDECL OVR_vsprintf(char *dest, UPInt destsize, const char * format, va_list argList)
+{
+ UPInt ret;
+#if defined(OVR_CC_MSVC)
+ #if defined(OVR_MSVC_SAFESTRING)
+ dest[0] = '\0';
+ int rv = vsnprintf_s(dest, destsize, _TRUNCATE, format, argList);
+ if (rv == -1)
+ {
+ dest[destsize - 1] = '\0';
+ ret = destsize - 1;
+ }
+ else
+ ret = (UPInt)rv;
+ #else
+ OVR_UNUSED(destsize);
+ int rv = _vsnprintf(dest, destsize - 1, format, argList);
+ OVR_ASSERT(rv != -1);
+ ret = (UPInt)rv;
+ dest[destsize-1] = 0;
+ #endif
+#else
+ OVR_UNUSED(destsize);
+ ret = (UPInt)vsprintf(dest, format, argList);
+ OVR_ASSERT(ret < destsize);
+#endif
+ return ret;
+}
+
+// Returns the number of characters in the formatted string.
+inline UPInt OVR_CDECL OVR_vscprintf(const char * format, va_list argList)
+{
+ UPInt ret;
+#if defined(OVR_CC_MSVC)
+ ret = (UPInt) _vscprintf(format, argList);
+#else
+ ret = (UPInt) vsnprintf(NULL, 0, format, argList);
+#endif
+ return ret;
+}
+
+
+wchar_t* OVR_CDECL OVR_wcscpy(wchar_t* dest, UPInt destsize, const wchar_t* src);
+wchar_t* OVR_CDECL OVR_wcsncpy(wchar_t* dest, UPInt destsize, const wchar_t* src, UPInt count);
+wchar_t* OVR_CDECL OVR_wcscat(wchar_t* dest, UPInt destsize, const wchar_t* src);
+UPInt OVR_CDECL OVR_wcslen(const wchar_t* str);
+int OVR_CDECL OVR_wcscmp(const wchar_t* a, const wchar_t* b);
+int OVR_CDECL OVR_wcsicmp(const wchar_t* a, const wchar_t* b);
+
+inline int OVR_CDECL OVR_wcsicoll(const wchar_t* a, const wchar_t* b)
+{
+#if defined(OVR_OS_WIN32)
+#if defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400)
+ return ::_wcsicoll(a, b);
+#else
+ return ::wcsicoll(a, b);
+#endif
+#else
+ // not supported, use regular wcsicmp
+ return OVR_wcsicmp(a, b);
+#endif
+}
+
+inline int OVR_CDECL OVR_wcscoll(const wchar_t* a, const wchar_t* b)
+{
+#if defined(OVR_OS_WIN32) || defined(OVR_OS_LINUX)
+ return wcscoll(a, b);
+#else
+ // not supported, use regular wcscmp
+ return OVR_wcscmp(a, b);
+#endif
+}
+
+#ifndef OVR_NO_WCTYPE
+
+inline int OVR_CDECL UnicodeCharIs(const UInt16* table, wchar_t charCode)
+{
+ unsigned offset = table[charCode >> 8];
+ if (offset == 0) return 0;
+ if (offset == 1) return 1;
+ return (table[offset + ((charCode >> 4) & 15)] & (1 << (charCode & 15))) != 0;
+}
+
+extern const UInt16 UnicodeAlnumBits[];
+extern const UInt16 UnicodeAlphaBits[];
+extern const UInt16 UnicodeDigitBits[];
+extern const UInt16 UnicodeSpaceBits[];
+extern const UInt16 UnicodeXDigitBits[];
+
+// Uncomment if necessary
+//extern const UInt16 UnicodeCntrlBits[];
+//extern const UInt16 UnicodeGraphBits[];
+//extern const UInt16 UnicodeLowerBits[];
+//extern const UInt16 UnicodePrintBits[];
+//extern const UInt16 UnicodePunctBits[];
+//extern const UInt16 UnicodeUpperBits[];
+
+inline int OVR_CDECL OVR_iswalnum (wchar_t charCode) { return UnicodeCharIs(UnicodeAlnumBits, charCode); }
+inline int OVR_CDECL OVR_iswalpha (wchar_t charCode) { return UnicodeCharIs(UnicodeAlphaBits, charCode); }
+inline int OVR_CDECL OVR_iswdigit (wchar_t charCode) { return UnicodeCharIs(UnicodeDigitBits, charCode); }
+inline int OVR_CDECL OVR_iswspace (wchar_t charCode) { return UnicodeCharIs(UnicodeSpaceBits, charCode); }
+inline int OVR_CDECL OVR_iswxdigit(wchar_t charCode) { return UnicodeCharIs(UnicodeXDigitBits, charCode); }
+
+// Uncomment if necessary
+//inline int OVR_CDECL OVR_iswcntrl (wchar_t charCode) { return UnicodeCharIs(UnicodeCntrlBits, charCode); }
+//inline int OVR_CDECL OVR_iswgraph (wchar_t charCode) { return UnicodeCharIs(UnicodeGraphBits, charCode); }
+//inline int OVR_CDECL OVR_iswlower (wchar_t charCode) { return UnicodeCharIs(UnicodeLowerBits, charCode); }
+//inline int OVR_CDECL OVR_iswprint (wchar_t charCode) { return UnicodeCharIs(UnicodePrintBits, charCode); }
+//inline int OVR_CDECL OVR_iswpunct (wchar_t charCode) { return UnicodeCharIs(UnicodePunctBits, charCode); }
+//inline int OVR_CDECL OVR_iswupper (wchar_t charCode) { return UnicodeCharIs(UnicodeUpperBits, charCode); }
+
+int OVR_CDECL OVR_towupper(wchar_t charCode);
+int OVR_CDECL OVR_towlower(wchar_t charCode);
+
+#else // OVR_NO_WCTYPE
+
+inline int OVR_CDECL OVR_iswspace(wchar_t c)
+{
+ return iswspace(c);
+}
+
+inline int OVR_CDECL OVR_iswdigit(wchar_t c)
+{
+ return iswdigit(c);
+}
+
+inline int OVR_CDECL OVR_iswxdigit(wchar_t c)
+{
+ return iswxdigit(c);
+}
+
+inline int OVR_CDECL OVR_iswalpha(wchar_t c)
+{
+ return iswalpha(c);
+}
+
+inline int OVR_CDECL OVR_iswalnum(wchar_t c)
+{
+ return iswalnum(c);
+}
+
+inline wchar_t OVR_CDECL OVR_towlower(wchar_t c)
+{
+ return (wchar_t)towlower(c);
+}
+
+inline wchar_t OVR_towupper(wchar_t c)
+{
+ return (wchar_t)towupper(c);
+}
+
+#endif // OVR_NO_WCTYPE
+
+// ASCII versions of tolower and toupper. Don't use "char"
+inline int OVR_CDECL OVR_tolower(int c)
+{
+ return (c >= 'A' && c <= 'Z') ? c - 'A' + 'a' : c;
+}
+
+inline int OVR_CDECL OVR_toupper(int c)
+{
+ return (c >= 'a' && c <= 'z') ? c - 'a' + 'A' : c;
+}
+
+
+
+inline double OVR_CDECL OVR_wcstod(const wchar_t* string, wchar_t** tailptr)
+{
+#if defined(OVR_OS_OTHER)
+ OVR_UNUSED(tailptr);
+ char buffer[64];
+ char* tp = NULL;
+ UPInt max = OVR_wcslen(string);
+ if (max > 63) max = 63;
+ unsigned char c = 0;
+ for (UPInt i=0; i < max; i++)
+ {
+ c = (unsigned char)string[i];
+ buffer[i] = ((c) < 128 ? (char)c : '!');
+ }
+ buffer[max] = 0;
+ return OVR_strtod(buffer, &tp);
+#else
+ return wcstod(string, tailptr);
+#endif
+}
+
+inline long OVR_CDECL OVR_wcstol(const wchar_t* string, wchar_t** tailptr, int radix)
+{
+#if defined(OVR_OS_OTHER)
+ OVR_UNUSED(tailptr);
+ char buffer[64];
+ char* tp = NULL;
+ UPInt max = OVR_wcslen(string);
+ if (max > 63) max = 63;
+ unsigned char c = 0;
+ for (UPInt i=0; i < max; i++)
+ {
+ c = (unsigned char)string[i];
+ buffer[i] = ((c) < 128 ? (char)c : '!');
+ }
+ buffer[max] = 0;
+ return strtol(buffer, &tp, radix);
+#else
+ return wcstol(string, tailptr, radix);
+#endif
+}
+
+} // OVR
+
+#endif // OVR_Std_h
diff --git a/LibOVR/Src/Kernel/OVR_String.cpp b/LibOVR/Src/Kernel/OVR_String.cpp
new file mode 100644
index 0000000..b591ede
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_String.cpp
@@ -0,0 +1,752 @@
+/************************************************************************************
+
+Filename : OVR_String.cpp
+Content : String UTF8 string implementation with copy-on-write semantics
+ (thread-safe for assignment but not modification).
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#include "OVR_String.h"
+
+#include <stdlib.h>
+#include <ctype.h>
+
+#ifdef OVR_OS_QNX
+# include <strings.h>
+#endif
+
+namespace OVR {
+
+#define String_LengthIsSize (UPInt(1) << String::Flag_LengthIsSizeShift)
+
+String::DataDesc String::NullData = {String_LengthIsSize, 1, {0} };
+
+
+String::String()
+{
+ pData = &NullData;
+ pData->AddRef();
+};
+
+String::String(const char* pdata)
+{
+ // Obtain length in bytes; it doesn't matter if _data is UTF8.
+ UPInt size = pdata ? OVR_strlen(pdata) : 0;
+ pData = AllocDataCopy1(size, 0, pdata, size);
+};
+
+String::String(const char* pdata1, const char* pdata2, const char* pdata3)
+{
+ // Obtain length in bytes; it doesn't matter if _data is UTF8.
+ UPInt size1 = pdata1 ? OVR_strlen(pdata1) : 0;
+ UPInt size2 = pdata2 ? OVR_strlen(pdata2) : 0;
+ UPInt size3 = pdata3 ? OVR_strlen(pdata3) : 0;
+
+ DataDesc *pdataDesc = AllocDataCopy2(size1 + size2 + size3, 0,
+ pdata1, size1, pdata2, size2);
+ memcpy(pdataDesc->Data + size1 + size2, pdata3, size3);
+ pData = pdataDesc;
+}
+
+String::String(const char* pdata, UPInt size)
+{
+ OVR_ASSERT((size == 0) || (pdata != 0));
+ pData = AllocDataCopy1(size, 0, pdata, size);
+};
+
+
+String::String(const InitStruct& src, UPInt size)
+{
+ pData = AllocData(size, 0);
+ src.InitString(GetData()->Data, size);
+}
+
+String::String(const String& src)
+{
+ pData = src.GetData();
+ pData->AddRef();
+}
+
+String::String(const StringBuffer& src)
+{
+ pData = AllocDataCopy1(src.GetSize(), 0, src.ToCStr(), src.GetSize());
+}
+
+String::String(const wchar_t* data)
+{
+ pData = &NullData;
+ pData->AddRef();
+ // Simplified logic for wchar_t constructor.
+ if (data)
+ *this = data;
+}
+
+
+String::DataDesc* String::AllocData(UPInt size, UPInt lengthIsSize)
+{
+ String::DataDesc* pdesc;
+
+ if (size == 0)
+ {
+ pdesc = &NullData;
+ pdesc->AddRef();
+ return pdesc;
+ }
+
+ pdesc = (DataDesc*)OVR_ALLOC(sizeof(DataDesc)+ size);
+ pdesc->Data[size] = 0;
+ pdesc->RefCount = 1;
+ pdesc->Size = size | lengthIsSize;
+ return pdesc;
+}
+
+
+String::DataDesc* String::AllocDataCopy1(UPInt size, UPInt lengthIsSize,
+ const char* pdata, UPInt copySize)
+{
+ String::DataDesc* pdesc = AllocData(size, lengthIsSize);
+ memcpy(pdesc->Data, pdata, copySize);
+ return pdesc;
+}
+
+String::DataDesc* String::AllocDataCopy2(UPInt size, UPInt lengthIsSize,
+ const char* pdata1, UPInt copySize1,
+ const char* pdata2, UPInt copySize2)
+{
+ String::DataDesc* pdesc = AllocData(size, lengthIsSize);
+ memcpy(pdesc->Data, pdata1, copySize1);
+ memcpy(pdesc->Data + copySize1, pdata2, copySize2);
+ return pdesc;
+}
+
+
+UPInt String::GetLength() const
+{
+ // Optimize length accesses for non-UTF8 character strings.
+ DataDesc* pdata = GetData();
+ UPInt length, size = pdata->GetSize();
+
+ if (pdata->LengthIsSize())
+ return size;
+
+ length = (UPInt)UTF8Util::GetLength(pdata->Data, (UPInt)size);
+
+ if (length == size)
+ pdata->Size |= String_LengthIsSize;
+
+ return length;
+}
+
+
+//static UInt32 String_CharSearch(const char* buf, )
+
+
+UInt32 String::GetCharAt(UPInt index) const
+{
+ SPInt i = (SPInt) index;
+ DataDesc* pdata = GetData();
+ const char* buf = pdata->Data;
+ UInt32 c;
+
+ if (pdata->LengthIsSize())
+ {
+ OVR_ASSERT(index < pdata->GetSize());
+ buf += i;
+ return UTF8Util::DecodeNextChar_Advance0(&buf);
+ }
+
+ c = UTF8Util::GetCharAt(index, buf, pdata->GetSize());
+ return c;
+}
+
+UInt32 String::GetFirstCharAt(UPInt index, const char** offset) const
+{
+ DataDesc* pdata = GetData();
+ SPInt i = (SPInt) index;
+ const char* buf = pdata->Data;
+ const char* end = buf + pdata->GetSize();
+ UInt32 c;
+
+ do
+ {
+ c = UTF8Util::DecodeNextChar_Advance0(&buf);
+ i--;
+
+ if (buf >= end)
+ {
+ // We've hit the end of the string; don't go further.
+ OVR_ASSERT(i == 0);
+ return c;
+ }
+ } while (i >= 0);
+
+ *offset = buf;
+
+ return c;
+}
+
+UInt32 String::GetNextChar(const char** offset) const
+{
+ return UTF8Util::DecodeNextChar(offset);
+}
+
+
+
+void String::AppendChar(UInt32 ch)
+{
+ DataDesc* pdata = GetData();
+ UPInt size = pdata->GetSize();
+ char buff[8];
+ SPInt encodeSize = 0;
+
+ // Converts ch into UTF8 string and fills it into buff.
+ UTF8Util::EncodeChar(buff, &encodeSize, ch);
+ OVR_ASSERT(encodeSize >= 0);
+
+ SetData(AllocDataCopy2(size + (UPInt)encodeSize, 0,
+ pdata->Data, size, buff, (UPInt)encodeSize));
+ pdata->Release();
+}
+
+
+void String::AppendString(const wchar_t* pstr, SPInt len)
+{
+ if (!pstr)
+ return;
+
+ DataDesc* pdata = GetData();
+ UPInt oldSize = pdata->GetSize();
+ UPInt encodeSize = (UPInt)UTF8Util::GetEncodeStringSize(pstr, len);
+
+ DataDesc* pnewData = AllocDataCopy1(oldSize + (UPInt)encodeSize, 0,
+ pdata->Data, oldSize);
+ UTF8Util::EncodeString(pnewData->Data + oldSize, pstr, len);
+
+ SetData(pnewData);
+ pdata->Release();
+}
+
+
+void String::AppendString(const char* putf8str, SPInt utf8StrSz)
+{
+ if (!putf8str || !utf8StrSz)
+ return;
+ if (utf8StrSz == -1)
+ utf8StrSz = (SPInt)OVR_strlen(putf8str);
+
+ DataDesc* pdata = GetData();
+ UPInt oldSize = pdata->GetSize();
+
+ SetData(AllocDataCopy2(oldSize + (UPInt)utf8StrSz, 0,
+ pdata->Data, oldSize, putf8str, (UPInt)utf8StrSz));
+ pdata->Release();
+}
+
+void String::AssignString(const InitStruct& src, UPInt size)
+{
+ DataDesc* poldData = GetData();
+ DataDesc* pnewData = AllocData(size, 0);
+ src.InitString(pnewData->Data, size);
+ SetData(pnewData);
+ poldData->Release();
+}
+
+void String::AssignString(const char* putf8str, UPInt size)
+{
+ DataDesc* poldData = GetData();
+ SetData(AllocDataCopy1(size, 0, putf8str, size));
+ poldData->Release();
+}
+
+void String::operator = (const char* pstr)
+{
+ AssignString(pstr, pstr ? OVR_strlen(pstr) : 0);
+}
+
+void String::operator = (const wchar_t* pwstr)
+{
+ DataDesc* poldData = GetData();
+ UPInt size = pwstr ? (UPInt)UTF8Util::GetEncodeStringSize(pwstr) : 0;
+
+ DataDesc* pnewData = AllocData(size, 0);
+ UTF8Util::EncodeString(pnewData->Data, pwstr);
+ SetData(pnewData);
+ poldData->Release();
+}
+
+
+void String::operator = (const String& src)
+{
+ DataDesc* psdata = src.GetData();
+ DataDesc* pdata = GetData();
+
+ SetData(psdata);
+ psdata->AddRef();
+ pdata->Release();
+}
+
+
+void String::operator = (const StringBuffer& src)
+{
+ DataDesc* polddata = GetData();
+ SetData(AllocDataCopy1(src.GetSize(), 0, src.ToCStr(), src.GetSize()));
+ polddata->Release();
+}
+
+void String::operator += (const String& src)
+{
+ DataDesc *pourData = GetData(),
+ *psrcData = src.GetData();
+ UPInt ourSize = pourData->GetSize(),
+ srcSize = psrcData->GetSize();
+ UPInt lflag = pourData->GetLengthFlag() & psrcData->GetLengthFlag();
+
+ SetData(AllocDataCopy2(ourSize + srcSize, lflag,
+ pourData->Data, ourSize, psrcData->Data, srcSize));
+ pourData->Release();
+}
+
+
+String String::operator + (const char* str) const
+{
+ String tmp1(*this);
+ tmp1 += (str ? str : "");
+ return tmp1;
+}
+
+String String::operator + (const String& src) const
+{
+ String tmp1(*this);
+ tmp1 += src;
+ return tmp1;
+}
+
+void String::Remove(UPInt posAt, SPInt removeLength)
+{
+ DataDesc* pdata = GetData();
+ UPInt oldSize = pdata->GetSize();
+ // Length indicates the number of characters to remove.
+ UPInt length = GetLength();
+
+ // If index is past the string, nothing to remove.
+ if (posAt >= length)
+ return;
+ // Otherwise, cap removeLength to the length of the string.
+ if ((posAt + removeLength) > length)
+ removeLength = length - posAt;
+
+ // Get the byte position of the UTF8 char at position posAt.
+ SPInt bytePos = UTF8Util::GetByteIndex(posAt, pdata->Data, oldSize);
+ SPInt removeSize = UTF8Util::GetByteIndex(removeLength, pdata->Data + bytePos, oldSize-bytePos);
+
+ SetData(AllocDataCopy2(oldSize - removeSize, pdata->GetLengthFlag(),
+ pdata->Data, bytePos,
+ pData->Data + bytePos + removeSize, (oldSize - bytePos - removeSize)));
+ pdata->Release();
+}
+
+
+String String::Substring(UPInt start, UPInt end) const
+{
+ UPInt length = GetLength();
+ if ((start >= length) || (start >= end))
+ return String();
+
+ DataDesc* pdata = GetData();
+
+ // If size matches, we know the exact index range.
+ if (pdata->LengthIsSize())
+ return String(pdata->Data + start, end - start);
+
+ // Get position of starting character.
+ SPInt byteStart = UTF8Util::GetByteIndex(start, pdata->Data, pdata->GetSize());
+ SPInt byteSize = UTF8Util::GetByteIndex(end - start, pdata->Data + byteStart, pdata->GetSize()-byteStart);
+ return String(pdata->Data + byteStart, (UPInt)byteSize);
+}
+
+void String::Clear()
+{
+ NullData.AddRef();
+ GetData()->Release();
+ SetData(&NullData);
+}
+
+
+String String::ToUpper() const
+{
+ UInt32 c;
+ const char* psource = GetData()->Data;
+ const char* pend = psource + GetData()->GetSize();
+ String str;
+ SPInt bufferOffset = 0;
+ char buffer[512];
+
+ while(psource < pend)
+ {
+ do {
+ c = UTF8Util::DecodeNextChar_Advance0(&psource);
+ UTF8Util::EncodeChar(buffer, &bufferOffset, OVR_towupper(wchar_t(c)));
+ } while ((psource < pend) && (bufferOffset < SPInt(sizeof(buffer)-8)));
+
+ // Append string a piece at a time.
+ str.AppendString(buffer, bufferOffset);
+ bufferOffset = 0;
+ }
+
+ return str;
+}
+
+String String::ToLower() const
+{
+ UInt32 c;
+ const char* psource = GetData()->Data;
+ const char* pend = psource + GetData()->GetSize();
+ String str;
+ SPInt bufferOffset = 0;
+ char buffer[512];
+
+ while(psource < pend)
+ {
+ do {
+ c = UTF8Util::DecodeNextChar_Advance0(&psource);
+ UTF8Util::EncodeChar(buffer, &bufferOffset, OVR_towlower(wchar_t(c)));
+ } while ((psource < pend) && (bufferOffset < SPInt(sizeof(buffer)-8)));
+
+ // Append string a piece at a time.
+ str.AppendString(buffer, bufferOffset);
+ bufferOffset = 0;
+ }
+
+ return str;
+}
+
+
+
+String& String::Insert(const char* substr, UPInt posAt, SPInt strSize)
+{
+ DataDesc* poldData = GetData();
+ UPInt oldSize = poldData->GetSize();
+ UPInt insertSize = (strSize < 0) ? OVR_strlen(substr) : (UPInt)strSize;
+ UPInt byteIndex = (poldData->LengthIsSize()) ?
+ posAt : (UPInt)UTF8Util::GetByteIndex(posAt, poldData->Data, oldSize);
+
+ OVR_ASSERT(byteIndex <= oldSize);
+
+ DataDesc* pnewData = AllocDataCopy2(oldSize + insertSize, 0,
+ poldData->Data, byteIndex, substr, insertSize);
+ memcpy(pnewData->Data + byteIndex + insertSize,
+ poldData->Data + byteIndex, oldSize - byteIndex);
+ SetData(pnewData);
+ poldData->Release();
+ return *this;
+}
+
+/*
+String& String::Insert(const UInt32* substr, UPInt posAt, SPInt len)
+{
+ for (SPInt i = 0; i < len; ++i)
+ {
+ UPInt charw = InsertCharAt(substr[i], posAt);
+ posAt += charw;
+ }
+ return *this;
+}
+*/
+
+UPInt String::InsertCharAt(UInt32 c, UPInt posAt)
+{
+ char buf[8];
+ SPInt index = 0;
+ UTF8Util::EncodeChar(buf, &index, c);
+ OVR_ASSERT(index >= 0);
+ buf[(UPInt)index] = 0;
+
+ Insert(buf, posAt, index);
+ return (UPInt)index;
+}
+
+
+int String::CompareNoCase(const char* a, const char* b)
+{
+ return OVR_stricmp(a, b);
+}
+
+int String::CompareNoCase(const char* a, const char* b, SPInt len)
+{
+ if (len)
+ {
+ SPInt f,l;
+ SPInt slen = len;
+ const char *s = b;
+ do {
+ f = (SPInt)OVR_tolower((int)(*(a++)));
+ l = (SPInt)OVR_tolower((int)(*(b++)));
+ } while (--len && f && (f == l) && *b != 0);
+
+ if (f == l && (len != 0 || *b != 0))
+ {
+ f = (SPInt)slen;
+ l = (SPInt)OVR_strlen(s);
+ return int(f - l);
+ }
+
+ return int(f - l);
+ }
+ else
+ return (0-(int)OVR_strlen(b));
+}
+
+// ***** Implement hash static functions
+
+// Hash function
+UPInt String::BernsteinHashFunction(const void* pdataIn, UPInt size, UPInt seed)
+{
+ const UByte* pdata = (const UByte*) pdataIn;
+ UPInt h = seed;
+ while (size > 0)
+ {
+ size--;
+ h = ((h << 5) + h) ^ (unsigned) pdata[size];
+ }
+
+ return h;
+}
+
+// Hash function, case-insensitive
+UPInt String::BernsteinHashFunctionCIS(const void* pdataIn, UPInt size, UPInt seed)
+{
+ const UByte* pdata = (const UByte*) pdataIn;
+ UPInt h = seed;
+ while (size > 0)
+ {
+ size--;
+ h = ((h << 5) + h) ^ OVR_tolower(pdata[size]);
+ }
+
+ // Alternative: "sdbm" hash function, suggested at same web page above.
+ // h = 0;
+ // for bytes { h = (h << 16) + (h << 6) - hash + *p; }
+ return h;
+}
+
+
+
+// ***** String Buffer used for Building Strings
+
+
+#define OVR_SBUFF_DEFAULT_GROW_SIZE 512
+// Constructors / Destructor.
+StringBuffer::StringBuffer()
+ : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false)
+{
+}
+
+StringBuffer::StringBuffer(UPInt growSize)
+ : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false)
+{
+ SetGrowSize(growSize);
+}
+
+StringBuffer::StringBuffer(const char* data)
+ : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false)
+{
+ *this = data;
+}
+
+StringBuffer::StringBuffer(const char* data, UPInt dataSize)
+ : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false)
+{
+ AppendString(data, dataSize);
+}
+
+StringBuffer::StringBuffer(const String& src)
+ : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false)
+{
+ AppendString(src.ToCStr(), src.GetSize());
+}
+
+StringBuffer::StringBuffer(const StringBuffer& src)
+ : pData(NULL), Size(0), BufferSize(src.GetGrowSize()), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false)
+{
+ AppendString(src.ToCStr(), src.GetSize());
+ LengthIsSize = src.LengthIsSize;
+}
+
+StringBuffer::StringBuffer(const wchar_t* data)
+ : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false)
+{
+ *this = data;
+}
+
+StringBuffer::~StringBuffer()
+{
+ if (pData)
+ OVR_FREE(pData);
+}
+void StringBuffer::SetGrowSize(UPInt growSize)
+{
+ if (growSize <= 16)
+ GrowSize = 16;
+ else
+ {
+ UByte bits = Alg::UpperBit(UInt32(growSize-1));
+ UPInt size = 1<<bits;
+ GrowSize = size == growSize ? growSize : size;
+ }
+}
+
+UPInt StringBuffer::GetLength() const
+{
+ UPInt length, size = GetSize();
+ if (LengthIsSize)
+ return size;
+
+ length = (UPInt)UTF8Util::GetLength(pData, (UPInt)GetSize());
+
+ if (length == GetSize())
+ LengthIsSize = true;
+ return length;
+}
+
+void StringBuffer::Reserve(UPInt _size)
+{
+ if (_size >= BufferSize) // >= because of trailing zero! (!AB)
+ {
+ BufferSize = (_size + 1 + GrowSize - 1)& ~(GrowSize-1);
+ if (!pData)
+ pData = (char*)OVR_ALLOC(BufferSize);
+ else
+ pData = (char*)OVR_REALLOC(pData, BufferSize);
+ }
+}
+void StringBuffer::Resize(UPInt _size)
+{
+ Reserve(_size);
+ LengthIsSize = false;
+ Size = _size;
+ if (pData)
+ pData[Size] = 0;
+}
+
+void StringBuffer::Clear()
+{
+ Resize(0);
+ /*
+ if (pData != pEmptyNullData)
+ {
+ OVR_FREE(pHeap, pData);
+ pData = pEmptyNullData;
+ Size = BufferSize = 0;
+ LengthIsSize = false;
+ }
+ */
+}
+// Appends a character
+void StringBuffer::AppendChar(UInt32 ch)
+{
+ char buff[8];
+ UPInt origSize = GetSize();
+
+ // Converts ch into UTF8 string and fills it into buff. Also increments index according to the number of bytes
+ // in the UTF8 string.
+ SPInt srcSize = 0;
+ UTF8Util::EncodeChar(buff, &srcSize, ch);
+ OVR_ASSERT(srcSize >= 0);
+
+ UPInt size = origSize + srcSize;
+ Resize(size);
+ memcpy(pData + origSize, buff, srcSize);
+}
+
+// Append a string
+void StringBuffer::AppendString(const wchar_t* pstr, SPInt len)
+{
+ if (!pstr)
+ return;
+
+ SPInt srcSize = UTF8Util::GetEncodeStringSize(pstr, len);
+ UPInt origSize = GetSize();
+ UPInt size = srcSize + origSize;
+
+ Resize(size);
+ UTF8Util::EncodeString(pData + origSize, pstr, len);
+}
+
+void StringBuffer::AppendString(const char* putf8str, SPInt utf8StrSz)
+{
+ if (!putf8str || !utf8StrSz)
+ return;
+ if (utf8StrSz == -1)
+ utf8StrSz = (SPInt)OVR_strlen(putf8str);
+
+ UPInt origSize = GetSize();
+ UPInt size = utf8StrSz + origSize;
+
+ Resize(size);
+ memcpy(pData + origSize, putf8str, utf8StrSz);
+}
+
+
+void StringBuffer::operator = (const char* pstr)
+{
+ pstr = pstr ? pstr : "";
+ UPInt size = OVR_strlen(pstr);
+ Resize(size);
+ memcpy(pData, pstr, size);
+}
+
+void StringBuffer::operator = (const wchar_t* pstr)
+{
+ pstr = pstr ? pstr : L"";
+ UPInt size = (UPInt)UTF8Util::GetEncodeStringSize(pstr);
+ Resize(size);
+ UTF8Util::EncodeString(pData, pstr);
+}
+
+void StringBuffer::operator = (const String& src)
+{
+ Resize(src.GetSize());
+ memcpy(pData, src.ToCStr(), src.GetSize());
+}
+
+
+// Inserts substr at posAt
+void StringBuffer::Insert(const char* substr, UPInt posAt, SPInt len)
+{
+ UPInt oldSize = Size;
+ UPInt insertSize = (len < 0) ? OVR_strlen(substr) : (UPInt)len;
+ UPInt byteIndex = LengthIsSize ? posAt :
+ (UPInt)UTF8Util::GetByteIndex(posAt, pData, (SPInt)Size);
+
+ OVR_ASSERT(byteIndex <= oldSize);
+ Reserve(oldSize + insertSize);
+
+ memmove(pData + byteIndex + insertSize, pData + byteIndex, oldSize - byteIndex + 1);
+ memcpy (pData + byteIndex, substr, insertSize);
+ LengthIsSize = false;
+ Size = oldSize + insertSize;
+ pData[Size] = 0;
+}
+
+// Inserts character at posAt
+UPInt StringBuffer::InsertCharAt(UInt32 c, UPInt posAt)
+{
+ char buf[8];
+ SPInt len = 0;
+ UTF8Util::EncodeChar(buf, &len, c);
+ OVR_ASSERT(len >= 0);
+ buf[(UPInt)len] = 0;
+
+ Insert(buf, posAt, len);
+ return (UPInt)len;
+}
+
+} // OVR
diff --git a/LibOVR/Src/Kernel/OVR_String.h b/LibOVR/Src/Kernel/OVR_String.h
new file mode 100644
index 0000000..112878c
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_String.h
@@ -0,0 +1,645 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_String.h
+Content : String UTF8 string implementation with copy-on-write semantics
+ (thread-safe for assignment but not modification).
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#ifndef OVR_String_h
+#define OVR_String_h
+
+#include "OVR_Types.h"
+#include "OVR_Allocator.h"
+#include "OVR_UTF8Util.h"
+#include "OVR_Atomic.h"
+#include "OVR_Std.h"
+#include "OVR_Alg.h"
+
+namespace OVR {
+
+// ***** Classes
+
+class String;
+class StringBuffer;
+
+
+//-----------------------------------------------------------------------------------
+// ***** String Class
+
+// String is UTF8 based string class with copy-on-write implementation
+// for assignment.
+
+class String
+{
+protected:
+
+ enum FlagConstants
+ {
+ //Flag_GetLength = 0x7FFFFFFF,
+ // This flag is set if GetLength() == GetSize() for a string.
+ // Avoid extra scanning is Substring and indexing logic.
+ Flag_LengthIsSizeShift = (sizeof(UPInt)*8 - 1)
+ };
+
+
+ // Internal structure to hold string data
+ struct DataDesc
+ {
+ // Number of bytes. Will be the same as the number of chars if the characters
+ // are ascii, may not be equal to number of chars in case string data is UTF8.
+ UPInt Size;
+ volatile SInt32 RefCount;
+ char Data[1];
+
+ void AddRef()
+ {
+ AtomicOps<SInt32>::ExchangeAdd_NoSync(&RefCount, 1);
+ }
+ // Decrement ref count. This needs to be thread-safe, since
+ // a different thread could have also decremented the ref count.
+ // For example, if u start off with a ref count = 2. Now if u
+ // decrement the ref count and check against 0 in different
+ // statements, a different thread can also decrement the ref count
+ // in between our decrement and checking against 0 and will find
+ // the ref count = 0 and delete the object. This will lead to a crash
+ // when context switches to our thread and we'll be trying to delete
+ // an already deleted object. Hence decrementing the ref count and
+ // checking against 0 needs to made an atomic operation.
+ void Release()
+ {
+ if ((AtomicOps<SInt32>::ExchangeAdd_NoSync(&RefCount, -1) - 1) == 0)
+ OVR_FREE(this);
+ }
+
+ static UPInt GetLengthFlagBit() { return UPInt(1) << Flag_LengthIsSizeShift; }
+ UPInt GetSize() const { return Size & ~GetLengthFlagBit() ; }
+ UPInt GetLengthFlag() const { return Size & GetLengthFlagBit(); }
+ bool LengthIsSize() const { return GetLengthFlag() != 0; }
+ };
+
+ // Heap type of the string is encoded in the lower bits.
+ enum HeapType
+ {
+ HT_Global = 0, // Heap is global.
+ HT_Local = 1, // SF::String_loc: Heap is determined based on string's address.
+ HT_Dynamic = 2, // SF::String_temp: Heap is stored as a part of the class.
+ HT_Mask = 3
+ };
+
+ union {
+ DataDesc* pData;
+ UPInt HeapTypeBits;
+ };
+ typedef union {
+ DataDesc* pData;
+ UPInt HeapTypeBits;
+ } DataDescUnion;
+
+ inline HeapType GetHeapType() const { return (HeapType) (HeapTypeBits & HT_Mask); }
+
+ inline DataDesc* GetData() const
+ {
+ DataDescUnion u;
+ u.pData = pData;
+ u.HeapTypeBits = (u.HeapTypeBits & ~(UPInt)HT_Mask);
+ return u.pData;
+ }
+
+ inline void SetData(DataDesc* pdesc)
+ {
+ HeapType ht = GetHeapType();
+ pData = pdesc;
+ OVR_ASSERT((HeapTypeBits & HT_Mask) == 0);
+ HeapTypeBits |= ht;
+ }
+
+
+ DataDesc* AllocData(UPInt size, UPInt lengthIsSize);
+ DataDesc* AllocDataCopy1(UPInt size, UPInt lengthIsSize,
+ const char* pdata, UPInt copySize);
+ DataDesc* AllocDataCopy2(UPInt size, UPInt lengthIsSize,
+ const char* pdata1, UPInt copySize1,
+ const char* pdata2, UPInt copySize2);
+
+ // Special constructor to avoid data initalization when used in derived class.
+ struct NoConstructor { };
+ String(const NoConstructor&) { }
+
+public:
+
+ // For initializing string with dynamic buffer
+ struct InitStruct
+ {
+ virtual ~InitStruct() { }
+ virtual void InitString(char* pbuffer, UPInt size) const = 0;
+ };
+
+
+ // Constructors / Destructors.
+ String();
+ String(const char* data);
+ String(const char* data1, const char* pdata2, const char* pdata3 = 0);
+ String(const char* data, UPInt buflen);
+ String(const String& src);
+ String(const StringBuffer& src);
+ String(const InitStruct& src, UPInt size);
+ explicit String(const wchar_t* data);
+
+ // Destructor (Captain Obvious guarantees!)
+ ~String()
+ {
+ GetData()->Release();
+ }
+
+ // Declaration of NullString
+ static DataDesc NullData;
+
+
+ // *** General Functions
+
+ void Clear();
+
+ // For casting to a pointer to char.
+ operator const char*() const { return GetData()->Data; }
+ // Pointer to raw buffer.
+ const char* ToCStr() const { return GetData()->Data; }
+
+ // Returns number of bytes
+ UPInt GetSize() const { return GetData()->GetSize() ; }
+ // Tells whether or not the string is empty
+ bool IsEmpty() const { return GetSize() == 0; }
+
+ // Returns number of characters
+ UPInt GetLength() const;
+
+ // Returns character at the specified index
+ UInt32 GetCharAt(UPInt index) const;
+ UInt32 GetFirstCharAt(UPInt index, const char** offset) const;
+ UInt32 GetNextChar(const char** offset) const;
+
+ // Appends a character
+ void AppendChar(UInt32 ch);
+
+ // Append a string
+ void AppendString(const wchar_t* pstr, SPInt len = -1);
+ void AppendString(const char* putf8str, SPInt utf8StrSz = -1);
+
+ // Assigned a string with dynamic data (copied through initializer).
+ void AssignString(const InitStruct& src, UPInt size);
+ // Assigns string with known size.
+ void AssignString(const char* putf8str, UPInt size);
+
+ // Resize the string to the new size
+// void Resize(UPInt _size);
+
+ // Removes the character at posAt
+ void Remove(UPInt posAt, SPInt len = 1);
+
+ // Returns a String that's a substring of this.
+ // -start is the index of the first UTF8 character you want to include.
+ // -end is the index one past the last UTF8 character you want to include.
+ String Substring(UPInt start, UPInt end) const;
+
+ // Case-conversion
+ String ToUpper() const;
+ String ToLower() const;
+
+ // Inserts substr at posAt
+ String& Insert (const char* substr, UPInt posAt, SPInt len = -1);
+
+ // Inserts character at posAt
+ UPInt InsertCharAt(UInt32 c, UPInt posAt);
+
+ // Inserts substr at posAt, which is an index of a character (not byte).
+ // Of size is specified, it is in bytes.
+// String& Insert(const UInt32* substr, UPInt posAt, SPInt size = -1);
+
+ // Get Byte index of the character at position = index
+ UPInt GetByteIndex(UPInt index) const { return (UPInt)UTF8Util::GetByteIndex(index, GetData()->Data); }
+
+ // Utility: case-insensitive string compare. stricmp() & strnicmp() are not
+ // ANSI or POSIX, do not seem to appear in Linux.
+ static int OVR_STDCALL CompareNoCase(const char* a, const char* b);
+ static int OVR_STDCALL CompareNoCase(const char* a, const char* b, SPInt len);
+
+ // Hash function, case-insensitive
+ static UPInt OVR_STDCALL BernsteinHashFunctionCIS(const void* pdataIn, UPInt size, UPInt seed = 5381);
+
+ // Hash function, case-sensitive
+ static UPInt OVR_STDCALL BernsteinHashFunction(const void* pdataIn, UPInt size, UPInt seed = 5381);
+
+
+ // ***** File path parsing helper functions.
+ // Implemented in OVR_String_FilePath.cpp.
+
+ // Absolute paths can star with:
+ // - protocols: 'file://', 'http://'
+ // - windows drive: 'c:\'
+ // - UNC share name: '\\share'
+ // - unix root '/'
+ static bool HasAbsolutePath(const char* path);
+ static bool HasExtension(const char* path);
+ static bool HasProtocol(const char* path);
+
+ bool HasAbsolutePath() const { return HasAbsolutePath(ToCStr()); }
+ bool HasExtension() const { return HasExtension(ToCStr()); }
+ bool HasProtocol() const { return HasProtocol(ToCStr()); }
+
+ String GetProtocol() const; // Returns protocol, if any, with trailing '://'.
+ String GetPath() const; // Returns path with trailing '/'.
+ String GetFilename() const; // Returns filename, including extension.
+ String GetExtension() const; // Returns extension with a dot.
+
+ void StripProtocol(); // Strips front protocol, if any, from the string.
+ void StripExtension(); // Strips off trailing extension.
+
+
+ // Operators
+ // Assignment
+ void operator = (const char* str);
+ void operator = (const wchar_t* str);
+ void operator = (const String& src);
+ void operator = (const StringBuffer& src);
+
+ // Addition
+ void operator += (const String& src);
+ void operator += (const char* psrc) { AppendString(psrc); }
+ void operator += (const wchar_t* psrc) { AppendString(psrc); }
+ void operator += (char ch) { AppendChar(ch); }
+ String operator + (const char* str) const;
+ String operator + (const String& src) const;
+
+ // Comparison
+ bool operator == (const String& str) const
+ {
+ return (OVR_strcmp(GetData()->Data, str.GetData()->Data)== 0);
+ }
+
+ bool operator != (const String& str) const
+ {
+ return !operator == (str);
+ }
+
+ bool operator == (const char* str) const
+ {
+ return OVR_strcmp(GetData()->Data, str) == 0;
+ }
+
+ bool operator != (const char* str) const
+ {
+ return !operator == (str);
+ }
+
+ bool operator < (const char* pstr) const
+ {
+ return OVR_strcmp(GetData()->Data, pstr) < 0;
+ }
+
+ bool operator < (const String& str) const
+ {
+ return *this < str.GetData()->Data;
+ }
+
+ bool operator > (const char* pstr) const
+ {
+ return OVR_strcmp(GetData()->Data, pstr) > 0;
+ }
+
+ bool operator > (const String& str) const
+ {
+ return *this > str.GetData()->Data;
+ }
+
+ int CompareNoCase(const char* pstr) const
+ {
+ return CompareNoCase(GetData()->Data, pstr);
+ }
+ int CompareNoCase(const String& str) const
+ {
+ return CompareNoCase(GetData()->Data, str.ToCStr());
+ }
+
+ // Accesses raw bytes
+ const char& operator [] (int index) const
+ {
+ OVR_ASSERT(index >= 0 && (UPInt)index < GetSize());
+ return GetData()->Data[index];
+ }
+ const char& operator [] (UPInt index) const
+ {
+ OVR_ASSERT(index < GetSize());
+ return GetData()->Data[index];
+ }
+
+
+ // Case insensitive keys are used to look up insensitive string in hash tables
+ // for SWF files with version before SWF 7.
+ struct NoCaseKey
+ {
+ const String* pStr;
+ NoCaseKey(const String &str) : pStr(&str){};
+ };
+
+ bool operator == (const NoCaseKey& strKey) const
+ {
+ return (CompareNoCase(ToCStr(), strKey.pStr->ToCStr()) == 0);
+ }
+ bool operator != (const NoCaseKey& strKey) const
+ {
+ return !(CompareNoCase(ToCStr(), strKey.pStr->ToCStr()) == 0);
+ }
+
+ // Hash functor used for strings.
+ struct HashFunctor
+ {
+ UPInt operator()(const String& data) const
+ {
+ UPInt size = data.GetSize();
+ return String::BernsteinHashFunction((const char*)data, size);
+ }
+ };
+ // Case-insensitive hash functor used for strings. Supports additional
+ // lookup based on NoCaseKey.
+ struct NoCaseHashFunctor
+ {
+ UPInt operator()(const String& data) const
+ {
+ UPInt size = data.GetSize();
+ return String::BernsteinHashFunctionCIS((const char*)data, size);
+ }
+ UPInt operator()(const NoCaseKey& data) const
+ {
+ UPInt size = data.pStr->GetSize();
+ return String::BernsteinHashFunctionCIS((const char*)data.pStr->ToCStr(), size);
+ }
+ };
+
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** String Buffer used for Building Strings
+
+class StringBuffer
+{
+ char* pData;
+ UPInt Size;
+ UPInt BufferSize;
+ UPInt GrowSize;
+ mutable bool LengthIsSize;
+
+public:
+
+ // Constructors / Destructor.
+ StringBuffer();
+ explicit StringBuffer(UPInt growSize);
+ StringBuffer(const char* data);
+ StringBuffer(const char* data, UPInt buflen);
+ StringBuffer(const String& src);
+ StringBuffer(const StringBuffer& src);
+ explicit StringBuffer(const wchar_t* data);
+ ~StringBuffer();
+
+
+ // Modify grow size used for growing/shrinking the buffer.
+ UPInt GetGrowSize() const { return GrowSize; }
+ void SetGrowSize(UPInt growSize);
+
+
+ // *** General Functions
+ // Does not release memory, just sets Size to 0
+ void Clear();
+
+ // For casting to a pointer to char.
+ operator const char*() const { return (pData) ? pData : ""; }
+ // Pointer to raw buffer.
+ const char* ToCStr() const { return (pData) ? pData : ""; }
+
+ // Returns number of bytes.
+ UPInt GetSize() const { return Size ; }
+ // Tells whether or not the string is empty.
+ bool IsEmpty() const { return GetSize() == 0; }
+
+ // Returns number of characters
+ UPInt GetLength() const;
+
+ // Returns character at the specified index
+ UInt32 GetCharAt(UPInt index) const;
+ UInt32 GetFirstCharAt(UPInt index, const char** offset) const;
+ UInt32 GetNextChar(const char** offset) const;
+
+
+ // Resize the string to the new size
+ void Resize(UPInt _size);
+ void Reserve(UPInt _size);
+
+ // Appends a character
+ void AppendChar(UInt32 ch);
+
+ // Append a string
+ void AppendString(const wchar_t* pstr, SPInt len = -1);
+ void AppendString(const char* putf8str, SPInt utf8StrSz = -1);
+ void AppendFormat(const char* format, ...);
+
+ // Assigned a string with dynamic data (copied through initializer).
+ //void AssignString(const InitStruct& src, UPInt size);
+
+ // Inserts substr at posAt
+ void Insert (const char* substr, UPInt posAt, SPInt len = -1);
+ // Inserts character at posAt
+ UPInt InsertCharAt(UInt32 c, UPInt posAt);
+
+ // Assignment
+ void operator = (const char* str);
+ void operator = (const wchar_t* str);
+ void operator = (const String& src);
+
+ // Addition
+ void operator += (const String& src) { AppendString(src.ToCStr(),src.GetSize()); }
+ void operator += (const char* psrc) { AppendString(psrc); }
+ void operator += (const wchar_t* psrc) { AppendString(psrc); }
+ void operator += (char ch) { AppendChar(ch); }
+ //String operator + (const char* str) const ;
+ //String operator + (const String& src) const ;
+
+ // Accesses raw bytes
+ char& operator [] (int index)
+ {
+ OVR_ASSERT(((UPInt)index) < GetSize());
+ return pData[index];
+ }
+ char& operator [] (UPInt index)
+ {
+ OVR_ASSERT(index < GetSize());
+ return pData[index];
+ }
+
+ const char& operator [] (int index) const
+ {
+ OVR_ASSERT(((UPInt)index) < GetSize());
+ return pData[index];
+ }
+ const char& operator [] (UPInt index) const
+ {
+ OVR_ASSERT(index < GetSize());
+ return pData[index];
+ }
+};
+
+
+//
+// Wrapper for string data. The data must have a guaranteed
+// lifespan throughout the usage of the wrapper. Not intended for
+// cached usage. Not thread safe.
+//
+class StringDataPtr
+{
+public:
+ StringDataPtr() : pStr(NULL), Size(0) {}
+ StringDataPtr(const StringDataPtr& p)
+ : pStr(p.pStr), Size(p.Size) {}
+ StringDataPtr(const char* pstr, UPInt sz)
+ : pStr(pstr), Size(sz) {}
+ StringDataPtr(const char* pstr)
+ : pStr(pstr), Size((pstr != NULL) ? OVR_strlen(pstr) : 0) {}
+ explicit StringDataPtr(const String& str)
+ : pStr(str.ToCStr()), Size(str.GetSize()) {}
+ template <typename T, int N>
+ StringDataPtr(const T (&v)[N])
+ : pStr(v), Size(N) {}
+
+public:
+ const char* ToCStr() const { return pStr; }
+ UPInt GetSize() const { return Size; }
+ bool IsEmpty() const { return GetSize() == 0; }
+
+ // value is a prefix of this string
+ // Character's values are not compared.
+ bool IsPrefix(const StringDataPtr& value) const
+ {
+ return ToCStr() == value.ToCStr() && GetSize() >= value.GetSize();
+ }
+ // value is a suffix of this string
+ // Character's values are not compared.
+ bool IsSuffix(const StringDataPtr& value) const
+ {
+ return ToCStr() <= value.ToCStr() && (End()) == (value.End());
+ }
+
+ // Find first character.
+ // init_ind - initial index.
+ SPInt FindChar(char c, UPInt init_ind = 0) const
+ {
+ for (UPInt i = init_ind; i < GetSize(); ++i)
+ if (pStr[i] == c)
+ return static_cast<SPInt>(i);
+
+ return -1;
+ }
+
+ // Find last character.
+ // init_ind - initial index.
+ SPInt FindLastChar(char c, UPInt init_ind = ~0) const
+ {
+ if (init_ind == (UPInt)~0 || init_ind > GetSize())
+ init_ind = GetSize();
+ else
+ ++init_ind;
+
+ for (UPInt i = init_ind; i > 0; --i)
+ if (pStr[i - 1] == c)
+ return static_cast<SPInt>(i - 1);
+
+ return -1;
+ }
+
+ // Create new object and trim size bytes from the left.
+ StringDataPtr GetTrimLeft(UPInt size) const
+ {
+ // Limit trim size to the size of the string.
+ size = Alg::PMin(GetSize(), size);
+
+ return StringDataPtr(ToCStr() + size, GetSize() - size);
+ }
+ // Create new object and trim size bytes from the right.
+ StringDataPtr GetTrimRight(UPInt size) const
+ {
+ // Limit trim to the size of the string.
+ size = Alg::PMin(GetSize(), size);
+
+ return StringDataPtr(ToCStr(), GetSize() - size);
+ }
+
+ // Create new object, which contains next token.
+ // Useful for parsing.
+ StringDataPtr GetNextToken(char separator = ':') const
+ {
+ UPInt cur_pos = 0;
+ const char* cur_str = ToCStr();
+
+ for (; cur_pos < GetSize() && cur_str[cur_pos]; ++cur_pos)
+ {
+ if (cur_str[cur_pos] == separator)
+ {
+ break;
+ }
+ }
+
+ return StringDataPtr(ToCStr(), cur_pos);
+ }
+
+ // Trim size bytes from the left.
+ StringDataPtr& TrimLeft(UPInt size)
+ {
+ // Limit trim size to the size of the string.
+ size = Alg::PMin(GetSize(), size);
+ pStr += size;
+ Size -= size;
+
+ return *this;
+ }
+ // Trim size bytes from the right.
+ StringDataPtr& TrimRight(UPInt size)
+ {
+ // Limit trim to the size of the string.
+ size = Alg::PMin(GetSize(), size);
+ Size -= size;
+
+ return *this;
+ }
+
+ const char* Begin() const { return ToCStr(); }
+ const char* End() const { return ToCStr() + GetSize(); }
+
+ // Hash functor used string data pointers
+ struct HashFunctor
+ {
+ UPInt operator()(const StringDataPtr& data) const
+ {
+ return String::BernsteinHashFunction(data.ToCStr(), data.GetSize());
+ }
+ };
+
+ bool operator== (const StringDataPtr& data) const
+ {
+ return (OVR_strncmp(pStr, data.pStr, data.Size) == 0);
+ }
+
+protected:
+ const char* pStr;
+ UPInt Size;
+};
+
+} // OVR
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_StringHash.h b/LibOVR/Src/Kernel/OVR_StringHash.h
new file mode 100644
index 0000000..237dc7e
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_StringHash.h
@@ -0,0 +1,89 @@
+/************************************************************************************
+
+PublicHeader: None
+Filename : OVR_StringHash.h
+Content : String hash table used when optional case-insensitive
+ lookup is required.
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#ifndef OVR_StringHash_h
+#define OVR_StringHash_h
+
+#include "OVR_String.h"
+#include "OVR_Hash.h"
+
+namespace OVR {
+
+//-----------------------------------------------------------------------------------
+// *** StringHash
+
+// This is a custom string hash table that supports case-insensitive
+// searches through special functions such as GetCaseInsensitive, etc.
+// This class is used for Flash labels, exports and other case-insensitive tables.
+
+template<class U, class Allocator = ContainerAllocator<U> >
+class StringHash : public Hash<String, U, String::NoCaseHashFunctor, Allocator>
+{
+public:
+ typedef U ValueType;
+ typedef StringHash<U, Allocator> SelfType;
+ typedef Hash<String, U, String::NoCaseHashFunctor, Allocator> BaseType;
+
+public:
+
+ void operator = (const SelfType& src) { BaseType::operator = (src); }
+
+ bool GetCaseInsensitive(const String& key, U* pvalue) const
+ {
+ String::NoCaseKey ikey(key);
+ return BaseType::GetAlt(ikey, pvalue);
+ }
+ // Pointer-returning get variety.
+ const U* GetCaseInsensitive(const String& key) const
+ {
+ String::NoCaseKey ikey(key);
+ return BaseType::GetAlt(ikey);
+ }
+ U* GetCaseInsensitive(const String& key)
+ {
+ String::NoCaseKey ikey(key);
+ return BaseType::GetAlt(ikey);
+ }
+
+
+ typedef typename BaseType::Iterator base_iterator;
+
+ base_iterator FindCaseInsensitive(const String& key)
+ {
+ String::NoCaseKey ikey(key);
+ return BaseType::FindAlt(ikey);
+ }
+
+ // Set just uses a find and assigns value if found. The key is not modified;
+ // this behavior is identical to Flash string variable assignment.
+ void SetCaseInsensitive(const String& key, const U& value)
+ {
+ base_iterator it = FindCaseInsensitive(key);
+ if (it != BaseType::End())
+ {
+ it->Second = value;
+ }
+ else
+ {
+ BaseType::Add(key, value);
+ }
+ }
+};
+
+} // OVR
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_String_FormatUtil.cpp b/LibOVR/Src/Kernel/OVR_String_FormatUtil.cpp
new file mode 100644
index 0000000..a6ceb3a
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_String_FormatUtil.cpp
@@ -0,0 +1,42 @@
+/************************************************************************************
+
+Filename : OVR_String_FormatUtil.cpp
+Content : String format functions.
+Created : February 27, 2013
+Notes :
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#include "OVR_String.h"
+#include "OVR_Log.h"
+
+namespace OVR {
+
+void StringBuffer::AppendFormat(const char* format, ...)
+{
+ va_list argList;
+
+ va_start(argList, format);
+ UPInt size = OVR_vscprintf(format, argList);
+ va_end(argList);
+
+ char* buffer = (char*) OVR_ALLOC(sizeof(char) * (size+1));
+
+ va_start(argList, format);
+ UPInt result = OVR_vsprintf(buffer, size+1, format, argList);
+ OVR_UNUSED1(result);
+ va_end(argList);
+ OVR_ASSERT_LOG(result == size, ("Error in OVR_vsprintf"));
+
+ AppendString(buffer);
+
+ OVR_FREE(buffer);
+}
+
+} // OVR
diff --git a/LibOVR/Src/Kernel/OVR_String_PathUtil.cpp b/LibOVR/Src/Kernel/OVR_String_PathUtil.cpp
new file mode 100644
index 0000000..27e79c6
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_String_PathUtil.cpp
@@ -0,0 +1,200 @@
+/************************************************************************************
+
+Filename : OVR_String_PathUtil.cpp
+Content : String filename/url helper function
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#include "OVR_String.h"
+#include "OVR_UTF8Util.h"
+
+namespace OVR {
+
+//--------------------------------------------------------------------
+// ***** Path-Scanner helper function
+
+// Scans file path finding filename start and extension start, fills in their addess.
+void ScanFilePath(const char* url, const char** pfilename, const char** pext)
+{
+ const char* urlStart = url;
+ const char *filename = 0;
+ const char *lastDot = 0;
+
+ UInt32 charVal = UTF8Util::DecodeNextChar(&url);
+
+ while (charVal != 0)
+ {
+ if ((charVal == '/') || (charVal == '\\'))
+ {
+ filename = url;
+ lastDot = 0;
+ }
+ else if (charVal == '.')
+ {
+ lastDot = url - 1;
+ }
+
+ charVal = UTF8Util::DecodeNextChar(&url);
+ }
+
+ if (pfilename)
+ {
+ // It was a naked filename
+ if (urlStart && (*urlStart != '.') && *urlStart)
+ *pfilename = urlStart;
+ else
+ *pfilename = filename;
+ }
+
+ if (pext)
+ {
+ *pext = lastDot;
+ }
+}
+
+// Scans till the end of protocol. Returns first character past protocol,
+// 0 if not found.
+// - protocol: 'file://', 'http://'
+const char* ScanPathProtocol(const char* url)
+{
+ UInt32 charVal = UTF8Util::DecodeNextChar(&url);
+ UInt32 charVal2;
+
+ while (charVal != 0)
+ {
+ // Treat a colon followed by a slash as absolute.
+ if (charVal == ':')
+ {
+ charVal2 = UTF8Util::DecodeNextChar(&url);
+ charVal = UTF8Util::DecodeNextChar(&url);
+ if ((charVal == '/') && (charVal2 == '\\'))
+ return url;
+ }
+ charVal = UTF8Util::DecodeNextChar(&url);
+ }
+ return 0;
+}
+
+
+//--------------------------------------------------------------------
+// ***** String Path API implementation
+
+bool String::HasAbsolutePath(const char* url)
+{
+ // Absolute paths can star with:
+ // - protocols: 'file://', 'http://'
+ // - windows drive: 'c:\'
+ // - UNC share name: '\\share'
+ // - unix root '/'
+
+ // On the other hand, relative paths are:
+ // - directory: 'directory/file'
+ // - this directory: './file'
+ // - parent directory: '../file'
+ //
+ // For now, we don't parse '.' or '..' out, but instead let it be concatenated
+ // to string and let the OS figure it out. This, however, is not good for file
+ // name matching in library/etc, so it should be improved.
+
+ if (!url || !*url)
+ return true; // Treat empty strings as absolute.
+
+ UInt32 charVal = UTF8Util::DecodeNextChar(&url);
+
+ // Fist character of '/' or '\\' means absolute url.
+ if ((charVal == '/') || (charVal == '\\'))
+ return true;
+
+ while (charVal != 0)
+ {
+ // Treat a colon followed by a slash as absolute.
+ if (charVal == ':')
+ {
+ charVal = UTF8Util::DecodeNextChar(&url);
+ // Protocol or windows drive. Absolute.
+ if ((charVal == '/') || (charVal == '\\'))
+ return true;
+ }
+ else if ((charVal == '/') || (charVal == '\\'))
+ {
+ // Not a first character (else 'if' above the loop would have caught it).
+ // Must be a relative url.
+ break;
+ }
+
+ charVal = UTF8Util::DecodeNextChar(&url);
+ }
+
+ // We get here for relative paths.
+ return false;
+}
+
+
+bool String::HasExtension(const char* path)
+{
+ const char* ext = 0;
+ ScanFilePath(path, 0, &ext);
+ return ext != 0;
+}
+bool String::HasProtocol(const char* path)
+{
+ return ScanPathProtocol(path) != 0;
+}
+
+
+String String::GetPath() const
+{
+ const char* filename = 0;
+ ScanFilePath(ToCStr(), &filename, 0);
+
+ // Technically we can have extra logic somewhere for paths,
+ // such as enforcing protocol and '/' only based on flags,
+ // but we keep it simple for now.
+ return String(ToCStr(), filename ? (filename-ToCStr()) : GetSize());
+}
+
+String String::GetProtocol() const
+{
+ const char* protocolEnd = ScanPathProtocol(ToCStr());
+ return String(ToCStr(), protocolEnd ? (protocolEnd-ToCStr()) : 0);
+}
+
+String String::GetFilename() const
+{
+ const char* filename = 0;
+ ScanFilePath(ToCStr(), &filename, 0);
+ return String(filename);
+}
+String String::GetExtension() const
+{
+ const char* ext = 0;
+ ScanFilePath(ToCStr(), 0, &ext);
+ return String(ext);
+}
+
+void String::StripExtension()
+{
+ const char* ext = 0;
+ ScanFilePath(ToCStr(), 0, &ext);
+ if (ext)
+ {
+ *this = String(ToCStr(), ext-ToCStr());
+ }
+}
+
+void String::StripProtocol()
+{
+ const char* protocol = ScanPathProtocol(ToCStr());
+ if (protocol)
+ AssignString(protocol, OVR_strlen(protocol));
+}
+
+} // OVR
diff --git a/LibOVR/Src/Kernel/OVR_SysFile.cpp b/LibOVR/Src/Kernel/OVR_SysFile.cpp
new file mode 100644
index 0000000..f72a8d2
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_SysFile.cpp
@@ -0,0 +1,125 @@
+/**************************************************************************
+
+Filename : OVR_SysFile.cpp
+Content : File wrapper class implementation (Win32)
+
+Created : April 5, 1999
+Authors : Michael Antonov
+
+Copyright : Copyright 2011 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+**************************************************************************/
+
+#define GFILE_CXX
+
+// Standard C library (Captain Obvious guarantees!)
+#include <stdio.h>
+
+#include "OVR_SysFile.h"
+
+namespace OVR {
+
+// This is - a dummy file that fails on all calls.
+
+class UnopenedFile : public File
+{
+public:
+ UnopenedFile() { }
+ ~UnopenedFile() { }
+
+ virtual const char* GetFilePath() { return 0; }
+
+ // ** File Information
+ virtual bool IsValid() { return 0; }
+ virtual bool IsWritable() { return 0; }
+
+ // Return position / file size
+ virtual int Tell() { return 0; }
+ virtual SInt64 LTell() { return 0; }
+ virtual int GetLength() { return 0; }
+ virtual SInt64 LGetLength() { return 0; }
+
+// virtual bool Stat(FileStats *pfs) { return 0; }
+ virtual int GetErrorCode() { return Error_FileNotFound; }
+
+ // ** Stream implementation & I/O
+ virtual int Write(const UByte *pbuffer, int numBytes) { return -1; OVR_UNUSED2(pbuffer, numBytes); }
+ virtual int Read(UByte *pbuffer, int numBytes) { return -1; OVR_UNUSED2(pbuffer, numBytes); }
+ virtual int SkipBytes(int numBytes) { return 0; OVR_UNUSED(numBytes); }
+ virtual int BytesAvailable() { return 0; }
+ virtual bool Flush() { return 0; }
+ virtual int Seek(int offset, int origin) { return -1; OVR_UNUSED2(offset, origin); }
+ virtual SInt64 LSeek(SInt64 offset, int origin) { return -1; OVR_UNUSED2(offset, origin); }
+
+ virtual int CopyFromStream(File *pstream, int byteSize) { return -1; OVR_UNUSED2(pstream, byteSize); }
+ virtual bool Close() { return 0; }
+};
+
+
+
+// ***** System File
+
+// System file is created to access objects on file system directly
+// This file can refer directly to path
+
+// ** Constructor
+SysFile::SysFile() : DelegatedFile(0)
+{
+ pFile = *new UnopenedFile;
+}
+
+File* FileFILEOpen(const String& path, int flags, int mode);
+
+// Opens a file
+SysFile::SysFile(const String& path, int flags, int mode) : DelegatedFile(0)
+{
+ Open(path, flags, mode);
+}
+
+
+// ** Open & management
+// Will fail if file's already open
+bool SysFile::Open(const String& path, int flags, int mode)
+{
+ pFile = *FileFILEOpen(path, flags, mode);
+ if ((!pFile) || (!pFile->IsValid()))
+ {
+ pFile = *new UnopenedFile;
+ return 0;
+ }
+ //pFile = *OVR_NEW DelegatedFile(pFile); // MA Testing
+ if (flags & Open_Buffered)
+ pFile = *new BufferedFile(pFile);
+ return 1;
+}
+
+
+// ** Overrides
+
+int SysFile::GetErrorCode()
+{
+ return pFile ? pFile->GetErrorCode() : Error_FileNotFound;
+}
+
+
+// Overrides to provide re-open support
+bool SysFile::IsValid()
+{
+ return pFile && pFile->IsValid();
+}
+bool SysFile::Close()
+{
+ if (IsValid())
+ {
+ DelegatedFile::Close();
+ pFile = *new UnopenedFile;
+ return 1;
+ }
+ return 0;
+}
+
+} // OVR
diff --git a/LibOVR/Src/Kernel/OVR_SysFile.h b/LibOVR/Src/Kernel/OVR_SysFile.h
new file mode 100644
index 0000000..a9bb0a0
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_SysFile.h
@@ -0,0 +1,93 @@
+/************************************************************************************
+
+PublicHeader: Kernel
+Filename : OVR_SysFile.h
+Content : Header for all internal file management - functions and structures
+ to be inherited by OS specific subclasses.
+Created : September 19, 2012
+Notes :
+
+Notes : errno may not be preserved across use of GBaseFile member functions
+ : Directories cannot be deleted while files opened from them are in use
+ (For the GetFullName function)
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#ifndef OVR_SysFile_h
+#define OVR_SysFile_h
+
+#include "OVR_File.h"
+
+namespace OVR {
+
+// ***** Declared classes
+class SysFile;
+
+//-----------------------------------------------------------------------------------
+// *** File Statistics
+
+// This class contents are similar to _stat, providing
+// creation, modify and other information about the file.
+struct FileStat
+{
+ // No change or create time because they are not available on most systems
+ SInt64 ModifyTime;
+ SInt64 AccessTime;
+ SInt64 FileSize;
+
+ bool operator== (const FileStat& stat) const
+ {
+ return ( (ModifyTime == stat.ModifyTime) &&
+ (AccessTime == stat.AccessTime) &&
+ (FileSize == stat.FileSize) );
+ }
+};
+
+//-----------------------------------------------------------------------------------
+// *** System File
+
+// System file is created to access objects on file system directly
+// This file can refer directly to path.
+// System file can be open & closed several times; however, such use is not recommended
+// This class is realy a wrapper around an implementation of File interface for a
+// particular platform.
+
+class SysFile : public DelegatedFile
+{
+protected:
+ SysFile(const SysFile &source) : DelegatedFile () { OVR_UNUSED(source); }
+public:
+
+ // ** Constructor
+ SysFile();
+ // Opens a file
+ SysFile(const String& path, int flags = Open_Read|Open_Buffered, int mode = Mode_ReadWrite);
+
+ // ** Open & management
+ bool Open(const String& path, int flags = Open_Read|Open_Buffered, int mode = Mode_ReadWrite);
+
+ OVR_FORCE_INLINE bool Create(const String& path, int mode = Mode_ReadWrite)
+ { return Open(path, Open_ReadWrite|Open_Create, mode); }
+
+ // Helper function: obtain file statistics information. In GFx, this is used to detect file changes.
+ // Return 0 if function failed, most likely because the file doesn't exist.
+ static bool OVR_CDECL GetFileStat(FileStat* pfileStats, const String& path);
+
+ // ** Overrides
+ // Overridden to provide re-open support
+ virtual int GetErrorCode();
+
+ virtual bool IsValid();
+
+ virtual bool Close();
+};
+
+} // Scaleform
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_System.cpp b/LibOVR/Src/Kernel/OVR_System.cpp
new file mode 100644
index 0000000..8eba8e2
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_System.cpp
@@ -0,0 +1,70 @@
+/************************************************************************************
+
+Filename : OVR_System.cpp
+Content : General kernel initialization/cleanup, including that
+ of the memory allocator.
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#include "OVR_System.h"
+#include "OVR_Threads.h"
+#include "OVR_Timer.h"
+
+namespace OVR {
+
+// ***** OVR::System Implementation
+
+// Initializes System core, installing allocator.
+void System::Init(Log* log, Allocator *palloc)
+{
+ if (!Allocator::GetInstance())
+ {
+ Log::SetGlobalLog(log);
+ Timer::initializeTimerSystem();
+ Allocator::setInstance(palloc);
+ }
+ else
+ {
+ OVR_DEBUG_LOG(("System::Init failed - duplicate call."));
+ }
+}
+
+void System::Destroy()
+{
+ if (Allocator::GetInstance())
+ {
+ // Wait for all threads to finish; this must be done so that memory
+ // allocator and all destructors finalize correctly.
+#ifdef OVR_ENABLE_THREADS
+ Thread::FinishAllThreads();
+#endif
+
+ // Shutdown heap and destroy SysAlloc singleton, if any.
+ Allocator::GetInstance()->onSystemShutdown();
+ Allocator::setInstance(0);
+
+ Timer::shutdownTimerSystem();
+ Log::SetGlobalLog(Log::GetDefaultLog());
+ }
+ else
+ {
+ OVR_DEBUG_LOG(("System::Destroy failed - System not initialized."));
+ }
+}
+
+// Returns 'true' if system was properly initialized.
+bool System::IsInitialized()
+{
+ return Allocator::GetInstance() != 0;
+}
+
+} // OVR
+
diff --git a/LibOVR/Src/Kernel/OVR_System.h b/LibOVR/Src/Kernel/OVR_System.h
new file mode 100644
index 0000000..e53b4c8
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_System.h
@@ -0,0 +1,67 @@
+/************************************************************************************
+
+PublicHeader: OVR
+Filename : OVR_System.h
+Content : General kernel initialization/cleanup, including that
+ of the memory allocator.
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#ifndef OVR_System_h
+#define OVR_System_h
+
+#include "OVR_Allocator.h"
+#include "OVR_Log.h"
+
+namespace OVR {
+
+// ***** System Core Initialization class
+
+// System initialization must take place before any other OVR_Kernel objects are used;
+// this is done my calling System::Init(). Among other things, this is necessary to
+// initialize the memory allocator. Similarly, System::Destroy must be
+// called before program exist for proper cleanup. Both of these tasks can be achieved by
+// simply creating System object first, allowing its constructor/destructor do the work.
+
+// TBD: Require additional System class for Oculus Rift API?
+
+class System
+{
+public:
+
+ // System constructor expects allocator to be specified, if it is being substituted.
+ System(Log* log = Log::ConfigureDefaultLog(LogMask_Debug),
+ Allocator* palloc = DefaultAllocator::InitSystemSingleton())
+ {
+ Init(log, palloc);
+ }
+
+ ~System()
+ {
+ Destroy();
+ }
+
+ // Returns 'true' if system was properly initialized.
+ static bool OVR_CDECL IsInitialized();
+
+ // Initializes System core. Users can override memory implementation by passing
+ // a different Allocator here.
+ static void OVR_CDECL Init(Log* log = Log::ConfigureDefaultLog(LogMask_Debug),
+ Allocator *palloc = DefaultAllocator::InitSystemSingleton());
+
+ // De-initializes System more, finalizing the threading system and destroying
+ // the global memory allocator.
+ static void OVR_CDECL Destroy();
+};
+
+} // OVR
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_Threads.h b/LibOVR/Src/Kernel/OVR_Threads.h
new file mode 100644
index 0000000..9c29fe0
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_Threads.h
@@ -0,0 +1,396 @@
+/************************************************************************************
+
+PublicHeader: None
+Filename : OVR_Threads.h
+Content : Contains thread-related (safe) functionality
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+#ifndef OVR_Threads_h
+#define OVR_Threads_h
+
+#include "OVR_Types.h"
+#include "OVR_Atomic.h"
+#include "OVR_RefCount.h"
+#include "OVR_Array.h"
+
+// Defines the infinite wait delay timeout
+#define OVR_WAIT_INFINITE 0xFFFFFFFF
+
+// To be defined in the project configuration options
+#ifdef OVR_ENABLE_THREADS
+
+
+namespace OVR {
+
+//-----------------------------------------------------------------------------------
+// ****** Declared classes
+
+// Declared with thread support only
+class Mutex;
+class WaitCondition;
+class Event;
+// Implementation forward declarations
+class MutexImpl;
+class WaitConditionImpl;
+
+
+
+//-----------------------------------------------------------------------------------
+// ***** Mutex
+
+// Mutex class represents a system Mutex synchronization object that provides access
+// serialization between different threads, allowing one thread mutually exclusive access
+// to a resource. Mutex is more heavy-weight then Lock, but supports WaitCondition.
+
+class Mutex
+{
+ friend class WaitConditionImpl;
+ friend class MutexImpl;
+
+ MutexImpl *pImpl;
+
+public:
+ // Constructor/destructor
+ Mutex(bool recursive = 1);
+ ~Mutex();
+
+ // Locking functions
+ void DoLock();
+ bool TryLock();
+ void Unlock();
+
+ // Returns 1 if the mutes is currently locked by another thread
+ // Returns 0 if the mutex is not locked by another thread, and can therefore be acquired.
+ bool IsLockedByAnotherThread();
+
+ // Locker class; Used for automatic locking of a mutex withing scope
+ class Locker
+ {
+ public:
+ Mutex *pMutex;
+ Locker(Mutex *pmutex)
+ { pMutex = pmutex; pMutex->DoLock(); }
+ ~Locker()
+ { pMutex->Unlock(); }
+ };
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** WaitCondition
+
+/*
+ WaitCondition is a synchronization primitive that can be used to implement what is known as a monitor.
+ Dependent threads wait on a wait condition by calling Wait(), and get woken up by other threads that
+ call Notify() or NotifyAll().
+
+ The unique feature of this class is that it provides an atomic way of first releasing a Mutex, and then
+ starting a wait on a wait condition. If both the mutex and the wait condition are associated with the same
+ resource, this ensures that any condition checked for while the mutex was locked does not change before
+ the wait on the condition is actually initiated.
+*/
+
+class WaitCondition
+{
+ friend class WaitConditionImpl;
+ // Internal implementation structure
+ WaitConditionImpl *pImpl;
+
+public:
+ // Constructor/destructor
+ WaitCondition();
+ ~WaitCondition();
+
+ // Release mutex and wait for condition. The mutex is re-aquired after the wait.
+ // Delay is specified in milliseconds (1/1000 of a second).
+ bool Wait(Mutex *pmutex, unsigned delay = OVR_WAIT_INFINITE);
+
+ // Notify a condition, releasing at one object waiting
+ void Notify();
+ // Notify a condition, releasing all objects waiting
+ void NotifyAll();
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** Event
+
+// Event is a wait-able synchronization object similar to Windows event.
+// Event can be waited on until it's signaled by another thread calling
+// either SetEvent or PulseEvent.
+
+class Event
+{
+ // Event state, its mutex and the wait condition
+ volatile bool State;
+ volatile bool Temporary;
+ mutable Mutex StateMutex;
+ WaitCondition StateWaitCondition;
+
+ void updateState(bool newState, bool newTemp, bool mustNotify);
+
+public:
+ Event(bool setInitially = 0) : State(setInitially), Temporary(false) { }
+ ~Event() { }
+
+ // Wait on an event condition until it is set
+ // Delay is specified in milliseconds (1/1000 of a second).
+ bool Wait(unsigned delay = OVR_WAIT_INFINITE);
+
+ // Set an event, releasing objects waiting on it
+ void SetEvent()
+ { updateState(true, false, true); }
+
+ // Reset an event, un-signaling it
+ void ResetEvent()
+ { updateState(false, false, false); }
+
+ // Set and then reset an event once a waiter is released.
+ // If threads are already waiting, they will be notified and released
+ // If threads are not waiting, the event is set until the first thread comes in
+ void PulseEvent()
+ { updateState(true, true, true); }
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** Thread class
+
+// ThreadId uniquely identifies a thread; returned by GetCurrentThreadId() and
+// Thread::GetThreadId.
+typedef void* ThreadId;
+
+
+// *** Thread flags
+
+// Indicates that the thread is has been started, i.e. Start method has been called, and threads
+// OnExit() method has not yet been called/returned.
+#define OVR_THREAD_STARTED 0x01
+// This flag is set once the thread has ran, and finished.
+#define OVR_THREAD_FINISHED 0x02
+// This flag is set temporarily if this thread was started suspended. It is used internally.
+#define OVR_THREAD_START_SUSPENDED 0x08
+// This flag is used to ask a thread to exit. Message driven threads will usually check this flag
+// and finish once it is set.
+#define OVR_THREAD_EXIT 0x10
+
+
+class Thread : public RefCountBase<Thread>
+{ // NOTE: Waitable must be the first base since it implements RefCountImpl.
+
+public:
+
+ // *** Callback functions, can be used instead of overriding Run
+
+ // Run function prototypes.
+ // Thread function and user handle passed to it, executed by the default
+ // Thread::Run implementation if not null.
+ typedef int (*ThreadFn)(Thread *pthread, void* h);
+
+ // Thread ThreadFunction1 is executed if not 0, otherwise ThreadFunction2 is tried
+ ThreadFn ThreadFunction;
+ // User handle passes to a thread
+ void* UserHandle;
+
+ // Thread state to start a thread with
+ enum ThreadState
+ {
+ NotRunning = 0,
+ Running = 1,
+ Suspended = 2
+ };
+
+ // Thread priority
+ enum ThreadPriority
+ {
+ CriticalPriority,
+ HighestPriority,
+ AboveNormalPriority,
+ NormalPriority,
+ BelowNormalPriority,
+ LowestPriority,
+ IdlePriority,
+ };
+
+ // Thread constructor parameters
+ struct CreateParams
+ {
+ CreateParams(ThreadFn func = 0, void* hand = 0, UPInt ssize = 128 * 1024,
+ int proc = -1, ThreadState state = NotRunning, ThreadPriority prior = NormalPriority)
+ : threadFunction(func), userHandle(hand), stackSize(ssize),
+ processor(proc), initialState(state), priority(prior) {}
+ ThreadFn threadFunction; // Thread function
+ void* userHandle; // User handle passes to a thread
+ UPInt stackSize; // Thread stack size
+ int processor; // Thread hardware processor
+ ThreadState initialState; //
+ ThreadPriority priority; // Thread priority
+ };
+
+ // *** Constructors
+
+ // A default constructor always creates a thread in NotRunning state, because
+ // the derived class has not yet been initialized. The derived class can call Start explicitly.
+ // "processor" parameter specifies which hardware processor this thread will be run on.
+ // -1 means OS decides this. Implemented only on Win32
+ Thread(UPInt stackSize = 128 * 1024, int processor = -1);
+ // Constructors that initialize the thread with a pointer to function.
+ // An option to start a thread is available, but it should not be used if classes are derived from Thread.
+ // "processor" parameter specifies which hardware processor this thread will be run on.
+ // -1 means OS decides this. Implemented only on Win32
+ Thread(ThreadFn threadFunction, void* userHandle = 0, UPInt stackSize = 128 * 1024,
+ int processor = -1, ThreadState initialState = NotRunning);
+ // Constructors that initialize the thread with a create parameters structure.
+ explicit Thread(const CreateParams& params);
+
+ // Destructor.
+ virtual ~Thread();
+
+ // Waits for all Threads to finish; should be called only from the root
+ // application thread. Once this function returns, we know that all other
+ // thread's references to Thread object have been released.
+ static void OVR_CDECL FinishAllThreads();
+
+
+ // *** Overridable Run function for thread processing
+
+ // - returning from this method will end the execution of the thread
+ // - return value is usually 0 for success
+ virtual int Run();
+ // Called after return/exit function
+ virtual void OnExit();
+
+
+ // *** Thread management
+
+ // Starts the thread if its not already running
+ // - internally sets up the threading and calls Run()
+ // - initial state can either be Running or Suspended, NotRunning will just fail and do nothing
+ // - returns the exit code
+ virtual bool Start(ThreadState initialState = Running);
+
+ // Quits with an exit code
+ virtual void Exit(int exitCode=0);
+
+ // Suspend the thread until resumed
+ // Returns 1 for success, 0 for failure.
+ bool Suspend();
+ // Resumes currently suspended thread
+ // Returns 1 for success, 0 for failure.
+ bool Resume();
+
+ // Static function to return a pointer to the current thread
+ //static Thread* GetThread();
+
+
+ // *** Thread status query functions
+
+ bool GetExitFlag() const;
+ void SetExitFlag(bool exitFlag);
+
+ // Determines whether the thread was running and is now finished
+ bool IsFinished() const;
+ // Determines if the thread is currently suspended
+ bool IsSuspended() const;
+ // Returns current thread state
+ ThreadState GetThreadState() const;
+
+ // Returns the number of available CPUs on the system
+ static int GetCPUCount();
+
+ // Returns the thread exit code. Exit code is initialized to 0,
+ // and set to the return value if Run function after the thread is finished.
+ inline int GetExitCode() const { return ExitCode; }
+ // Returns an OS handle
+#if defined(OVR_OS_WIN32)
+ void* GetOSHandle() const { return ThreadHandle; }
+#else
+ pthread_t GetOSHandle() const { return ThreadHandle; }
+#endif
+
+#if defined(OVR_OS_WIN32)
+ ThreadId GetThreadId() const { return IdValue; }
+#else
+ ThreadId GetThreadId() const { return (ThreadId)GetOSHandle(); }
+#endif
+
+ static int GetOSPriority(ThreadPriority);
+ // *** Sleep
+
+ // Sleep secs seconds
+ static bool Sleep(unsigned secs);
+ // Sleep msecs milliseconds
+ static bool MSleep(unsigned msecs);
+
+
+ // *** Debugging functionality
+#if defined(OVR_OS_WIN32)
+ virtual void SetThreadName( const char* name );
+#else
+ virtual void SetThreadName( const char* name ) { OVR_UNUSED(name); }
+#endif
+
+private:
+#if defined(OVR_OS_WIN32)
+ friend unsigned WINAPI Thread_Win32StartFn(void *pthread);
+
+#else
+ friend void *Thread_PthreadStartFn(void * phandle);
+
+ static int InitAttr;
+ static pthread_attr_t Attr;
+#endif
+
+protected:
+ // Thread state flags
+ AtomicInt<UInt32> ThreadFlags;
+ AtomicInt<SInt32> SuspendCount;
+ UPInt StackSize;
+
+ // Hardware processor which this thread is running on.
+ int Processor;
+ ThreadPriority Priority;
+
+#if defined(OVR_OS_WIN32)
+ void* ThreadHandle;
+ volatile ThreadId IdValue;
+
+ // System-specific cleanup function called from destructor
+ void CleanupSystemThread();
+
+#else
+ pthread_t ThreadHandle;
+#endif
+
+ // Exit code of the thread, as returned by Run.
+ int ExitCode;
+
+ // Internal run function.
+ int PRun();
+ // Finishes the thread and releases internal reference to it.
+ void FinishAndRelease();
+
+ void Init(const CreateParams& params);
+
+ // Protected copy constructor
+ Thread(const Thread &source) { OVR_UNUSED(source); }
+
+};
+
+// Returns the unique Id of a thread it is called on, intended for
+// comparison purposes.
+ThreadId GetCurrentThreadId();
+
+
+} // OVR
+
+#endif // OVR_ENABLE_THREADS
+#endif // OVR_Threads_h
diff --git a/LibOVR/Src/Kernel/OVR_ThreadsPthread.cpp b/LibOVR/Src/Kernel/OVR_ThreadsPthread.cpp
new file mode 100644
index 0000000..c40d9d8
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_ThreadsPthread.cpp
@@ -0,0 +1,795 @@
+
+#include "OVR_Threads.h"
+#include "OVR_Hash.h"
+
+#ifdef OVR_ENABLE_THREADS
+
+#include "OVR_Timer.h"
+#include "OVR_Log.h"
+
+#include <pthread.h>
+#include <time.h>
+
+#ifdef OVR_OS_PS3
+#include <sys/sys_time.h>
+#include <sys/timer.h>
+#include <sys/synchronization.h>
+#define sleep(x) sys_timer_sleep(x)
+#define usleep(x) sys_timer_usleep(x)
+using std::timespec;
+#else
+#include <unistd.h>
+#include <sys/time.h>
+#include <errno.h>
+#endif
+
+namespace OVR {
+
+// ***** Mutex implementation
+
+
+// *** Internal Mutex implementation structure
+
+class MutexImpl : public NewOverrideBase
+{
+ // System mutex or semaphore
+ pthread_mutex_t SMutex;
+ bool Recursive;
+ unsigned LockCount;
+ pthread_t LockedBy;
+
+ friend class WaitConditionImpl;
+
+public:
+ // Constructor/destructor
+ MutexImpl(Mutex* pmutex, bool recursive = 1);
+ ~MutexImpl();
+
+ // Locking functions
+ void DoLock();
+ bool TryLock();
+ void Unlock(Mutex* pmutex);
+ // Returns 1 if the mutes is currently locked
+ bool IsLockedByAnotherThread(Mutex* pmutex);
+ bool IsSignaled() const;
+};
+
+pthread_mutexattr_t Lock::RecursiveAttr;
+bool Lock::RecursiveAttrInit = 0;
+
+// *** Constructor/destructor
+MutexImpl::MutexImpl(Mutex* pmutex, bool recursive)
+{
+ Recursive = recursive;
+ LockCount = 0;
+
+ if (Recursive)
+ {
+ if (!Lock::RecursiveAttrInit)
+ {
+ pthread_mutexattr_init(&Lock::RecursiveAttr);
+ pthread_mutexattr_settype(&Lock::RecursiveAttr, PTHREAD_MUTEX_RECURSIVE);
+ Lock::RecursiveAttrInit = 1;
+ }
+
+ pthread_mutex_init(&SMutex, &Lock::RecursiveAttr);
+ }
+ else
+ pthread_mutex_init(&SMutex, 0);
+}
+
+MutexImpl::~MutexImpl()
+{
+ pthread_mutex_destroy(&SMutex);
+}
+
+
+// Lock and try lock
+void MutexImpl::DoLock()
+{
+ while (pthread_mutex_lock(&SMutex));
+ LockCount++;
+ LockedBy = pthread_self();
+}
+
+bool MutexImpl::TryLock()
+{
+ if (!pthread_mutex_trylock(&SMutex))
+ {
+ LockCount++;
+ LockedBy = pthread_self();
+ return 1;
+ }
+
+ return 0;
+}
+
+void MutexImpl::Unlock(Mutex* pmutex)
+{
+ OVR_ASSERT(pthread_self() == LockedBy && LockCount > 0);
+
+ unsigned lockCount;
+ LockCount--;
+ lockCount = LockCount;
+
+ pthread_mutex_unlock(&SMutex);
+}
+
+bool MutexImpl::IsLockedByAnotherThread(Mutex* pmutex)
+{
+ // There could be multiple interpretations of IsLocked with respect to current thread
+ if (LockCount == 0)
+ return 0;
+ if (pthread_self() != LockedBy)
+ return 1;
+ return 0;
+}
+
+bool MutexImpl::IsSignaled() const
+{
+ // An mutex is signaled if it is not locked ANYWHERE
+ // Note that this is different from IsLockedByAnotherThread function,
+ // that takes current thread into account
+ return LockCount == 0;
+}
+
+
+// *** Actual Mutex class implementation
+
+Mutex::Mutex(bool recursive)
+{
+ // NOTE: RefCount mode already thread-safe for all waitables.
+ pImpl = new MutexImpl(this, recursive);
+}
+
+Mutex::~Mutex()
+{
+ delete pImpl;
+}
+
+// Lock and try lock
+void Mutex::DoLock()
+{
+ pImpl->DoLock();
+}
+bool Mutex::TryLock()
+{
+ return pImpl->TryLock();
+}
+void Mutex::Unlock()
+{
+ pImpl->Unlock(this);
+}
+bool Mutex::IsLockedByAnotherThread()
+{
+ return pImpl->IsLockedByAnotherThread(this);
+}
+
+
+
+//-----------------------------------------------------------------------------------
+// ***** Event
+
+bool Event::Wait(unsigned delay)
+{
+ Mutex::Locker lock(&StateMutex);
+
+ // Do the correct amount of waiting
+ if (delay == OVR_WAIT_INFINITE)
+ {
+ while(!State)
+ StateWaitCondition.Wait(&StateMutex);
+ }
+ else if (delay)
+ {
+ if (!State)
+ StateWaitCondition.Wait(&StateMutex, delay);
+ }
+
+ bool state = State;
+ // Take care of temporary 'pulsing' of a state
+ if (Temporary)
+ {
+ Temporary = false;
+ State = false;
+ }
+ return state;
+}
+
+void Event::updateState(bool newState, bool newTemp, bool mustNotify)
+{
+ Mutex::Locker lock(&StateMutex);
+ State = newState;
+ Temporary = newTemp;
+ if (mustNotify)
+ StateWaitCondition.NotifyAll();
+}
+
+
+
+// ***** Wait Condition Implementation
+
+// Internal implementation class
+class WaitConditionImpl : public NewOverrideBase
+{
+ pthread_mutex_t SMutex;
+ pthread_cond_t Condv;
+
+public:
+
+ // Constructor/destructor
+ WaitConditionImpl();
+ ~WaitConditionImpl();
+
+ // Release mutex and wait for condition. The mutex is re-aqured after the wait.
+ bool Wait(Mutex *pmutex, unsigned delay = OVR_WAIT_INFINITE);
+
+ // Notify a condition, releasing at one object waiting
+ void Notify();
+ // Notify a condition, releasing all objects waiting
+ void NotifyAll();
+};
+
+
+WaitConditionImpl::WaitConditionImpl()
+{
+ pthread_mutex_init(&SMutex, 0);
+ pthread_cond_init(&Condv, 0);
+}
+
+WaitConditionImpl::~WaitConditionImpl()
+{
+ pthread_mutex_destroy(&SMutex);
+ pthread_cond_destroy(&Condv);
+}
+
+bool WaitConditionImpl::Wait(Mutex *pmutex, unsigned delay)
+{
+ bool result = 1;
+ unsigned lockCount = pmutex->pImpl->LockCount;
+
+ // Mutex must have been locked
+ if (lockCount == 0)
+ return 0;
+
+ pthread_mutex_lock(&SMutex);
+
+ // Finally, release a mutex or semaphore
+ if (pmutex->pImpl->Recursive)
+ {
+ // Release the recursive mutex N times
+ pmutex->pImpl->LockCount = 0;
+ for(unsigned i=0; i<lockCount; i++)
+ pthread_mutex_unlock(&pmutex->pImpl->SMutex);
+ }
+ else
+ {
+ pmutex->pImpl->LockCount = 0;
+ pthread_mutex_unlock(&pmutex->pImpl->SMutex);
+ }
+
+ // Note that there is a gap here between mutex.Unlock() and Wait().
+ // The other mutex protects this gap.
+
+ if (delay == OVR_WAIT_INFINITE)
+ pthread_cond_wait(&Condv,&SMutex);
+ else
+ {
+ timespec ts;
+#ifdef OVR_OS_PS3
+ sys_time_sec_t s;
+ sys_time_nsec_t ns;
+ sys_time_get_current_time(&s, &ns);
+
+ ts.tv_sec = s + (delay / 1000);
+ ts.tv_nsec = ns + (delay % 1000) * 1000000;
+
+#else
+ struct timeval tv;
+ gettimeofday(&tv, 0);
+
+ ts.tv_sec = tv.tv_sec + (delay / 1000);
+ ts.tv_nsec = (tv.tv_usec + (delay % 1000) * 1000) * 1000;
+#endif
+ if (ts.tv_nsec > 999999999)
+ {
+ ts.tv_sec++;
+ ts.tv_nsec -= 1000000000;
+ }
+ int r = pthread_cond_timedwait(&Condv,&SMutex, &ts);
+ OVR_ASSERT(r == 0 || r == ETIMEDOUT);
+ if (r)
+ result = 0;
+ }
+
+ pthread_mutex_unlock(&SMutex);
+
+ // Re-aquire the mutex
+ for(unsigned i=0; i<lockCount; i++)
+ pmutex->DoLock();
+
+ // Return the result
+ return result;
+}
+
+// Notify a condition, releasing the least object in a queue
+void WaitConditionImpl::Notify()
+{
+ pthread_mutex_lock(&SMutex);
+ pthread_cond_signal(&Condv);
+ pthread_mutex_unlock(&SMutex);
+}
+
+// Notify a condition, releasing all objects waiting
+void WaitConditionImpl::NotifyAll()
+{
+ pthread_mutex_lock(&SMutex);
+ pthread_cond_broadcast(&Condv);
+ pthread_mutex_unlock(&SMutex);
+}
+
+
+
+// *** Actual implementation of WaitCondition
+
+WaitCondition::WaitCondition()
+{
+ pImpl = new WaitConditionImpl;
+}
+WaitCondition::~WaitCondition()
+{
+ delete pImpl;
+}
+
+bool WaitCondition::Wait(Mutex *pmutex, unsigned delay)
+{
+ return pImpl->Wait(pmutex, delay);
+}
+// Notification
+void WaitCondition::Notify()
+{
+ pImpl->Notify();
+}
+void WaitCondition::NotifyAll()
+{
+ pImpl->NotifyAll();
+}
+
+
+// ***** Current thread
+
+// Per-thread variable
+/*
+static __thread Thread* pCurrentThread = 0;
+
+// Static function to return a pointer to the current thread
+void Thread::InitCurrentThread(Thread *pthread)
+{
+ pCurrentThread = pthread;
+}
+
+// Static function to return a pointer to the current thread
+Thread* Thread::GetThread()
+{
+ return pCurrentThread;
+}
+*/
+
+
+// *** Thread constructors.
+
+Thread::Thread(UPInt stackSize, int processor)
+{
+ // NOTE: RefCount mode already thread-safe for all Waitable objects.
+ CreateParams params;
+ params.stackSize = stackSize;
+ params.processor = processor;
+ Init(params);
+}
+
+Thread::Thread(Thread::ThreadFn threadFunction, void* userHandle, UPInt stackSize,
+ int processor, Thread::ThreadState initialState)
+{
+ CreateParams params(threadFunction, userHandle, stackSize, processor, initialState);
+ Init(params);
+}
+
+Thread::Thread(const CreateParams& params)
+{
+ Init(params);
+}
+
+void Thread::Init(const CreateParams& params)
+{
+ // Clear the variables
+ ThreadFlags = 0;
+ ThreadHandle = 0;
+ ExitCode = 0;
+ SuspendCount = 0;
+ StackSize = params.stackSize;
+ Processor = params.processor;
+ Priority = params.priority;
+
+ // Clear Function pointers
+ ThreadFunction = params.threadFunction;
+ UserHandle = params.userHandle;
+ if (params.initialState != NotRunning)
+ Start(params.initialState);
+}
+
+Thread::~Thread()
+{
+ // Thread should not running while object is being destroyed,
+ // this would indicate ref-counting issue.
+ //OVR_ASSERT(IsRunning() == 0);
+
+ // Clean up thread.
+ ThreadHandle = 0;
+}
+
+
+
+// *** Overridable User functions.
+
+// Default Run implementation
+int Thread::Run()
+{
+ // Call pointer to function, if available.
+ return (ThreadFunction) ? ThreadFunction(this, UserHandle) : 0;
+}
+void Thread::OnExit()
+{
+}
+
+
+// Finishes the thread and releases internal reference to it.
+void Thread::FinishAndRelease()
+{
+ // Note: thread must be US.
+ ThreadFlags &= (UInt32)~(OVR_THREAD_STARTED);
+ ThreadFlags |= OVR_THREAD_FINISHED;
+
+ // Release our reference; this is equivalent to 'delete this'
+ // from the point of view of our thread.
+ Release();
+}
+
+
+
+// *** ThreadList - used to track all created threads
+
+class ThreadList : public NewOverrideBase
+{
+ //------------------------------------------------------------------------
+ struct ThreadHashOp
+ {
+ size_t operator()(const Thread* ptr)
+ {
+ return (((size_t)ptr) >> 6) ^ (size_t)ptr;
+ }
+ };
+
+ HashSet<Thread*, ThreadHashOp> ThreadSet;
+ Mutex ThreadMutex;
+ WaitCondition ThreadsEmpty;
+ // Track the root thread that created us.
+ pthread_t RootThreadId;
+
+ static ThreadList* volatile pRunningThreads;
+
+ void addThread(Thread *pthread)
+ {
+ Mutex::Locker lock(&ThreadMutex);
+ ThreadSet.Add(pthread);
+ }
+
+ void removeThread(Thread *pthread)
+ {
+ Mutex::Locker lock(&ThreadMutex);
+ ThreadSet.Remove(pthread);
+ if (ThreadSet.GetSize() == 0)
+ ThreadsEmpty.Notify();
+ }
+
+ void finishAllThreads()
+ {
+ // Only original root thread can call this.
+ OVR_ASSERT(pthread_self() == RootThreadId);
+
+ Mutex::Locker lock(&ThreadMutex);
+ while (ThreadSet.GetSize() != 0)
+ ThreadsEmpty.Wait(&ThreadMutex);
+ }
+
+public:
+
+ ThreadList()
+ {
+ RootThreadId = pthread_self();
+ }
+ ~ThreadList() { }
+
+
+ static void AddRunningThread(Thread *pthread)
+ {
+ // Non-atomic creation ok since only the root thread
+ if (!pRunningThreads)
+ {
+ pRunningThreads = new ThreadList;
+ OVR_ASSERT(pRunningThreads);
+ }
+ pRunningThreads->addThread(pthread);
+ }
+
+ // NOTE: 'pthread' might be a dead pointer when this is
+ // called so it should not be accessed; it is only used
+ // for removal.
+ static void RemoveRunningThread(Thread *pthread)
+ {
+ OVR_ASSERT(pRunningThreads);
+ pRunningThreads->removeThread(pthread);
+ }
+
+ static void FinishAllThreads()
+ {
+ // This is ok because only root thread can wait for other thread finish.
+ if (pRunningThreads)
+ {
+ pRunningThreads->finishAllThreads();
+ delete pRunningThreads;
+ pRunningThreads = 0;
+ }
+ }
+};
+
+// By default, we have no thread list.
+ThreadList* volatile ThreadList::pRunningThreads = 0;
+
+
+// FinishAllThreads - exposed publicly in Thread.
+void Thread::FinishAllThreads()
+{
+ ThreadList::FinishAllThreads();
+}
+
+// *** Run override
+
+int Thread::PRun()
+{
+ // Suspend us on start, if requested
+ if (ThreadFlags & OVR_THREAD_START_SUSPENDED)
+ {
+ Suspend();
+ ThreadFlags &= (UInt32)~OVR_THREAD_START_SUSPENDED;
+ }
+
+ // Call the virtual run function
+ ExitCode = Run();
+ return ExitCode;
+}
+
+
+
+
+// *** User overridables
+
+bool Thread::GetExitFlag() const
+{
+ return (ThreadFlags & OVR_THREAD_EXIT) != 0;
+}
+
+void Thread::SetExitFlag(bool exitFlag)
+{
+ // The below is atomic since ThreadFlags is AtomicInt.
+ if (exitFlag)
+ ThreadFlags |= OVR_THREAD_EXIT;
+ else
+ ThreadFlags &= (UInt32) ~OVR_THREAD_EXIT;
+}
+
+
+// Determines whether the thread was running and is now finished
+bool Thread::IsFinished() const
+{
+ return (ThreadFlags & OVR_THREAD_FINISHED) != 0;
+}
+// Determines whether the thread is suspended
+bool Thread::IsSuspended() const
+{
+ return SuspendCount > 0;
+}
+// Returns current thread state
+Thread::ThreadState Thread::GetThreadState() const
+{
+ if (IsSuspended())
+ return Suspended;
+ if (ThreadFlags & OVR_THREAD_STARTED)
+ return Running;
+ return NotRunning;
+}
+/*
+static const char* mapsched_policy(int policy)
+{
+ switch(policy)
+ {
+ case SCHED_OTHER:
+ return "SCHED_OTHER";
+ case SCHED_RR:
+ return "SCHED_RR";
+ case SCHED_FIFO:
+ return "SCHED_FIFO";
+
+ }
+ return "UNKNOWN";
+}
+ int policy;
+ sched_param sparam;
+ pthread_getschedparam(pthread_self(), &policy, &sparam);
+ int max_prior = sched_get_priority_max(policy);
+ int min_prior = sched_get_priority_min(policy);
+ printf(" !!!! policy: %s, priority: %d, max priority: %d, min priority: %d\n", mapsched_policy(policy), sparam.sched_priority, max_prior, min_prior);
+#include <stdio.h>
+*/
+// ***** Thread management
+
+// The actual first function called on thread start
+void* Thread_PthreadStartFn(void* phandle)
+{
+ Thread* pthread = (Thread*)phandle;
+ int result = pthread->PRun();
+ // Signal the thread as done and release it atomically.
+ pthread->FinishAndRelease();
+ // At this point Thread object might be dead; however we can still pass
+ // it to RemoveRunningThread since it is only used as a key there.
+ ThreadList::RemoveRunningThread(pthread);
+ return (void*) result;
+}
+
+int Thread::InitAttr = 0;
+pthread_attr_t Thread::Attr;
+
+/* static */
+int Thread::GetOSPriority(ThreadPriority p)
+//static inline int MapToSystemPrority(Thread::ThreadPriority p)
+{
+#ifdef OVR_OS_PS3
+ switch(p)
+ {
+ case Thread::CriticalPriority: return 0;
+ case Thread::HighestPriority: return 300;
+ case Thread::AboveNormalPriority: return 600;
+ case Thread::NormalPriority: return 1000;
+ case Thread::BelowNormalPriority: return 1500;
+ case Thread::LowestPriority: return 2500;
+ case Thread::IdlePriority: return 3071;
+ } return 1000;
+#else
+ OVR_UNUSED(p);
+ return -1;
+#endif
+}
+
+bool Thread::Start(ThreadState initialState)
+{
+ if (initialState == NotRunning)
+ return 0;
+ if (GetThreadState() != NotRunning)
+ {
+ OVR_DEBUG_LOG(("Thread::Start failed - thread %p already running", this));
+ return 0;
+ }
+
+ if (!InitAttr)
+ {
+ pthread_attr_init(&Attr);
+ pthread_attr_setdetachstate(&Attr, PTHREAD_CREATE_DETACHED);
+ pthread_attr_setstacksize(&Attr, 128 * 1024);
+ sched_param sparam;
+ sparam.sched_priority = Thread::GetOSPriority(NormalPriority);
+ pthread_attr_setschedparam(&Attr, &sparam);
+ InitAttr = 1;
+ }
+
+ ExitCode = 0;
+ SuspendCount = 0;
+ ThreadFlags = (initialState == Running) ? 0 : OVR_THREAD_START_SUSPENDED;
+
+ // AddRef to us until the thread is finished
+ AddRef();
+ ThreadList::AddRunningThread(this);
+
+ int result;
+ if (StackSize != 128 * 1024 || Priority != NormalPriority)
+ {
+ pthread_attr_t attr;
+
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ pthread_attr_setstacksize(&attr, StackSize);
+ sched_param sparam;
+ sparam.sched_priority = Thread::GetOSPriority(Priority);
+ pthread_attr_setschedparam(&attr, &sparam);
+ result = pthread_create(&ThreadHandle, &attr, Thread_PthreadStartFn, this);
+ pthread_attr_destroy(&attr);
+ }
+ else
+ result = pthread_create(&ThreadHandle, &Attr, Thread_PthreadStartFn, this);
+
+ if (result)
+ {
+ ThreadFlags = 0;
+ Release();
+ ThreadList::RemoveRunningThread(this);
+ return 0;
+ }
+ return 1;
+}
+
+
+// Suspend the thread until resumed
+bool Thread::Suspend()
+{
+ OVR_DEBUG_LOG(("Thread::Suspend - cannot suspend threads on this system"));
+ return 0;
+}
+
+// Resumes currently suspended thread
+bool Thread::Resume()
+{
+ return 0;
+}
+
+
+// Quits with an exit code
+void Thread::Exit(int exitCode)
+{
+ // Can only exist the current thread
+ // if (GetThread() != this)
+ // return;
+
+ // Call the virtual OnExit function
+ OnExit();
+
+ // Signal this thread object as done and release it's references.
+ FinishAndRelease();
+ ThreadList::RemoveRunningThread(this);
+
+ pthread_exit((void *) exitCode);
+}
+
+ThreadId GetCurrentThreadId()
+{
+ return (void*)pthread_self();
+}
+
+// *** Sleep functions
+
+/* static */
+bool Thread::Sleep(unsigned secs)
+{
+ sleep(secs);
+ return 1;
+}
+/* static */
+bool Thread::MSleep(unsigned msecs)
+{
+ usleep(msecs*1000);
+ return 1;
+}
+
+/* static */
+int Thread::GetCPUCount()
+{
+ return 1;
+}
+
+
+#ifdef OVR_OS_PS3
+
+sys_lwmutex_attribute_t Lock::LockAttr = { SYS_SYNC_PRIORITY, SYS_SYNC_RECURSIVE };
+
+#endif
+
+}
+
+#endif // OVR_ENABLE_THREADS
diff --git a/LibOVR/Src/Kernel/OVR_ThreadsWinAPI.cpp b/LibOVR/Src/Kernel/OVR_ThreadsWinAPI.cpp
new file mode 100644
index 0000000..fa3962e
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_ThreadsWinAPI.cpp
@@ -0,0 +1,994 @@
+/************************************************************************************
+
+Filename : OVR_ThreadsWinAPI.cpp
+Platform : WinAPI
+Content : Windows specific thread-related (safe) functionality
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#include "OVR_Threads.h"
+#include "OVR_Hash.h"
+#include "OVR_Log.h"
+
+#ifdef OVR_ENABLE_THREADS
+
+// For _beginthreadex / _endtheadex
+#include <process.h>
+
+namespace OVR {
+
+
+//-----------------------------------------------------------------------------------
+// *** Internal Mutex implementation class
+
+class MutexImpl : public NewOverrideBase
+{
+ // System mutex or semaphore
+ HANDLE hMutexOrSemaphore;
+ bool Recursive;
+ volatile unsigned LockCount;
+
+ friend class WaitConditionImpl;
+
+public:
+ // Constructor/destructor
+ MutexImpl(bool recursive = 1);
+ ~MutexImpl();
+
+ // Locking functions
+ void DoLock();
+ bool TryLock();
+ void Unlock(Mutex* pmutex);
+ // Returns 1 if the mutes is currently locked
+ bool IsLockedByAnotherThread(Mutex* pmutex);
+};
+
+// *** Constructor/destructor
+MutexImpl::MutexImpl(bool recursive)
+{
+ Recursive = recursive;
+ LockCount = 0;
+ hMutexOrSemaphore = Recursive ? CreateMutex(NULL, 0, NULL) : CreateSemaphore(NULL, 1, 1, NULL);
+}
+MutexImpl::~MutexImpl()
+{
+ CloseHandle(hMutexOrSemaphore);
+}
+
+
+// Lock and try lock
+void MutexImpl::DoLock()
+{
+ if (::WaitForSingleObject(hMutexOrSemaphore, INFINITE) != WAIT_OBJECT_0)
+ return;
+ LockCount++;
+}
+
+bool MutexImpl::TryLock()
+{
+ DWORD ret;
+ if ((ret=::WaitForSingleObject(hMutexOrSemaphore, 0)) != WAIT_OBJECT_0)
+ return 0;
+ LockCount++;
+ return 1;
+}
+
+void MutexImpl::Unlock(Mutex* pmutex)
+{
+ OVR_UNUSED(pmutex);
+
+ unsigned lockCount;
+ LockCount--;
+ lockCount = LockCount;
+
+ // Release mutex
+ if ((Recursive ? ReleaseMutex(hMutexOrSemaphore) :
+ ReleaseSemaphore(hMutexOrSemaphore, 1, NULL)) != 0)
+ {
+ // This used to call Wait handlers if lockCount == 0.
+ }
+}
+
+bool MutexImpl::IsLockedByAnotherThread(Mutex* pmutex)
+{
+ // There could be multiple interpretations of IsLocked with respect to current thread
+ if (LockCount == 0)
+ return 0;
+ if (!TryLock())
+ return 1;
+ Unlock(pmutex);
+ return 0;
+}
+
+/*
+bool MutexImpl::IsSignaled() const
+{
+ // An mutex is signaled if it is not locked ANYWHERE
+ // Note that this is different from IsLockedByAnotherThread function,
+ // that takes current thread into account
+ return LockCount == 0;
+}
+*/
+
+
+// *** Actual Mutex class implementation
+
+Mutex::Mutex(bool recursive)
+{
+ pImpl = new MutexImpl(recursive);
+}
+Mutex::~Mutex()
+{
+ delete pImpl;
+}
+
+// Lock and try lock
+void Mutex::DoLock()
+{
+ pImpl->DoLock();
+}
+bool Mutex::TryLock()
+{
+ return pImpl->TryLock();
+}
+void Mutex::Unlock()
+{
+ pImpl->Unlock(this);
+}
+bool Mutex::IsLockedByAnotherThread()
+{
+ return pImpl->IsLockedByAnotherThread(this);
+}
+
+//-----------------------------------------------------------------------------------
+// ***** Event
+
+bool Event::Wait(unsigned delay)
+{
+ Mutex::Locker lock(&StateMutex);
+
+ // Do the correct amount of waiting
+ if (delay == OVR_WAIT_INFINITE)
+ {
+ while(!State)
+ StateWaitCondition.Wait(&StateMutex);
+ }
+ else if (delay)
+ {
+ if (!State)
+ StateWaitCondition.Wait(&StateMutex, delay);
+ }
+
+ bool state = State;
+ // Take care of temporary 'pulsing' of a state
+ if (Temporary)
+ {
+ Temporary = false;
+ State = false;
+ }
+ return state;
+}
+
+void Event::updateState(bool newState, bool newTemp, bool mustNotify)
+{
+ Mutex::Locker lock(&StateMutex);
+ State = newState;
+ Temporary = newTemp;
+ if (mustNotify)
+ StateWaitCondition.NotifyAll();
+}
+
+
+//-----------------------------------------------------------------------------------
+// ***** Win32 Wait Condition Implementation
+
+// Internal implementation class
+class WaitConditionImpl : public NewOverrideBase
+{
+ // Event pool entries for extra events
+ struct EventPoolEntry : public NewOverrideBase
+ {
+ HANDLE hEvent;
+ EventPoolEntry *pNext;
+ EventPoolEntry *pPrev;
+ };
+
+ Lock WaitQueueLoc;
+ // Stores free events that can be used later
+ EventPoolEntry * pFreeEventList;
+
+ // A queue of waiting objects to be signaled
+ EventPoolEntry* pQueueHead;
+ EventPoolEntry* pQueueTail;
+
+ // Allocation functions for free events
+ EventPoolEntry* GetNewEvent();
+ void ReleaseEvent(EventPoolEntry* pevent);
+
+ // Queue operations
+ void QueuePush(EventPoolEntry* pentry);
+ EventPoolEntry* QueuePop();
+ void QueueFindAndRemove(EventPoolEntry* pentry);
+
+public:
+
+ // Constructor/destructor
+ WaitConditionImpl();
+ ~WaitConditionImpl();
+
+ // Release mutex and wait for condition. The mutex is re-acqured after the wait.
+ bool Wait(Mutex *pmutex, unsigned delay = OVR_WAIT_INFINITE);
+
+ // Notify a condition, releasing at one object waiting
+ void Notify();
+ // Notify a condition, releasing all objects waiting
+ void NotifyAll();
+};
+
+
+
+WaitConditionImpl::WaitConditionImpl()
+{
+ pFreeEventList = 0;
+ pQueueHead =
+ pQueueTail = 0;
+}
+
+WaitConditionImpl::~WaitConditionImpl()
+{
+ // Free all the resources
+ EventPoolEntry* p = pFreeEventList;
+ EventPoolEntry* pentry;
+
+ while(p)
+ {
+ // Move to next
+ pentry = p;
+ p = p->pNext;
+ // Delete old
+ ::CloseHandle(pentry->hEvent);
+ delete pentry;
+ }
+ // Shouldn't we also consider the queue?
+
+ // To be safe
+ pFreeEventList = 0;
+ pQueueHead =
+ pQueueTail = 0;
+}
+
+
+// Allocation functions for free events
+WaitConditionImpl::EventPoolEntry* WaitConditionImpl::GetNewEvent()
+{
+ EventPoolEntry* pentry;
+
+ // If there are any free nodes, use them
+ if (pFreeEventList)
+ {
+ pentry = pFreeEventList;
+ pFreeEventList = pFreeEventList->pNext;
+ }
+ else
+ {
+ // Allocate a new node
+ pentry = new EventPoolEntry;
+ pentry->pNext = 0;
+ pentry->pPrev = 0;
+ // Non-signaled manual event
+ pentry->hEvent = ::CreateEvent(NULL, TRUE, 0, NULL);
+ }
+
+ return pentry;
+}
+
+void WaitConditionImpl::ReleaseEvent(EventPoolEntry* pevent)
+{
+ // Mark event as non-signaled
+ ::ResetEvent(pevent->hEvent);
+ // And add it to free pool
+ pevent->pNext = pFreeEventList;
+ pevent->pPrev = 0;
+ pFreeEventList = pevent;
+}
+
+// Queue operations
+void WaitConditionImpl::QueuePush(EventPoolEntry* pentry)
+{
+ // Items already exist? Just add to tail
+ if (pQueueTail)
+ {
+ pentry->pPrev = pQueueTail;
+ pQueueTail->pNext = pentry;
+ pentry->pNext = 0;
+ pQueueTail = pentry;
+ }
+ else
+ {
+ // No items in queue
+ pentry->pNext =
+ pentry->pPrev = 0;
+ pQueueHead =
+ pQueueTail = pentry;
+ }
+}
+
+WaitConditionImpl::EventPoolEntry* WaitConditionImpl::QueuePop()
+{
+ EventPoolEntry* pentry = pQueueHead;
+
+ // No items, null pointer
+ if (pentry)
+ {
+ // More items after this one? just grab the first item
+ if (pQueueHead->pNext)
+ {
+ pQueueHead = pentry->pNext;
+ pQueueHead->pPrev = 0;
+ }
+ else
+ {
+ // Last item left
+ pQueueTail =
+ pQueueHead = 0;
+ }
+ }
+ return pentry;
+}
+
+void WaitConditionImpl::QueueFindAndRemove(EventPoolEntry* pentry)
+{
+ // Do an exhaustive search looking for an entry
+ EventPoolEntry* p = pQueueHead;
+
+ while(p)
+ {
+ // Entry found? Remove.
+ if (p == pentry)
+ {
+
+ // Remove the node form the list
+ // Prev link
+ if (pentry->pPrev)
+ pentry->pPrev->pNext = pentry->pNext;
+ else
+ pQueueHead = pentry->pNext;
+ // Next link
+ if (pentry->pNext)
+ pentry->pNext->pPrev = pentry->pPrev;
+ else
+ pQueueTail = pentry->pPrev;
+ // Done
+ return;
+ }
+
+ // Move to next item
+ p = p->pNext;
+ }
+}
+
+
+bool WaitConditionImpl::Wait(Mutex *pmutex, unsigned delay)
+{
+ bool result = 0;
+ unsigned i;
+ unsigned lockCount = pmutex->pImpl->LockCount;
+ EventPoolEntry* pentry;
+
+ // Mutex must have been locked
+ if (lockCount == 0)
+ return 0;
+
+ // Add an object to the wait queue
+ WaitQueueLoc.DoLock();
+ QueuePush(pentry = GetNewEvent());
+ WaitQueueLoc.Unlock();
+
+ // Finally, release a mutex or semaphore
+ if (pmutex->pImpl->Recursive)
+ {
+ // Release the recursive mutex N times
+ pmutex->pImpl->LockCount = 0;
+ for(i=0; i<lockCount; i++)
+ ::ReleaseMutex(pmutex->pImpl->hMutexOrSemaphore);
+ }
+ else
+ {
+ pmutex->pImpl->LockCount = 0;
+ ::ReleaseSemaphore(pmutex->pImpl->hMutexOrSemaphore, 1, NULL);
+ }
+
+ // Note that there is a gap here between mutex.Unlock() and Wait(). However,
+ // if notify() comes in at this point in the other thread it will set our
+ // corresponding event so wait will just fall through, as expected.
+
+ // Block and wait on the event
+ DWORD waitResult = ::WaitForSingleObject(pentry->hEvent,
+ (delay == OVR_WAIT_INFINITE) ? INFINITE : delay);
+ /*
+repeat_wait:
+ DWORD waitResult =
+
+ ::MsgWaitForMultipleObjects(1, &pentry->hEvent, FALSE,
+ (delay == OVR_WAIT_INFINITE) ? INFINITE : delay,
+ QS_ALLINPUT);
+ */
+
+ WaitQueueLoc.DoLock();
+ switch(waitResult)
+ {
+ case WAIT_ABANDONED:
+ case WAIT_OBJECT_0:
+ result = 1;
+ // Wait was successful, therefore the event entry should already be removed
+ // So just add entry back to a free list
+ ReleaseEvent(pentry);
+ break;
+ /*
+ case WAIT_OBJECT_0 + 1:
+ // Messages in WINDOWS queue
+ {
+ MSG msg;
+ PeekMessage(&msg, NULL, 0U, 0U, PM_NOREMOVE);
+ WaitQueueLoc.Unlock();
+ goto repeat_wait;
+ }
+ break; */
+ default:
+ // Timeout, our entry should still be in a queue
+ QueueFindAndRemove(pentry);
+ ReleaseEvent(pentry);
+ }
+ WaitQueueLoc.Unlock();
+
+ // Re-aquire the mutex
+ for(i=0; i<lockCount; i++)
+ pmutex->DoLock();
+
+ // Return the result
+ return result;
+}
+
+// Notify a condition, releasing the least object in a queue
+void WaitConditionImpl::Notify()
+{
+ Lock::Locker lock(&WaitQueueLoc);
+
+ // Pop last entry & signal it
+ EventPoolEntry* pentry = QueuePop();
+ if (pentry)
+ ::SetEvent(pentry->hEvent);
+}
+
+// Notify a condition, releasing all objects waiting
+void WaitConditionImpl::NotifyAll()
+{
+ Lock::Locker lock(&WaitQueueLoc);
+
+ // Pop and signal all events
+ // NOTE : There is no need to release the events, it's the waiters job to do so
+ EventPoolEntry* pentry = QueuePop();
+ while (pentry)
+ {
+ ::SetEvent(pentry->hEvent);
+ pentry = QueuePop();
+ }
+}
+
+
+
+// *** Actual implementation of WaitCondition
+
+WaitCondition::WaitCondition()
+{
+ pImpl = new WaitConditionImpl;
+}
+WaitCondition::~WaitCondition()
+{
+ delete pImpl;
+}
+
+// Wait without a mutex
+bool WaitCondition::Wait(Mutex *pmutex, unsigned delay)
+{
+ return pImpl->Wait(pmutex, delay);
+}
+// Notification
+void WaitCondition::Notify()
+{
+ pImpl->Notify();
+}
+void WaitCondition::NotifyAll()
+{
+ pImpl->NotifyAll();
+}
+
+
+
+//-----------------------------------------------------------------------------------
+// ***** Thread Class
+
+// Per-thread variable
+// MA: Don't use TLS for now - portability issues with DLLs, etc.
+/*
+#if !defined(OVR_CC_MSVC) || (OVR_CC_MSVC < 1300)
+__declspec(thread) Thread* pCurrentThread = 0;
+#else
+#pragma data_seg(".tls$")
+__declspec(thread) Thread* pCurrentThread = 0;
+#pragma data_seg(".rwdata")
+#endif
+*/
+
+// *** Thread constructors.
+
+Thread::Thread(UPInt stackSize, int processor)
+{
+ CreateParams params;
+ params.stackSize = stackSize;
+ params.processor = processor;
+ Init(params);
+}
+
+Thread::Thread(Thread::ThreadFn threadFunction, void* userHandle, UPInt stackSize,
+ int processor, Thread::ThreadState initialState)
+{
+ CreateParams params(threadFunction, userHandle, stackSize, processor, initialState);
+ Init(params);
+}
+
+Thread::Thread(const CreateParams& params)
+{
+ Init(params);
+}
+void Thread::Init(const CreateParams& params)
+{
+ // Clear the variables
+ ThreadFlags = 0;
+ ThreadHandle = 0;
+ IdValue = 0;
+ ExitCode = 0;
+ SuspendCount = 0;
+ StackSize = params.stackSize;
+ Processor = params.processor;
+ Priority = params.priority;
+
+ // Clear Function pointers
+ ThreadFunction = params.threadFunction;
+ UserHandle = params.userHandle;
+ if (params.initialState != NotRunning)
+ Start(params.initialState);
+
+}
+
+Thread::~Thread()
+{
+ // Thread should not running while object is being destroyed,
+ // this would indicate ref-counting issue.
+ //OVR_ASSERT(IsRunning() == 0);
+
+ // Clean up thread.
+ CleanupSystemThread();
+ ThreadHandle = 0;
+}
+
+
+// *** Overridable User functions.
+
+// Default Run implementation
+int Thread::Run()
+{
+ // Call pointer to function, if available.
+ return (ThreadFunction) ? ThreadFunction(this, UserHandle) : 0;
+}
+void Thread::OnExit()
+{
+}
+
+// Finishes the thread and releases internal reference to it.
+void Thread::FinishAndRelease()
+{
+ // Note: thread must be US.
+ ThreadFlags &= (UInt32)~(OVR_THREAD_STARTED);
+ ThreadFlags |= OVR_THREAD_FINISHED;
+
+ // Release our reference; this is equivalent to 'delete this'
+ // from the point of view of our thread.
+ Release();
+}
+
+
+// *** ThreadList - used to tack all created threads
+
+class ThreadList : public NewOverrideBase
+{
+ //------------------------------------------------------------------------
+ struct ThreadHashOp
+ {
+ UPInt operator()(const Thread* ptr)
+ {
+ return (((UPInt)ptr) >> 6) ^ (UPInt)ptr;
+ }
+ };
+
+ HashSet<Thread*, ThreadHashOp> ThreadSet;
+ Mutex ThreadMutex;
+ WaitCondition ThreadsEmpty;
+ // Track the root thread that created us.
+ ThreadId RootThreadId;
+
+ static ThreadList* volatile pRunningThreads;
+
+ void addThread(Thread *pthread)
+ {
+ Mutex::Locker lock(&ThreadMutex);
+ ThreadSet.Add(pthread);
+ }
+
+ void removeThread(Thread *pthread)
+ {
+ Mutex::Locker lock(&ThreadMutex);
+ ThreadSet.Remove(pthread);
+ if (ThreadSet.GetSize() == 0)
+ ThreadsEmpty.Notify();
+ }
+
+ void finishAllThreads()
+ {
+ // Only original root thread can call this.
+ OVR_ASSERT(GetCurrentThreadId() == RootThreadId);
+
+ Mutex::Locker lock(&ThreadMutex);
+ while (ThreadSet.GetSize() != 0)
+ ThreadsEmpty.Wait(&ThreadMutex);
+ }
+
+public:
+
+ ThreadList()
+ {
+ RootThreadId = GetCurrentThreadId();
+ }
+ ~ThreadList() { }
+
+
+ static void AddRunningThread(Thread *pthread)
+ {
+ // Non-atomic creation ok since only the root thread
+ if (!pRunningThreads)
+ {
+ pRunningThreads = new ThreadList;
+ OVR_ASSERT(pRunningThreads);
+ }
+ pRunningThreads->addThread(pthread);
+ }
+
+ // NOTE: 'pthread' might be a dead pointer when this is
+ // called so it should not be accessed; it is only used
+ // for removal.
+ static void RemoveRunningThread(Thread *pthread)
+ {
+ OVR_ASSERT(pRunningThreads);
+ pRunningThreads->removeThread(pthread);
+ }
+
+ static void FinishAllThreads()
+ {
+ // This is ok because only root thread can wait for other thread finish.
+ if (pRunningThreads)
+ {
+ pRunningThreads->finishAllThreads();
+ delete pRunningThreads;
+ pRunningThreads = 0;
+ }
+ }
+};
+
+// By default, we have no thread list.
+ThreadList* volatile ThreadList::pRunningThreads = 0;
+
+
+// FinishAllThreads - exposed publicly in Thread.
+void Thread::FinishAllThreads()
+{
+ ThreadList::FinishAllThreads();
+}
+
+
+// *** Run override
+
+int Thread::PRun()
+{
+ // Suspend us on start, if requested
+ if (ThreadFlags & OVR_THREAD_START_SUSPENDED)
+ {
+ Suspend();
+ ThreadFlags &= (UInt32)~OVR_THREAD_START_SUSPENDED;
+ }
+
+ // Call the virtual run function
+ ExitCode = Run();
+ return ExitCode;
+}
+
+
+
+/* MA: Don't use TLS for now.
+
+// Static function to return a pointer to the current thread
+void Thread::InitCurrentThread(Thread *pthread)
+{
+ pCurrentThread = pthread;
+}
+
+// Static function to return a pointer to the current thread
+Thread* Thread::GetThread()
+{
+ return pCurrentThread;
+}
+*/
+
+
+// *** User overridables
+
+bool Thread::GetExitFlag() const
+{
+ return (ThreadFlags & OVR_THREAD_EXIT) != 0;
+}
+
+void Thread::SetExitFlag(bool exitFlag)
+{
+ // The below is atomic since ThreadFlags is AtomicInt.
+ if (exitFlag)
+ ThreadFlags |= OVR_THREAD_EXIT;
+ else
+ ThreadFlags &= (UInt32) ~OVR_THREAD_EXIT;
+}
+
+
+// Determines whether the thread was running and is now finished
+bool Thread::IsFinished() const
+{
+ return (ThreadFlags & OVR_THREAD_FINISHED) != 0;
+}
+// Determines whether the thread is suspended
+bool Thread::IsSuspended() const
+{
+ return SuspendCount > 0;
+}
+// Returns current thread state
+Thread::ThreadState Thread::GetThreadState() const
+{
+ if (IsSuspended())
+ return Suspended;
+ if (ThreadFlags & OVR_THREAD_STARTED)
+ return Running;
+ return NotRunning;
+}
+
+
+
+// ***** Thread management
+/* static */
+int Thread::GetOSPriority(ThreadPriority p)
+{
+ switch(p)
+ {
+ case Thread::CriticalPriority: return THREAD_PRIORITY_TIME_CRITICAL;
+ case Thread::HighestPriority: return THREAD_PRIORITY_HIGHEST;
+ case Thread::AboveNormalPriority: return THREAD_PRIORITY_ABOVE_NORMAL;
+ case Thread::NormalPriority: return THREAD_PRIORITY_NORMAL;
+ case Thread::BelowNormalPriority: return THREAD_PRIORITY_BELOW_NORMAL;
+ case Thread::LowestPriority: return THREAD_PRIORITY_LOWEST;
+ case Thread::IdlePriority: return THREAD_PRIORITY_IDLE;
+ }
+ return THREAD_PRIORITY_NORMAL;
+}
+
+// The actual first function called on thread start
+unsigned WINAPI Thread_Win32StartFn(void * phandle)
+{
+ Thread * pthread = (Thread*)phandle;
+ if (pthread->Processor != -1)
+ {
+ DWORD_PTR ret = SetThreadAffinityMask(GetCurrentThread(), (DWORD)pthread->Processor);
+ if (ret == 0)
+ OVR_DEBUG_LOG(("Could not set hardware processor for the thread"));
+ }
+ BOOL ret = ::SetThreadPriority(GetCurrentThread(), Thread::GetOSPriority(pthread->Priority));
+ if (ret == 0)
+ OVR_DEBUG_LOG(("Could not set thread priority"));
+ OVR_UNUSED(ret);
+
+ // Ensure that ThreadId is assigned once thread is running, in case
+ // beginthread hasn't filled it in yet.
+ pthread->IdValue = (ThreadId)::GetCurrentThreadId();
+
+ DWORD result = pthread->PRun();
+ // Signal the thread as done and release it atomically.
+ pthread->FinishAndRelease();
+ // At this point Thread object might be dead; however we can still pass
+ // it to RemoveRunningThread since it is only used as a key there.
+ ThreadList::RemoveRunningThread(pthread);
+ return (unsigned) result;
+}
+
+bool Thread::Start(ThreadState initialState)
+{
+ if (initialState == NotRunning)
+ return 0;
+ if (GetThreadState() != NotRunning)
+ {
+ OVR_DEBUG_LOG(("Thread::Start failed - thread %p already running", this));
+ return 0;
+ }
+
+ // Free old thread handle before creating the new one
+ CleanupSystemThread();
+
+ // AddRef to us until the thread is finished.
+ AddRef();
+ ThreadList::AddRunningThread(this);
+
+ ExitCode = 0;
+ SuspendCount = 0;
+ ThreadFlags = (initialState == Running) ? 0 : OVR_THREAD_START_SUSPENDED;
+ ThreadHandle = (HANDLE) _beginthreadex(0, (unsigned)StackSize,
+ Thread_Win32StartFn, this, 0, (unsigned*)&IdValue);
+
+ // Failed? Fail the function
+ if (ThreadHandle == 0)
+ {
+ ThreadFlags = 0;
+ Release();
+ ThreadList::RemoveRunningThread(this);
+ return 0;
+ }
+ return 1;
+}
+
+
+// Suspend the thread until resumed
+bool Thread::Suspend()
+{
+ // Can't suspend a thread that wasn't started
+ if (!(ThreadFlags & OVR_THREAD_STARTED))
+ return 0;
+
+ if (::SuspendThread(ThreadHandle) != 0xFFFFFFFF)
+ {
+ SuspendCount++;
+ return 1;
+ }
+ return 0;
+}
+
+// Resumes currently suspended thread
+bool Thread::Resume()
+{
+ // Can't suspend a thread that wasn't started
+ if (!(ThreadFlags & OVR_THREAD_STARTED))
+ return 0;
+
+ // Decrement count, and resume thread if it is 0
+ SInt32 oldCount = SuspendCount.ExchangeAdd_Acquire(-1);
+ if (oldCount >= 1)
+ {
+ if (oldCount == 1)
+ {
+ if (::ResumeThread(ThreadHandle) != 0xFFFFFFFF)
+ return 1;
+ }
+ else
+ {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+// Quits with an exit code
+void Thread::Exit(int exitCode)
+{
+ // Can only exist the current thread.
+ // MA: Don't use TLS for now.
+ //if (GetThread() != this)
+ // return;
+
+ // Call the virtual OnExit function.
+ OnExit();
+
+ // Signal this thread object as done and release it's references.
+ FinishAndRelease();
+ ThreadList::RemoveRunningThread(this);
+
+ // Call the exit function.
+ _endthreadex((unsigned)exitCode);
+}
+
+
+void Thread::CleanupSystemThread()
+{
+ if (ThreadHandle != 0)
+ {
+ ::CloseHandle(ThreadHandle);
+ ThreadHandle = 0;
+ }
+}
+
+// *** Sleep functions
+// static
+bool Thread::Sleep(unsigned secs)
+{
+ ::Sleep(secs*1000);
+ return 1;
+}
+
+// static
+bool Thread::MSleep(unsigned msecs)
+{
+ ::Sleep(msecs);
+ return 1;
+}
+
+void Thread::SetThreadName( const char* name )
+{
+#if !defined(OVR_BUILD_SHIPPING) || defined(OVR_BUILD_PROFILING)
+ // Looks ugly, but it is the recommended way to name a thread.
+ typedef struct tagTHREADNAME_INFO {
+ DWORD dwType; // Must be 0x1000
+ LPCSTR szName; // Pointer to name (in user address space)
+ DWORD dwThreadID; // Thread ID (-1 for caller thread)
+ DWORD dwFlags; // Reserved for future use; must be zero
+ } THREADNAME_INFO;
+
+ THREADNAME_INFO info;
+
+ info.dwType = 0x1000;
+ info.szName = name;
+ info.dwThreadID = reinterpret_cast<DWORD>(GetThreadId());
+ info.dwFlags = 0;
+
+ __try
+ {
+#ifdef _WIN64
+ RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(DWORD), (const ULONG_PTR *)&info );
+#else
+ RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(DWORD), (DWORD *)&info );
+#endif
+ }
+ __except( GetExceptionCode()==0x406D1388 ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_EXECUTE_HANDLER )
+ {
+ }
+#endif // OVR_BUILD_SHIPPING
+}
+
+// static
+int Thread::GetCPUCount()
+{
+ SYSTEM_INFO sysInfo;
+ GetSystemInfo(&sysInfo);
+ return (int) sysInfo.dwNumberOfProcessors;
+}
+
+// Returns the unique Id of a thread it is called on, intended for
+// comparison purposes.
+ThreadId GetCurrentThreadId()
+{
+ return (ThreadId)::GetCurrentThreadId();
+}
+
+} // OVR
+
+#endif
+
+
diff --git a/LibOVR/Src/Kernel/OVR_Timer.cpp b/LibOVR/Src/Kernel/OVR_Timer.cpp
new file mode 100644
index 0000000..db25b8f
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_Timer.cpp
@@ -0,0 +1,156 @@
+/************************************************************************************
+
+Filename : OVR_Timer.cpp
+Content : Provides static functions for precise timing
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#include "OVR_Timer.h"
+
+#if defined (OVR_OS_WIN32)
+#include <windows.h>
+
+#else
+#include <sys/time.h>
+#endif
+
+namespace OVR {
+
+//-----------------------------------------------------------------------------------
+// ***** Timer Class
+
+UInt64 Timer::GetProfileTicks()
+{
+ return (GetRawTicks() * MksPerSecond) / GetRawFrequency();
+}
+double Timer::GetProfileSeconds()
+{
+ static UInt64 StartTime = GetProfileTicks();
+ return TicksToSeconds(GetProfileTicks()-StartTime);
+}
+
+
+//------------------------------------------------------------------------
+// *** Win32 Specific Timer
+
+#if (defined (OVR_OS_WIN32))
+
+CRITICAL_SECTION WinAPI_GetTimeCS;
+volatile UInt32 WinAPI_OldTime = 0;
+volatile UInt32 WinAPI_WrapCounter = 0;
+
+
+UInt32 Timer::GetTicksMs()
+{
+ return timeGetTime();
+}
+
+UInt64 Timer::GetTicks()
+{
+ DWORD ticks = timeGetTime();
+ UInt64 result;
+
+ // On Win32 QueryPerformanceFrequency is unreliable due to SMP and
+ // performance levels, so use this logic to detect wrapping and track
+ // high bits.
+ ::EnterCriticalSection(&WinAPI_GetTimeCS);
+
+ if (WinAPI_OldTime > ticks)
+ WinAPI_WrapCounter++;
+ WinAPI_OldTime = ticks;
+
+ result = (UInt64(WinAPI_WrapCounter) << 32) | ticks;
+ ::LeaveCriticalSection(&WinAPI_GetTimeCS);
+
+ return result * MksPerMs;
+}
+
+UInt64 Timer::GetRawTicks()
+{
+ LARGE_INTEGER li;
+ QueryPerformanceCounter(&li);
+ return li.QuadPart;
+}
+
+UInt64 Timer::GetRawFrequency()
+{
+ static UInt64 perfFreq = 0;
+ if (perfFreq == 0)
+ {
+ LARGE_INTEGER freq;
+ QueryPerformanceFrequency(&freq);
+ perfFreq = freq.QuadPart;
+ }
+ return perfFreq;
+}
+
+void Timer::initializeTimerSystem()
+{
+ timeBeginPeriod(1);
+ InitializeCriticalSection(&WinAPI_GetTimeCS);
+
+}
+void Timer::shutdownTimerSystem()
+{
+ DeleteCriticalSection(&WinAPI_GetTimeCS);
+ timeEndPeriod(1);
+}
+
+#else // !OVR_OS_WIN32
+
+
+//------------------------------------------------------------------------
+// *** Standard OS Timer
+
+UInt32 Timer::GetTicksMs()
+{
+ return (UInt32)(GetProfileTicks() / 1000);
+}
+// The profile ticks implementation is just fine for a normal timer.
+UInt64 Timer::GetTicks()
+{
+ return GetProfileTicks();
+}
+
+void Timer::initializeTimerSystem()
+{
+}
+void Timer::shutdownTimerSystem()
+{
+}
+
+UInt64 Timer::GetRawTicks()
+{
+ // TODO: prefer rdtsc when available?
+
+ // Return microseconds.
+ struct timeval tv;
+ UInt64 result;
+
+ gettimeofday(&tv, 0);
+
+ result = (UInt64)tv.tv_sec * 1000000;
+ result += tv.tv_usec;
+
+ return result;
+}
+
+UInt64 Timer::GetRawFrequency()
+{
+ return MksPerSecond;
+}
+
+#endif // !OVR_OS_WIN32
+
+
+
+} // OVR
+
diff --git a/LibOVR/Src/Kernel/OVR_Timer.h b/LibOVR/Src/Kernel/OVR_Timer.h
new file mode 100644
index 0000000..0b71912
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_Timer.h
@@ -0,0 +1,100 @@
+/************************************************************************************
+
+PublicHeader: OVR
+Filename : OVR_Timer.h
+Content : Provides static functions for precise timing
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#ifndef OVR_Timer_h
+#define OVR_Timer_h
+
+#include "OVR_Types.h"
+
+namespace OVR {
+
+//-----------------------------------------------------------------------------------
+// ***** Timer
+
+// Timer class defines a family of static functions used for application
+// timing and profiling.
+
+class Timer
+{
+public:
+ enum {
+ MsPerSecond = 1000, // Milliseconds in one second.
+ MksPerMs = 1000, // Microseconds in one millisecond.
+ MksPerSecond = MsPerSecond * MksPerMs
+ };
+
+
+ // ***** Timing APIs for Application
+ // These APIs should be used to guide animation and other program functions
+ // that require precision.
+
+ // Returns ticks in milliseconds, as a 32-bit number. May wrap around every
+ // 49.2 days. Use either time difference of two values of GetTicks to avoid
+ // wrap-around. GetTicksMs may perform better then GetTicks.
+ static UInt32 OVR_STDCALL GetTicksMs();
+
+ // GetTicks returns general-purpose high resolution application timer value,
+ // measured in microseconds (mks, or 1/1000000 of a second). The actual precision
+ // is system-specific and may be much lower, such as 1 ms.
+ static UInt64 OVR_STDCALL GetTicks();
+
+
+ // ***** Profiling APIs.
+ // These functions should be used for profiling, but may have system specific
+ // artifacts that make them less appropriate for general system use.
+ // On Win32, for example these rely on QueryPerformanceConter may have
+ // problems with thread-core switching and power modes.
+
+ // Return a hi-res timer value in mks (1/1000000 of a sec).
+ // Generally you want to call this at the start and end of an
+ // operation, and pass the difference to
+ // TicksToSeconds() to find out how long the operation took.
+ static UInt64 OVR_STDCALL GetProfileTicks();
+
+ // More convenient zero-based profile timer in seconds. First call initializes
+ // the "zero" value; future calls return the difference. Not thread safe for first call.
+ // Due to low precision of Double, may malfunction after long runtime.
+ static double OVR_STDCALL GetProfileSeconds();
+
+ // Get the raw cycle counter value, providing the maximum possible timer resolution.
+ static UInt64 OVR_STDCALL GetRawTicks();
+ static UInt64 OVR_STDCALL GetRawFrequency();
+
+
+ // ***** Tick and time unit conversion.
+
+ // Convert micro-second ticks value into seconds value.
+ static inline double TicksToSeconds(UInt64 ticks)
+ {
+ return static_cast<double>(ticks) * (1.0 / (double)MksPerSecond);
+ }
+ // Convert Raw or frequency-unit ticks to seconds based on specified frequency.
+ static inline double RawTicksToSeconds(UInt64 rawTicks, UInt64 rawFrequency)
+ {
+ return static_cast<double>(rawTicks) * rawFrequency;
+ }
+
+private:
+ friend class System;
+ // System called during program startup/shutdown.
+ static void initializeTimerSystem();
+ static void shutdownTimerSystem();
+};
+
+
+} // Scaleform::Timer
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_Types.h b/LibOVR/Src/Kernel/OVR_Types.h
new file mode 100644
index 0000000..40eb156
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_Types.h
@@ -0,0 +1,456 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_Types.h
+Content : Standard library defines and simple types
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#ifndef OVR_Types_H
+#define OVR_Types_H
+
+//-----------------------------------------------------------------------------------
+// ****** Operating System
+//
+// Type definitions exist for the following operating systems: (OVR_OS_x)
+//
+// WIN32 - Win32 (Windows 95/98/ME and Windows NT/2000/XP)
+// DARWIN - Darwin OS (Mac OS X)
+// LINUX - Linux
+// ANDROID - Android
+// IPHONE - iPhone
+
+#if (defined(__APPLE__) && (defined(__GNUC__) ||\
+ defined(__xlC__) || defined(__xlc__))) || defined(__MACOS__)
+# if (defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) || defined(__IPHONE_OS_VERSION_MIN_REQUIRED))
+# define OVR_OS_IPHONE
+# else
+# define OVR_OS_DARWIN
+# define OVR_OS_MAC
+# endif
+#elif (defined(WIN64) || defined(_WIN64) || defined(__WIN64__))
+# define OVR_OS_WIN32
+#elif (defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__))
+# define OVR_OS_WIN32
+#elif defined(__linux__) || defined(__linux)
+# define OVR_OS_LINUX
+#else
+# define OVR_OS_OTHER
+#endif
+
+#if defined(ANDROID)
+# define OVR_OS_ANDROID
+#endif
+
+
+//-----------------------------------------------------------------------------------
+// ***** CPU Architecture
+//
+// The following CPUs are defined: (OVR_CPU_x)
+//
+// X86 - x86 (IA-32)
+// X86_64 - x86_64 (amd64)
+// PPC - PowerPC
+// PPC64 - PowerPC64
+// MIPS - MIPS
+// OTHER - CPU for which no special support is present or needed
+
+
+#if defined(__x86_64__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64__)
+# define OVR_CPU_X86_64
+# define OVR_64BIT_POINTERS
+#elif defined(__i386__) || defined(OVR_OS_WIN32)
+# define OVR_CPU_X86
+#elif defined(__powerpc64__)
+# define OVR_CPU_PPC64
+#elif defined(__ppc__)
+# define OVR_CPU_PPC
+#elif defined(__mips__) || defined(__MIPSEL__)
+# define OVR_CPU_MIPS
+#elif defined(__arm__)
+# define OVR_CPU_ARM
+#else
+# define OVR_CPU_OTHER
+#endif
+
+//-----------------------------------------------------------------------------------
+// ***** Co-Processor Architecture
+//
+// The following co-processors are defined: (OVR_CPU_x)
+//
+// SSE - Available on all modern x86 processors.
+// Altivec - Available on all modern ppc processors.
+// Neon - Available on some armv7+ processors.
+
+#if defined(__SSE__) || defined(OVR_OS_WIN32)
+# define OVR_CPU_SSE
+#endif // __SSE__
+
+#if defined( __ALTIVEC__ )
+# define OVR_CPU_ALTIVEC
+#endif // __ALTIVEC__
+
+#if defined(__ARM_NEON__)
+# define OVR_CPU_ARM_NEON
+#endif // __ARM_NEON__
+
+
+//-----------------------------------------------------------------------------------
+// ***** Compiler
+//
+// The following compilers are defined: (OVR_CC_x)
+//
+// MSVC - Microsoft Visual C/C++
+// INTEL - Intel C++ for Linux / Windows
+// GNU - GNU C++
+// ARM - ARM C/C++
+
+#if defined(__INTEL_COMPILER)
+// Intel 4.0 = 400
+// Intel 5.0 = 500
+// Intel 6.0 = 600
+// Intel 8.0 = 800
+// Intel 9.0 = 900
+# define OVR_CC_INTEL __INTEL_COMPILER
+
+#elif defined(_MSC_VER)
+// MSVC 5.0 = 1100
+// MSVC 6.0 = 1200
+// MSVC 7.0 (VC2002) = 1300
+// MSVC 7.1 (VC2003) = 1310
+// MSVC 8.0 (VC2005) = 1400
+// MSVC 9.0 (VC2008) = 1500
+// MSVC 10.0 (VC2010) = 1600
+# define OVR_CC_MSVC _MSC_VER
+
+#elif defined(__GNUC__)
+# define OVR_CC_GNU
+
+#elif defined(__CC_ARM)
+# define OVR_CC_ARM
+
+#else
+# error "Oculus does not support this Compiler"
+#endif
+
+
+//-----------------------------------------------------------------------------------
+// ***** Compiler Warnings
+
+// Disable MSVC warnings
+#if defined(OVR_CC_MSVC)
+# pragma warning(disable : 4127) // Inconsistent dll linkage
+# pragma warning(disable : 4530) // Exception handling
+# if (OVR_CC_MSVC<1300)
+# pragma warning(disable : 4514) // Unreferenced inline function has been removed
+# pragma warning(disable : 4710) // Function not inlined
+# pragma warning(disable : 4714) // _force_inline not inlined
+# pragma warning(disable : 4786) // Debug variable name longer than 255 chars
+# endif // (OVR_CC_MSVC<1300)
+#endif // (OVR_CC_MSVC)
+
+
+
+// *** Linux Unicode - must come before Standard Includes
+
+#ifdef OVR_OS_LINUX
+// Use glibc unicode functions on linux.
+# ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+# endif
+#endif
+
+//-----------------------------------------------------------------------------------
+// ***** Standard Includes
+//
+#include <stddef.h>
+#include <limits.h>
+#include <float.h>
+
+
+// MSVC Based Memory Leak checking - for now
+#if defined(OVR_CC_MSVC) && defined(OVR_BUILD_DEBUG)
+# define _CRTDBG_MAP_ALLOC
+# include <stdlib.h>
+# include <crtdbg.h>
+
+// Uncomment this to help debug memory leaks under Visual Studio in OVR apps only.
+// This shouldn't be defined in customer releases.
+# ifndef OVR_DEFINE_NEW
+# define OVR_DEFINE_NEW new(__FILE__, __LINE__)
+# define new OVR_DEFINE_NEW
+# endif
+
+#endif
+
+
+//-----------------------------------------------------------------------------------
+// ***** Type definitions for Common Systems
+
+namespace OVR {
+
+typedef char Char;
+
+// Pointer-sized integer
+typedef size_t UPInt;
+typedef ptrdiff_t SPInt;
+
+
+#if defined(OVR_OS_WIN32)
+
+typedef char SByte; // 8 bit Integer (Byte)
+typedef unsigned char UByte;
+typedef short SInt16; // 16 bit Integer (Word)
+typedef unsigned short UInt16;
+typedef long SInt32; // 32 bit Integer
+typedef unsigned long UInt32;
+typedef __int64 SInt64; // 64 bit Integer (QWord)
+typedef unsigned __int64 UInt64;
+
+
+#elif defined(OVR_OS_MAC) || defined(OVR_OS_IPHONE) || defined(OVR_CC_GNU)
+
+typedef int SByte __attribute__((__mode__ (__QI__)));
+typedef unsigned int UByte __attribute__((__mode__ (__QI__)));
+typedef int SInt16 __attribute__((__mode__ (__HI__)));
+typedef unsigned int UInt16 __attribute__((__mode__ (__HI__)));
+typedef int SInt32 __attribute__((__mode__ (__SI__)));
+typedef unsigned int UInt32 __attribute__((__mode__ (__SI__)));
+typedef int SInt64 __attribute__((__mode__ (__DI__)));
+typedef unsigned int UInt64 __attribute__((__mode__ (__DI__)));
+
+#else
+
+#include <sys/types.h>
+typedef int8_t SByte;
+typedef uint8_t UByte;
+typedef int16_t SInt16;
+typedef uint16_t UInt16;
+typedef int32_t SInt32;
+typedef uint32_t UInt32;
+typedef int64_t SInt64;
+typedef uint64_t UInt64;
+
+#endif
+
+
+// ***** BaseTypes Namespace
+
+// BaseTypes namespace is explicitly declared to allow base types to be used
+// by customers directly without other contents of OVR namespace.
+//
+// Its is expected that GFx samples will declare 'using namespace OVR::BaseTypes'
+// to allow using these directly without polluting the target scope with other
+// OVR declarations, such as Ptr<>, String or Mutex.
+namespace BaseTypes
+{
+ using OVR::UPInt;
+ using OVR::SPInt;
+ using OVR::UByte;
+ using OVR::SByte;
+ using OVR::UInt16;
+ using OVR::SInt16;
+ using OVR::UInt32;
+ using OVR::SInt32;
+ using OVR::UInt64;
+ using OVR::SInt64;
+} // OVR::BaseTypes
+
+} // OVR
+
+
+//-----------------------------------------------------------------------------------
+// ***** Macro Definitions
+//
+// We define the following:
+//
+// OVR_BYTE_ORDER - Defined to either OVR_LITTLE_ENDIAN or OVR_BIG_ENDIAN
+// OVR_FORCE_INLINE - Forces inline expansion of function
+// OVR_ASM - Assembly language prefix
+// OVR_STR - Prefixes string with L"" if building unicode
+//
+// OVR_STDCALL - Use stdcall calling convention (Pascal arg order)
+// OVR_CDECL - Use cdecl calling convention (C argument order)
+// OVR_FASTCALL - Use fastcall calling convention (registers)
+//
+
+// Byte order constants, OVR_BYTE_ORDER is defined to be one of these.
+#define OVR_LITTLE_ENDIAN 1
+#define OVR_BIG_ENDIAN 2
+
+
+// Force inline substitute - goes before function declaration
+#if defined(OVR_CC_MSVC)
+# define OVR_FORCE_INLINE __forceinline
+#elif defined(OVR_CC_GNU)
+# define OVR_FORCE_INLINE __attribute__((always_inline))
+#else
+# define OVR_FORCE_INLINE inline
+#endif // OVR_CC_MSVC
+
+
+#if defined(OVR_OS_WIN32)
+
+ // ***** Win32
+
+ // Byte order
+ #define OVR_BYTE_ORDER OVR_LITTLE_ENDIAN
+
+ // Calling convention - goes after function return type but before function name
+ #ifdef __cplusplus_cli
+ # define OVR_FASTCALL __stdcall
+ #else
+ # define OVR_FASTCALL __fastcall
+ #endif
+
+ #define OVR_STDCALL __stdcall
+ #define OVR_CDECL __cdecl
+
+
+ // Assembly macros
+ #if defined(OVR_CC_MSVC)
+ # define OVR_ASM _asm
+ #else
+ # define OVR_ASM asm
+ #endif // (OVR_CC_MSVC)
+
+ #ifdef UNICODE
+ # define OVR_STR(str) L##str
+ #else
+ # define OVR_STR(str) str
+ #endif // UNICODE
+
+#else
+
+ // **** Standard systems
+
+ #if (defined(BYTE_ORDER) && (BYTE_ORDER == BIG_ENDIAN))|| \
+ (defined(_BYTE_ORDER) && (_BYTE_ORDER == _BIG_ENDIAN))
+ # define OVR_BYTE_ORDER OVR_BIG_ENDIAN
+ #elif (defined(__ARMEB__) || defined(OVR_CPU_PPC) || defined(OVR_CPU_PPC64))
+ # define OVR_BYTE_ORDER OVR_BIG_ENDIAN
+ #else
+ # define OVR_BYTE_ORDER OVR_LITTLE_ENDIAN
+ #endif
+
+ // Assembly macros
+ #define OVR_ASM __asm__
+ #define OVR_ASM_PROC(procname) OVR_ASM
+ #define OVR_ASM_END OVR_ASM
+
+ // Calling convention - goes after function return type but before function name
+ #define OVR_FASTCALL
+ #define OVR_STDCALL
+ #define OVR_CDECL
+
+#endif // defined(OVR_OS_WIN32)
+
+
+
+//-----------------------------------------------------------------------------------
+// ***** OVR_DEBUG_BREAK, OVR_ASSERT
+//
+// If not in debug build, macros do nothing
+#ifndef OVR_BUILD_DEBUG
+
+# define OVR_DEBUG_BREAK ((void)0)
+# define OVR_ASSERT(p) ((void)0)
+
+#else
+
+// Microsoft Win32 specific debugging support
+#if defined(OVR_OS_WIN32)
+# ifdef OVR_CPU_X86
+# if defined(__cplusplus_cli)
+# define OVR_DEBUG_BREAK do { __debugbreak(); } while(0)
+# elif defined(OVR_CC_GNU)
+# define OVR_DEBUG_BREAK do { OVR_ASM("int $3\n\t"); } while(0)
+# else
+# define OVR_DEBUG_BREAK do { OVR_ASM int 3 } while (0)
+# endif
+# else
+# define OVR_DEBUG_BREAK do { __debugbreak(); } while(0)
+# endif
+// Unix specific debugging support
+#elif defined(OVR_CPU_X86) || defined(OVR_CPU_X86_64)
+# define OVR_DEBUG_BREAK do { OVR_ASM("int $3\n\t"); } while(0)
+#else
+# define OVR_DEBUG_BREAK do { *((int *) 0) = 1; } while(0)
+#endif
+
+// This will cause compiler breakpoint
+#define OVR_ASSERT(p) do { if (!(p)) { OVR_DEBUG_BREAK; } } while(0)
+
+#endif // OVR_BUILD_DEBUG
+
+
+// Compile-time assert; produces compiler error if condition is false
+#define OVR_COMPILER_ASSERT(x) { int zero = 0; switch(zero) {case 0: case x:;} }
+
+
+
+//-----------------------------------------------------------------------------------
+// ***** OVR_UNUSED - Unused Argument handling
+
+// Macro to quiet compiler warnings about unused parameters/variables.
+#if defined(OVR_CC_GNU)
+# define OVR_UNUSED(a) do {__typeof__ (&a) __attribute__ ((unused)) __tmp = &a; } while(0)
+#else
+# define OVR_UNUSED(a) (a)
+#endif
+
+#define OVR_UNUSED1(a1) OVR_UNUSED(a1)
+#define OVR_UNUSED2(a1,a2) OVR_UNUSED(a1); OVR_UNUSED(a2)
+#define OVR_UNUSED3(a1,a2,a3) OVR_UNUSED2(a1,a2); OVR_UNUSED(a3)
+#define OVR_UNUSED4(a1,a2,a3,a4) OVR_UNUSED3(a1,a2,a3); OVR_UNUSED(a4)
+#define OVR_UNUSED5(a1,a2,a3,a4,a5) OVR_UNUSED4(a1,a2,a3,a4); OVR_UNUSED(a5)
+#define OVR_UNUSED6(a1,a2,a3,a4,a5,a6) OVR_UNUSED4(a1,a2,a3,a4); OVR_UNUSED2(a5,a6)
+#define OVR_UNUSED7(a1,a2,a3,a4,a5,a6,a7) OVR_UNUSED4(a1,a2,a3,a4); OVR_UNUSED3(a5,a6,a7)
+#define OVR_UNUSED8(a1,a2,a3,a4,a5,a6,a7,a8) OVR_UNUSED4(a1,a2,a3,a4); OVR_UNUSED4(a5,a6,a7,a8)
+#define OVR_UNUSED9(a1,a2,a3,a4,a5,a6,a7,a8,a9) OVR_UNUSED4(a1,a2,a3,a4); OVR_UNUSED5(a5,a6,a7,a8,a9)
+
+
+//-----------------------------------------------------------------------------------
+// ***** Configuration Macros
+
+// SF Build type
+#ifdef OVR_BUILD_DEBUG
+# define OVR_BUILD_STRING "Debug"
+#else
+# define OVR_BUILD_STRING "Release"
+#endif
+
+
+//// Enables SF Debugging information
+//# define OVR_BUILD_DEBUG
+
+// OVR_DEBUG_STATEMENT injects a statement only in debug builds.
+// OVR_DEBUG_SELECT injects first argument in debug builds, second argument otherwise.
+#ifdef OVR_BUILD_DEBUG
+#define OVR_DEBUG_STATEMENT(s) s
+#define OVR_DEBUG_SELECT(d, nd) d
+#else
+#define OVR_DEBUG_STATEMENT(s)
+#define OVR_DEBUG_SELECT(d, nd) nd
+#endif
+
+
+#define OVR_ENABLE_THREADS
+//
+// Prevents OVR from defining new within
+// type macros, so developers can override
+// new using the #define new new(...) trick
+// - used with OVR_DEFINE_NEW macro
+//# define OVR_BUILD_DEFINE_NEW
+//
+
+
+#endif // OVR_Types_h
diff --git a/LibOVR/Src/Kernel/OVR_UTF8Util.cpp b/LibOVR/Src/Kernel/OVR_UTF8Util.cpp
new file mode 100644
index 0000000..21485df
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_UTF8Util.cpp
@@ -0,0 +1,545 @@
+/**************************************************************************
+
+Filename : OVR_UTF8Util.cpp
+Content : UTF8 Unicode character encoding/decoding support
+Created : September 19, 2012
+Notes :
+Notes : Much useful info at "UTF-8 and Unicode FAQ"
+ http://www.cl.cam.ac.uk/~mgk25/unicode.html
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#include "OVR_UTF8Util.h"
+
+namespace OVR { namespace UTF8Util {
+
+SPInt OVR_STDCALL GetLength(const char* buf, SPInt buflen)
+{
+ const char* p = buf;
+ SPInt length = 0;
+
+ if (buflen != -1)
+ {
+ while (p - buf < buflen)
+ {
+ // We should be able to have ASStrings with 0 in the middle.
+ UTF8Util::DecodeNextChar_Advance0(&p);
+ length++;
+ }
+ }
+ else
+ {
+ while (UTF8Util::DecodeNextChar_Advance0(&p))
+ length++;
+ }
+
+ return length;
+}
+
+UInt32 OVR_STDCALL GetCharAt(SPInt index, const char* putf8str, SPInt length)
+{
+ const char* buf = putf8str;
+ UInt32 c = 0;
+
+ if (length != -1)
+ {
+ while (buf - putf8str < length)
+ {
+ c = UTF8Util::DecodeNextChar_Advance0(&buf);
+ if (index == 0)
+ return c;
+ index--;
+ }
+
+ return c;
+ }
+
+ do
+ {
+ c = UTF8Util::DecodeNextChar_Advance0(&buf);
+ index--;
+
+ if (c == 0)
+ {
+ // We've hit the end of the string; don't go further.
+ OVR_ASSERT(index == 0);
+ return c;
+ }
+ } while (index >= 0);
+
+ return c;
+}
+
+SPInt OVR_STDCALL GetByteIndex(SPInt index, const char *putf8str, SPInt length)
+{
+ const char* buf = putf8str;
+
+ if (length != -1)
+ {
+ while ((buf - putf8str) < length && index > 0)
+ {
+ UTF8Util::DecodeNextChar_Advance0(&buf);
+ index--;
+ }
+
+ return buf-putf8str;
+ }
+
+ while (index > 0)
+ {
+ UInt32 c = UTF8Util::DecodeNextChar_Advance0(&buf);
+ index--;
+
+ if (c == 0)
+ return buf-putf8str;
+ };
+
+ return buf-putf8str;
+}
+
+int OVR_STDCALL GetEncodeCharSize(UInt32 ucs_character)
+{
+ if (ucs_character <= 0x7F)
+ return 1;
+ else if (ucs_character <= 0x7FF)
+ return 2;
+ else if (ucs_character <= 0xFFFF)
+ return 3;
+ else if (ucs_character <= 0x1FFFFF)
+ return 4;
+ else if (ucs_character <= 0x3FFFFFF)
+ return 5;
+ else if (ucs_character <= 0x7FFFFFFF)
+ return 6;
+ else
+ return 0;
+}
+
+UInt32 OVR_STDCALL DecodeNextChar_Advance0(const char** putf8Buffer)
+{
+ UInt32 uc;
+ char c;
+
+ // Security considerations:
+ //
+ // Changed, this is now only the case for DecodeNextChar:
+ // - If we hit a zero byte, we want to return 0 without stepping
+ // the buffer pointer past the 0. th
+ //
+ // If we hit an "overlong sequence"; i.e. a character encoded
+ // in a longer multibyte string than is necessary, then we
+ // need to discard the character. This is so attackers can't
+ // disguise dangerous characters or character sequences --
+ // there is only one valid encoding for each character.
+ //
+ // If we decode characters { 0xD800 .. 0xDFFF } or { 0xFFFE,
+ // 0xFFFF } then we ignore them; they are not valid in UTF-8.
+
+ // This isn't actually an invalid character; it's a valid char that
+ // looks like an inverted question mark.
+#define INVALID_CHAR 0x0FFFD
+
+#define FIRST_BYTE(mask, shift) \
+ uc = (c & (mask)) << (shift);
+
+#define NEXT_BYTE(shift) \
+ c = **putf8Buffer; \
+ if (c == 0) return 0; /* end of buffer, do not advance */ \
+ if ((c & 0xC0) != 0x80) return INVALID_CHAR; /* standard check */ \
+ (*putf8Buffer)++; \
+ uc |= (c & 0x3F) << shift;
+
+ c = **putf8Buffer;
+ (*putf8Buffer)++;
+ if (c == 0)
+ return 0; // End of buffer.
+
+ if ((c & 0x80) == 0) return (UInt32) c; // Conventional 7-bit ASCII.
+
+ // Multi-byte sequences.
+ if ((c & 0xE0) == 0xC0)
+ {
+ // Two-byte sequence.
+ FIRST_BYTE(0x1F, 6);
+ NEXT_BYTE(0);
+ if (uc < 0x80) return INVALID_CHAR; // overlong
+ return uc;
+ }
+ else if ((c & 0xF0) == 0xE0)
+ {
+ // Three-byte sequence.
+ FIRST_BYTE(0x0F, 12);
+ NEXT_BYTE(6);
+ NEXT_BYTE(0);
+ if (uc < 0x800) return INVALID_CHAR; // overlong
+ // Not valid ISO 10646, but Flash requires these to work
+ // see AS3 test e15_5_3_2_3 for String.fromCharCode().charCodeAt(0)
+ // if (uc >= 0x0D800 && uc <= 0x0DFFF) return INVALID_CHAR;
+ // if (uc == 0x0FFFE || uc == 0x0FFFF) return INVALID_CHAR; // not valid ISO 10646
+ return uc;
+ }
+ else if ((c & 0xF8) == 0xF0)
+ {
+ // Four-byte sequence.
+ FIRST_BYTE(0x07, 18);
+ NEXT_BYTE(12);
+ NEXT_BYTE(6);
+ NEXT_BYTE(0);
+ if (uc < 0x010000) return INVALID_CHAR; // overlong
+ return uc;
+ }
+ else if ((c & 0xFC) == 0xF8)
+ {
+ // Five-byte sequence.
+ FIRST_BYTE(0x03, 24);
+ NEXT_BYTE(18);
+ NEXT_BYTE(12);
+ NEXT_BYTE(6);
+ NEXT_BYTE(0);
+ if (uc < 0x0200000) return INVALID_CHAR; // overlong
+ return uc;
+ }
+ else if ((c & 0xFE) == 0xFC)
+ {
+ // Six-byte sequence.
+ FIRST_BYTE(0x01, 30);
+ NEXT_BYTE(24);
+ NEXT_BYTE(18);
+ NEXT_BYTE(12);
+ NEXT_BYTE(6);
+ NEXT_BYTE(0);
+ if (uc < 0x04000000) return INVALID_CHAR; // overlong
+ return uc;
+ }
+ else
+ {
+ // Invalid.
+ return INVALID_CHAR;
+ }
+}
+
+
+void OVR_STDCALL EncodeChar(char* pbuffer, SPInt* pindex, UInt32 ucs_character)
+{
+ if (ucs_character <= 0x7F)
+ {
+ // Plain single-byte ASCII.
+ pbuffer[(*pindex)++] = (char) ucs_character;
+ }
+ else if (ucs_character <= 0x7FF)
+ {
+ // Two bytes.
+ pbuffer[(*pindex)++] = 0xC0 | (char)(ucs_character >> 6);
+ pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 0) & 0x3F);
+ }
+ else if (ucs_character <= 0xFFFF)
+ {
+ // Three bytes.
+ pbuffer[(*pindex)++] = 0xE0 | (char)(ucs_character >> 12);
+ pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 6) & 0x3F);
+ pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 0) & 0x3F);
+ }
+ else if (ucs_character <= 0x1FFFFF)
+ {
+ // Four bytes.
+ pbuffer[(*pindex)++] = 0xF0 | (char)(ucs_character >> 18);
+ pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 12) & 0x3F);
+ pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 6) & 0x3F);
+ pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 0) & 0x3F);
+ }
+ else if (ucs_character <= 0x3FFFFFF)
+ {
+ // Five bytes.
+ pbuffer[(*pindex)++] = 0xF8 | (char)(ucs_character >> 24);
+ pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 18) & 0x3F);
+ pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 12) & 0x3F);
+ pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 6) & 0x3F);
+ pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 0) & 0x3F);
+ }
+ else if (ucs_character <= 0x7FFFFFFF)
+ {
+ // Six bytes.
+ pbuffer[(*pindex)++] = 0xFC | (char)(ucs_character >> 30);
+ pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 24) & 0x3F);
+ pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 18) & 0x3F);
+ pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 12) & 0x3F);
+ pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 6) & 0x3F);
+ pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 0) & 0x3F);
+ }
+ else
+ {
+ // Invalid char; don't encode anything.
+ }
+}
+
+SPInt OVR_STDCALL GetEncodeStringSize(const wchar_t* pchar, SPInt length)
+{
+ SPInt len = 0;
+ if (length != -1)
+ for (int i = 0; i < length; i++)
+ {
+ len += GetEncodeCharSize(pchar[i]);
+ }
+ else
+ for (int i = 0;; i++)
+ {
+ if (pchar[i] == 0)
+ return len;
+ len += GetEncodeCharSize(pchar[i]);
+ }
+ return len;
+}
+
+void OVR_STDCALL EncodeString(char *pbuff, const wchar_t* pchar, SPInt length)
+{
+ SPInt ofs = 0;
+ if (length != -1)
+ {
+ for (int i = 0; i < length; i++)
+ {
+ EncodeChar(pbuff, &ofs, pchar[i]);
+ }
+ }
+ else
+ {
+ for (int i = 0;; i++)
+ {
+ if (pchar[i] == 0)
+ break;
+ EncodeChar(pbuff, &ofs, pchar[i]);
+ }
+ }
+ pbuff[ofs] = 0;
+}
+
+UPInt OVR_STDCALL DecodeString(wchar_t *pbuff, const char* putf8str, SPInt bytesLen)
+{
+ wchar_t *pbegin = pbuff;
+ if (bytesLen == -1)
+ {
+ while (1)
+ {
+ UInt32 ch = DecodeNextChar_Advance0(&putf8str);
+ if (ch == 0)
+ break;
+ else if (ch >= 0xFFFF)
+ ch = 0xFFFD;
+ *pbuff++ = wchar_t(ch);
+ }
+ }
+ else
+ {
+ const char* p = putf8str;
+ while ((p - putf8str) < bytesLen)
+ {
+ UInt32 ch = DecodeNextChar_Advance0(&p);
+ if (ch >= 0xFFFF)
+ ch = 0xFFFD;
+ *pbuff++ = wchar_t(ch);
+ }
+ }
+
+ *pbuff = 0;
+ return pbuff - pbegin;
+}
+
+
+#ifdef UTF8_UNIT_TEST
+
+// Compile this test case with something like:
+//
+// gcc utf8.cpp -g -I.. -DUTF8_UNIT_TEST -lstdc++ -o utf8_test
+//
+// or
+//
+// cl utf8.cpp -Zi -Od -DUTF8_UNIT_TEST -I..
+//
+// If possible, try running the test program with the first arg
+// pointing at the file:
+//
+// http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
+//
+// and examine the results by eye to make sure they are acceptable to
+// you.
+
+
+#include "base/utility.h"
+#include <stdio.h>
+
+
+bool check_equal(const char* utf8_in, const UInt32* ucs_in)
+{
+ for (;;)
+ {
+ UInt32 next_ucs = *ucs_in++;
+ UInt32 next_ucs_from_utf8 = utf8::decode_next_unicode_character(&utf8_in);
+ if (next_ucs != next_ucs_from_utf8)
+ {
+ return false;
+ }
+ if (next_ucs == 0)
+ {
+ OVR_ASSERT(next_ucs_from_utf8 == 0);
+ break;
+ }
+ }
+
+ return true;
+}
+
+
+void log_ascii(const char* line)
+{
+ for (;;)
+ {
+ unsigned char c = (unsigned char) *line++;
+ if (c == 0)
+ {
+ // End of line.
+ return;
+ }
+ else if (c != '\n'
+ && (c < 32 || c > 127))
+ {
+ // Non-printable as plain ASCII.
+ printf("<0x%02X>", (int) c);
+ }
+ else
+ {
+ printf("%c", c);
+ }
+ }
+}
+
+
+void log_ucs(const UInt32* line)
+{
+ for (;;)
+ {
+ UInt32 uc = *line++;
+ if (uc == 0)
+ {
+ // End of line.
+ return;
+ }
+ else if (uc != '\n'
+ && (uc < 32 || uc > 127))
+ {
+ // Non-printable as plain ASCII.
+ printf("<U-%04X>", uc);
+ }
+ else
+ {
+ printf("%c", (char) uc);
+ }
+ }
+}
+
+
+// Simple canned test.
+int main(int argc, const char* argv[])
+{
+ {
+ const char* test8 = "Ignacio Castaño";
+ const UInt32 test32[] =
+ {
+ 0x49, 0x67, 0x6E, 0x61, 0x63,
+ 0x69, 0x6F, 0x20, 0x43, 0x61,
+ 0x73, 0x74, 0x61, 0xF1, 0x6F,
+ 0x00
+ };
+
+ OVR_ASSERT(check_equal(test8, test32));
+ }
+
+ // If user passed an arg, try reading the file as UTF-8 encoded text.
+ if (argc > 1)
+ {
+ const char* filename = argv[1];
+ FILE* fp = fopen(filename, "rb");
+ if (fp == NULL)
+ {
+ printf("Can't open file '%s'\n", filename);
+ return 1;
+ }
+
+ // Read lines from the file, encode/decode them, and highlight discrepancies.
+ const int LINE_SIZE = 200; // max line size
+ char line_buffer_utf8[LINE_SIZE];
+ char reencoded_utf8[6 * LINE_SIZE];
+ UInt32 line_buffer_ucs[LINE_SIZE];
+
+ int byte_counter = 0;
+ for (;;)
+ {
+ int c = fgetc(fp);
+ if (c == EOF)
+ {
+ // Done.
+ break;
+ }
+ line_buffer_utf8[byte_counter++] = c;
+ if (c == '\n' || byte_counter >= LINE_SIZE - 2)
+ {
+ // End of line. Process the line.
+ line_buffer_utf8[byte_counter++] = 0; // terminate.
+
+ // Decode into UCS.
+ const char* p = line_buffer_utf8;
+ UInt32* q = line_buffer_ucs;
+ for (;;)
+ {
+ UInt32 uc = UTF8Util::DecodeNextChar(&p);
+ *q++ = uc;
+
+ OVR_ASSERT(q < line_buffer_ucs + LINE_SIZE);
+ OVR_ASSERT(p < line_buffer_utf8 + LINE_SIZE);
+
+ if (uc == 0) break;
+ }
+
+ // Encode back into UTF-8.
+ q = line_buffer_ucs;
+ int index = 0;
+ for (;;)
+ {
+ UInt32 uc = *q++;
+ OVR_ASSERT(index < LINE_SIZE * 6 - 6);
+ int last_index = index;
+ UTF8Util::EncodeChar(reencoded_utf8, &index, uc);
+ OVR_ASSERT(index <= last_index + 6);
+ if (uc == 0) break;
+ }
+
+ // This can be useful for debugging.
+#if 0
+ // Show the UCS and the re-encoded UTF-8.
+ log_ucs(line_buffer_ucs);
+ log_ascii(reencoded_utf8);
+#endif // 0
+
+ OVR_ASSERT(check_equal(line_buffer_utf8, line_buffer_ucs));
+ OVR_ASSERT(check_equal(reencoded_utf8, line_buffer_ucs));
+
+ // Start next line.
+ byte_counter = 0;
+ }
+ }
+
+ fclose(fp);
+ }
+
+ return 0;
+}
+
+
+#endif // UTF8_UNIT_TEST
+
+}} // namespace UTF8Util::OVR
+
diff --git a/LibOVR/Src/Kernel/OVR_UTF8Util.h b/LibOVR/Src/Kernel/OVR_UTF8Util.h
new file mode 100644
index 0000000..bf5af46
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_UTF8Util.h
@@ -0,0 +1,88 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_UTF8Util.h
+Content : UTF8 Unicode character encoding/decoding support
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#ifndef OVR_UTF8Util_h
+#define OVR_UTF8Util_h
+
+#include "OVR_Types.h"
+
+namespace OVR { namespace UTF8Util {
+
+//-----------------------------------------------------------------------------------
+
+// *** UTF8 string length and indexing.
+
+// Determines the length of UTF8 string in characters.
+// If source length is specified (in bytes), null 0 character is counted properly.
+SPInt OVR_STDCALL GetLength(const char* putf8str, SPInt length = -1);
+
+// Gets a decoded UTF8 character at index; you can access up to the index returned
+// by GetLength. 0 will be returned for out of bounds access.
+UInt32 OVR_STDCALL GetCharAt(SPInt index, const char* putf8str, SPInt length = -1);
+
+// Converts UTF8 character index into byte offset.
+// -1 is returned if index was out of bounds.
+SPInt OVR_STDCALL GetByteIndex(SPInt index, const char* putf8str, SPInt length = -1);
+
+
+// *** 16-bit Unicode string Encoding/Decoding routines.
+
+// Determines the number of bytes necessary to encode a string.
+// Does not count the terminating 0 (null) character.
+SPInt OVR_STDCALL GetEncodeStringSize(const wchar_t* pchar, SPInt length = -1);
+
+// Encodes a unicode (UCS-2 only) string into a buffer. The size of buffer must be at
+// least GetEncodeStringSize() + 1.
+void OVR_STDCALL EncodeString(char *pbuff, const wchar_t* pchar, SPInt length = -1);
+
+// Decode UTF8 into a wchar_t buffer. Must have GetLength()+1 characters available.
+// Characters over 0xFFFF are replaced with 0xFFFD.
+// Returns the length of resulting string (number of characters)
+UPInt OVR_STDCALL DecodeString(wchar_t *pbuff, const char* putf8str, SPInt bytesLen = -1);
+
+
+// *** Individual character Encoding/Decoding.
+
+// Determined the number of bytes necessary to encode a UCS character.
+int OVR_STDCALL GetEncodeCharSize(UInt32 ucsCharacter);
+
+// Encodes the given UCS character into the given UTF-8 buffer.
+// Writes the data starting at buffer[offset], and
+// increments offset by the number of bytes written.
+// May write up to 6 bytes, so make sure there's room in the buffer
+void OVR_STDCALL EncodeChar(char* pbuffer, SPInt* poffset, UInt32 ucsCharacter);
+
+// Return the next Unicode character in the UTF-8 encoded buffer.
+// Invalid UTF-8 sequences produce a U+FFFD character as output.
+// Advances *utf8_buffer past the character returned. Pointer advance
+// occurs even if the terminating 0 character is hit, since that allows
+// strings with middle '\0' characters to be supported.
+UInt32 OVR_STDCALL DecodeNextChar_Advance0(const char** putf8Buffer);
+
+// Safer version of DecodeNextChar, which doesn't advance pointer if
+// null character is hit.
+inline UInt32 DecodeNextChar(const char** putf8Buffer)
+{
+ UInt32 ch = DecodeNextChar_Advance0(putf8Buffer);
+ if (ch == 0)
+ (*putf8Buffer)--;
+ return ch;
+}
+
+
+}} // OVR::UTF8Util
+
+#endif
diff --git a/LibOVR/Src/OVR_Device.h b/LibOVR/Src/OVR_Device.h
new file mode 100644
index 0000000..4434916
--- /dev/null
+++ b/LibOVR/Src/OVR_Device.h
@@ -0,0 +1,627 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_Device.h
+Content : Definition of HMD-related Device interfaces
+Created : September 21, 2012
+Authors : Michael Antonov
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_Device_h
+#define OVR_Device_h
+
+#include "OVR_DeviceConstants.h"
+#include "OVR_DeviceHandle.h"
+#include "OVR_DeviceMessages.h"
+#include "OVR_HIDDeviceBase.h"
+
+#include "Kernel/OVR_Atomic.h"
+#include "Kernel/OVR_RefCount.h"
+#include "Kernel/OVR_String.h"
+
+namespace OVR {
+
+class SensorDevice;
+class DeviceCommon;
+class DeviceManager;
+
+// MessageHandler is a base class from which users derive to receive messages,
+// its OnMessage handler will be called for messages once it is installed on
+// a device. Same message handler can be installed on multiple devices.
+class MessageHandler
+{
+ friend class MessageHandlerImpl;
+public:
+ MessageHandler();
+ virtual ~MessageHandler();
+
+ // Returns 'true' if handler is currently installed on any devices.
+ bool IsHandlerInstalled() const;
+
+ // Should be called from derived class destructor to avoid handler
+ // being called after it exits.
+ void RemoveHandlerFromDevices();
+
+ // Returns a pointer to the internal lock object that is locked by a
+ // background thread while OnMessage() is called.
+ // This lock guaranteed to survive until ~MessageHandler.
+ Lock* GetHandlerLock() const;
+
+
+ virtual void OnMessage(const Message&) { }
+
+ // Determines if handler supports a specific message type. Can
+ // be used to filter out entire message groups. The result
+ // returned by this function shouldn't change after handler creation.
+ virtual bool SupportsMessageType(MessageType) const { return true; }
+
+private:
+ UPInt Internal[4];
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** DeviceBase
+
+// DeviceBase is the base class for all OVR Devices. It provides the following basic
+// functionality:
+// - Reports device type, manager, and associated parent (if any).
+// - Supports installable message handlers, which are notified of device events.
+// - Device objects are created through DeviceHandle::CreateDevice or more commonly
+// through DeviceEnumerator<>::CreateDevice.
+// - Created devices are reference counted, starting with RefCount of 1.
+// - Device is resources are cleaned up when it is Released, although its handles
+// may survive longer if referenced.
+
+class DeviceBase : public NewOverrideBase
+{
+ friend class DeviceHandle;
+ friend class DeviceManagerImpl;
+public:
+
+ // Enumerating DeviceBase enumerates all devices.
+ enum { EnumDeviceType = Device_All };
+
+ virtual ~DeviceBase() { }
+ virtual void AddRef();
+ virtual void Release();
+
+ virtual DeviceBase* GetParent() const;
+ virtual DeviceManager* GetManager() const;
+
+ virtual void SetMessageHandler(MessageHandler* handler);
+ virtual MessageHandler* GetMessageHandler() const;
+
+ virtual DeviceType GetType() const;
+ virtual bool GetDeviceInfo(DeviceInfo* info) const;
+
+ // returns the MessageHandler's lock
+ Lock* GetHandlerLock() const;
+protected:
+ // Internal
+ virtual DeviceCommon* getDeviceCommon() const = 0;
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** DeviceInfo
+
+// DeviceInfo describes a device and its capabilities, obtained by calling
+// GetDeviceInfo. This base class only contains device-independent functionality;
+// users will normally use a derived HMDInfo or SensorInfo classes for more
+// extensive device info.
+
+class DeviceInfo
+{
+public:
+ DeviceInfo() : InfoClassType(Device_None), Type(Device_None), Version(0)
+ { ProductName[0] = Manufacturer[0] = 0; }
+
+ enum { MaxNameLength = 32 };
+
+ // Type of device for which DeviceInfo is intended.
+ // This will be set to Device_HMD for HMDInfo structure, note that this may be
+ // different form the actual device type since (Device_None) is valid.
+ const DeviceType InfoClassType;
+ // Type of device this describes. This must be the same as InfoClassType when
+ // InfoClassType != Device_None.
+ DeviceType Type;
+ // Name string describing the product: "Oculus Rift DK1", etc.
+ char ProductName[MaxNameLength];
+ char Manufacturer[MaxNameLength];
+ unsigned Version;
+
+protected:
+ DeviceInfo(DeviceType type) : InfoClassType(type), Type(type), Version(0)
+ { ProductName[0] = Manufacturer[0] = 0; }
+ void operator = (const DeviceInfo&) { OVR_ASSERT(0); } // Assignment not allowed.
+};
+
+
+//-------------------------------------------------------------------------------------
+// DeviceEnumerationArgs provides device enumeration argumenrs for DeviceManager::EnumerateDevicesEx.
+class DeviceEnumerationArgs
+{
+public:
+ DeviceEnumerationArgs(DeviceType enumType, bool availableOnly)
+ : EnumType(enumType), AvailableOnly(availableOnly)
+ { }
+
+ // Helper; returns true if args match our enumeration criteria.
+ bool MatchRule(DeviceType type, bool available) const
+ {
+ return ((EnumType == type) || (EnumType == Device_All)) &&
+ (available || !AvailableOnly);
+ }
+
+protected:
+ DeviceType EnumType;
+ bool AvailableOnly;
+};
+
+
+// DeviceEnumerator<> is used to enumerate and create devices of specified class,
+// it is returned by calling MeviceManager::EnumerateDevices. Initially, the enumerator will
+// refer to the first device of specified type. Additional devices can be accessed by
+// calling Next().
+
+template<class T = DeviceBase>
+class DeviceEnumerator : public DeviceHandle
+{
+ friend class DeviceManager;
+ friend class DeviceManagerImpl;
+public:
+ DeviceEnumerator()
+ : DeviceHandle(), EnumArgs(Device_None, true) { }
+
+ // Next advances enumeration to the next device that first criteria.
+ // Returns false if no more devices exist that match enumeration criteria.
+ bool Next() { return enumerateNext(EnumArgs); }
+
+ // Creates an instance of the device referenced by enumerator; returns null
+ // if enumerator does not refer to a valid device or device is unavailable.
+ // If device was already created, the same object with incremented ref-count is returned.
+ T* CreateDevice() { return static_cast<T*>(DeviceHandle::CreateDevice()); }
+
+protected:
+ DeviceEnumerator(const DeviceHandle &dev, const DeviceEnumerationArgs& args)
+ : DeviceHandle(dev), EnumArgs(args)
+ { }
+
+ DeviceEnumerationArgs EnumArgs;
+};
+
+//-------------------------------------------------------------------------------------
+// ***** DeviceManager
+
+// DeviceManager maintains and provides access to devices supported by OVR, such as
+// HMDs and sensors. A single instance of DeviceManager is normally created at
+// program startup, allowing devices to be enumerated and created. DeviceManager is
+// reference counted and is AddRefed by its created child devices, causing it to
+// always be the last object that is released.
+//
+// Install MessageHandler on DeviceManager to detect when devices are inserted or removed.
+//
+// The following code will create the manager and its first available HMDDevice,
+// and then release it when not needed:
+//
+// DeviceManager* manager = DeviceManager::Create();
+// HMDDevice* hmd = manager->EnumerateDevices<HMDDevice>().CreateDevice();
+//
+// if (hmd) hmd->Release();
+// if (manager) manager->Release();
+
+
+class DeviceManager : public DeviceBase
+{
+public:
+
+ DeviceManager()
+ { }
+
+ // DeviceBase implementation.
+ virtual DeviceType GetType() const { return Device_Manager; }
+ virtual DeviceManager* GetManager() const { return const_cast<DeviceManager*>(this); }
+
+
+ // EnumerateDevices enumerates all of the available devices of the specified class,
+ // returning an enumerator that references the first device. An empty enumerator is
+ // returned if no devices are available. The following APIs are exposed through
+ // DeviceEnumerator:
+ // DeviceEnumerator::GetType() - Check device type. Returns Device_None
+ // if no device was found/pointed to.
+ // DeviceEnumerator::GetDeviceInfo() - Get more information on device.
+ // DeviceEnumerator::CreateDevice() - Create an instance of device.
+ // DeviceEnumerator::Next() - Move onto next device.
+ template<class D>
+ DeviceEnumerator<D> EnumerateDevices(bool availableOnly = true)
+ {
+ // TBD: A cleaner (but less efficient) alternative is though enumeratorFromHandle.
+ DeviceEnumerator<> e = EnumerateDevicesEx(DeviceEnumerationArgs((DeviceType)D::EnumDeviceType, availableOnly));
+ return *reinterpret_cast<DeviceEnumerator<D>*>(&e);
+ }
+
+ // EnumerateDevicesEx provides internal implementation for device enumeration, enumerating
+ // devices based on dynamically specified DeviceType in DeviceEnumerationArgs.
+ // End users should call DeumerateDevices<>() instead.
+ virtual DeviceEnumerator<> EnumerateDevicesEx(const DeviceEnumerationArgs& args) = 0;
+
+ // Adds a device (DeviceCreateDesc*) into Devices. Returns NULL,
+ // if unsuccessful or device is already in the list.
+ virtual Ptr<DeviceCreateDesc> AddDevice_NeedsLock(const DeviceCreateDesc& createDesc) = 0;
+
+ // Creates a new DeviceManager. Only one instance of DeviceManager should be created at a time.
+ static DeviceManager* Create();
+
+ // Static constant for this device type, used in template cast type checks.
+ enum { EnumDeviceType = Device_Manager };
+
+protected:
+ DeviceEnumerator<> enumeratorFromHandle(const DeviceHandle& h, const DeviceEnumerationArgs& args)
+ { return DeviceEnumerator<>(h, args); }
+
+ DeviceManager* getThis() { return this; }
+};
+
+
+
+//-------------------------------------------------------------------------------------
+// ***** HMDInfo
+
+// This structure describes various aspects of the HMD allowing us to configure rendering.
+//
+// Currently included data:
+// - Physical screen dimensions, resolution, and eye distances.
+// (some of these will be configurable with a tool in the future).
+// These arguments allow us to properly setup projection across HMDs.
+// - DisplayDeviceName for identifying HMD screen; system-specific interpretation.
+//
+// TBD:
+// - Power on/ off?
+// - Sensor rates and capabilities
+// - Distortion radius/variables
+// - Screen update frequency
+// - Distortion needed flag
+// - Update modes:
+// Set update mode: Stereo (both sides together), mono (same in both eyes),
+// Alternating, Alternating scan-lines.
+
+class HMDInfo : public DeviceInfo
+{
+public:
+ // Size of the entire screen, in pixels.
+ unsigned HResolution, VResolution;
+ // Physical dimensions of the active screen in meters. Can be used to calculate
+ // projection center while considering IPD.
+ float HScreenSize, VScreenSize;
+ // Physical offset from the top of the screen to the eye center, in meters.
+ // This will usually, but not necessarily be half of VScreenSize.
+ float VScreenCenter;
+ // Distance from the eye to screen surface, in meters.
+ // Useful for calculating FOV and projection.
+ float EyeToScreenDistance;
+ // Distance between physical lens centers useful for calculating distortion center.
+ float LensSeparationDistance;
+ // Configured distance between the user's eye centers, in meters. Defaults to 0.064.
+ float InterpupillaryDistance;
+
+ // Radial distortion correction coefficients.
+ // The distortion assumes that the input texture coordinates will be scaled
+ // by the following equation:
+ // uvResult = uvInput * (K0 + K1 * uvLength^2 + K2 * uvLength^4)
+ // Where uvInput is the UV vector from the center of distortion in direction
+ // of the mapped pixel, uvLength is the magnitude of that vector, and uvResult
+ // the corresponding location after distortion.
+ float DistortionK[4];
+
+ float ChromaAbCorrection[4];
+
+ // Desktop coordinate position of the screen (can be negative; may not be present on all platforms)
+ int DesktopX, DesktopY;
+
+ // Windows:
+ // "\\\\.\\DISPLAY3", etc. Can be used in EnumDisplaySettings/CreateDC.
+ char DisplayDeviceName[32];
+
+ // MacOS:
+ long DisplayId;
+
+
+ HMDInfo()
+ : DeviceInfo(Device_HMD),
+ HResolution(0), VResolution(0), HScreenSize(0), VScreenSize(0),
+ VScreenCenter(0), EyeToScreenDistance(0),
+ LensSeparationDistance(0), InterpupillaryDistance(0),
+ DesktopX(0), DesktopY(0), DisplayId(0)
+ {
+ DisplayDeviceName[0] = 0;
+ memset(DistortionK, 0, sizeof(DistortionK));
+ DistortionK[0] = 1;
+ ChromaAbCorrection[0] = ChromaAbCorrection[2] = 1;
+ ChromaAbCorrection[1] = ChromaAbCorrection[3] = 0;
+ }
+
+ // Operator = copies local fields only (base class must be correct already)
+ void operator = (const HMDInfo& src)
+ {
+ HResolution = src.HResolution;
+ VResolution = src.VResolution;
+ HScreenSize = src.HScreenSize;
+ VScreenSize = src.VScreenSize;
+ VScreenCenter = src.VScreenCenter;
+ EyeToScreenDistance = src.EyeToScreenDistance;
+ LensSeparationDistance = src.LensSeparationDistance;
+ InterpupillaryDistance = src.InterpupillaryDistance;
+ DistortionK[0] = src.DistortionK[0];
+ DistortionK[1] = src.DistortionK[1];
+ DistortionK[2] = src.DistortionK[2];
+ DistortionK[3] = src.DistortionK[3];
+ ChromaAbCorrection[0] = src.ChromaAbCorrection[0];
+ ChromaAbCorrection[1] = src.ChromaAbCorrection[1];
+ ChromaAbCorrection[2] = src.ChromaAbCorrection[2];
+ ChromaAbCorrection[3] = src.ChromaAbCorrection[3];
+ DesktopX = src.DesktopX;
+ DesktopY = src.DesktopY;
+ memcpy(DisplayDeviceName, src.DisplayDeviceName, sizeof(DisplayDeviceName));
+ DisplayId = src.DisplayId;
+ }
+
+ bool IsSameDisplay(const HMDInfo& o) const
+ {
+ return DisplayId == o.DisplayId &&
+ String::CompareNoCase(DisplayDeviceName,
+ o.DisplayDeviceName) == 0;
+ }
+
+};
+
+
+// HMDDevice represents an Oculus HMD device unit. An instance of this class
+// is typically created from the DeviceManager.
+// After HMD device is created, we its sensor data can be obtained by
+// first creating a Sensor object and then.
+
+// TBD:
+// - Configure Sensor
+// - APIs to set On-Screen message, other states?
+
+class HMDDevice : public DeviceBase
+{
+public:
+ HMDDevice()
+ { }
+
+ // Static constant for this device type, used in template cast type checks.
+ enum { EnumDeviceType = Device_HMD };
+
+ virtual DeviceType GetType() const { return Device_HMD; }
+
+ // Creates a sensor associated with this HMD.
+ virtual SensorDevice* GetSensor() = 0;
+
+ // Disconnects from real HMD device. This HMDDevice remains as 'fake' HMD.
+ // SensorDevice ptr is used to restore the 'fake' HMD (can be NULL).
+ HMDDevice* Disconnect(SensorDevice*);
+
+ // Returns 'true' if HMD device is a 'fake' HMD (was created this way or
+ // 'Disconnect' method was called).
+ bool IsDisconnected() const;
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** SensorRange & SensorInfo
+
+// SensorRange specifies maximum value ranges that SensorDevice hardware is configured
+// to detect. Although this range doesn't affect the scale of MessageBodyFrame values,
+// physical motions whose positive or negative magnitude is outside the specified range
+// may get clamped or misreported. Setting lower values may result in higher precision
+// tracking.
+struct SensorRange
+{
+ SensorRange(float maxAcceleration = 0.0f, float maxRotationRate = 0.0f,
+ float maxMagneticField = 0.0f)
+ : MaxAcceleration(maxAcceleration), MaxRotationRate(maxRotationRate),
+ MaxMagneticField(maxMagneticField)
+ { }
+
+ // Maximum detected acceleration in m/s^2. Up to 8*G equivalent support guaranteed,
+ // where G is ~9.81 m/s^2.
+ // Oculus DK1 HW has thresholds near: 2, 4 (default), 8, 16 G.
+ float MaxAcceleration;
+ // Maximum detected angular velocity in rad/s. Up to 8*Pi support guaranteed.
+ // Oculus DK1 HW thresholds near: 1, 2, 4, 8 Pi (default).
+ float MaxRotationRate;
+ // Maximum detectable Magnetic field strength in Gauss. Up to 2.5 Gauss support guaranteed.
+ // Oculus DK1 HW thresholds near: 0.88, 1.3, 1.9, 2.5 gauss.
+ float MaxMagneticField;
+};
+
+// SensorInfo describes capabilities of the sensor device.
+class SensorInfo : public DeviceInfo
+{
+public:
+ SensorInfo() : DeviceInfo(Device_Sensor), VendorId(0), ProductId(0)
+ {
+ SerialNumber[0] = 0;
+ }
+
+ // HID Vendor and ProductId of the device.
+ UInt16 VendorId;
+ UInt16 ProductId;
+ // MaxRanges report maximum sensor range values supported by HW.
+ SensorRange MaxRanges;
+ // Sensor (and display) serial number.
+ char SerialNumber[20];
+
+private:
+ void operator = (const SensorInfo&) { OVR_ASSERT(0); } // Assignment not allowed.
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** SensorDevice
+
+// SensorDevice is an interface to sensor data.
+// Install a MessageHandler of SensorDevice instance to receive MessageBodyFrame
+// notifications.
+//
+// TBD: Add Polling API? More HID interfaces?
+
+class SensorDevice : public HIDDeviceBase, public DeviceBase
+{
+public:
+ SensorDevice()
+ { }
+
+ // Static constant for this device type, used in template cast type checks.
+ enum { EnumDeviceType = Device_Sensor };
+
+ virtual DeviceType GetType() const { return Device_Sensor; }
+
+
+ // CoordinateFrame defines whether messages come in the coordinate frame
+ // of the sensor device or HMD, which has a different internal sensor.
+ // Sensors obtained form the HMD will automatically use HMD coordinates.
+ enum CoordinateFrame
+ {
+ Coord_Sensor = 0,
+ Coord_HMD = 1
+ };
+
+ virtual void SetCoordinateFrame(CoordinateFrame coordframe) = 0;
+ virtual CoordinateFrame GetCoordinateFrame() const = 0;
+
+ // Sets report rate (in Hz) of MessageBodyFrame messages (delivered through MessageHandler::OnMessage call).
+ // Currently supported maximum rate is 1000Hz. If the rate is set to 500 or 333 Hz then OnMessage will be
+ // called twice or thrice at the same 'tick'.
+ // If the rate is < 333 then the OnMessage / MessageBodyFrame will be called three
+ // times for each 'tick': the first call will contain averaged values, the second
+ // and third calls will provide with most recent two recorded samples.
+ virtual void SetReportRate(unsigned rateHz) = 0;
+ // Returns currently set report rate, in Hz. If 0 - error occurred.
+ // Note, this value may be different from the one provided for SetReportRate. The return
+ // value will contain the actual rate.
+ virtual unsigned GetReportRate() const = 0;
+
+ // Sets maximum range settings for the sensor described by SensorRange.
+ // The function will fail if you try to pass values outside Maximum supported
+ // by the HW, as described by SensorInfo.
+ // Pass waitFlag == true to wait for command completion. For waitFlag == true,
+ // returns true if the range was applied successfully (no HW error).
+ // For waitFlag = false, return 'true' means that command was enqueued successfully.
+ virtual bool SetRange(const SensorRange& range, bool waitFlag = false) = 0;
+
+ // Return the current sensor range settings for the device. These may not exactly
+ // match the values applied through SetRange.
+ virtual void GetRange(SensorRange* range) const = 0;
+};
+
+//-------------------------------------------------------------------------------------
+// ***** LatencyTestConfiguration
+// LatencyTestConfiguration specifies configuration information for the Oculus Latency Tester device.
+struct LatencyTestConfiguration
+{
+ LatencyTestConfiguration(const Color& threshold, bool sendSamples = false)
+ : Threshold(threshold), SendSamples(sendSamples)
+ {
+ }
+
+ // The color threshold for triggering a detected display change.
+ Color Threshold;
+ // Flag specifying whether we wish to receive a stream of color values from the sensor.
+ bool SendSamples;
+};
+
+//-------------------------------------------------------------------------------------
+// ***** LatencyTestCalibrate
+// LatencyTestCalibrate specifies colors used for Latency Tester calibration.
+struct LatencyTestCalibrate
+{
+ LatencyTestCalibrate(const Color& value)
+ : Value(value)
+ {
+ }
+
+ // The color being calibrated to.
+ Color Value;
+};
+
+//-------------------------------------------------------------------------------------
+// ***** LatencyTestStartTest
+// LatencyTestStartTest specifies values used when starting the Latency Tester test.
+struct LatencyTestStartTest
+{
+ LatencyTestStartTest(const Color& targetValue)
+ : TargetValue(targetValue)
+ {
+ }
+
+ // The color value that the display is being set to.
+ Color TargetValue;
+};
+
+//-------------------------------------------------------------------------------------
+// ***** LatencyTestDisplay
+// LatencyTestDisplay sets the mode and contents of the Latency Tester LED display.
+// See the 'Latency Tester Specification' document for more details.
+struct LatencyTestDisplay
+{
+ LatencyTestDisplay(UByte mode, UInt32 value)
+ : Mode(mode), Value(value)
+ {
+ }
+
+ UByte Mode; // The display mode that we wish to select.
+ UInt32 Value; // The value to display.
+};
+
+//-------------------------------------------------------------------------------------
+// ***** LatencyTestDevice
+
+// LatencyTestDevice provides an interface to the Oculus Latency Tester which is used to test 'motion to photon' latency.
+class LatencyTestDevice : public HIDDeviceBase, public DeviceBase
+{
+public:
+ LatencyTestDevice()
+ { }
+
+ // Static constant for this device type, used in template cast type checks.
+ enum { EnumDeviceType = Device_LatencyTester };
+
+ virtual DeviceType GetType() const { return Device_LatencyTester; }
+
+ // Specifies configuration information including the threshold for triggering a detected color change,
+ // and a flag to enable a stream of sensor values (typically used for debugging).
+ virtual bool SetConfiguration(const LatencyTestConfiguration& configuration, bool waitFlag = false) = 0;
+
+ // Get configuration information from device.
+ virtual bool GetConfiguration(LatencyTestConfiguration* configuration) = 0;
+
+ // Used to calibrate the latency tester at the start of a test. Calibration information is lost
+ // when power is removed from the device.
+ virtual bool SetCalibrate(const LatencyTestCalibrate& calibrate, bool waitFlag = false) = 0;
+
+ // Get calibration information from device.
+ virtual bool GetCalibrate(LatencyTestCalibrate* calibrate) = 0;
+
+ // Triggers the start of a measurement. This starts the millisecond timer on the device and
+ // causes it to respond with the 'MessageLatencyTestStarted' message.
+ virtual bool SetStartTest(const LatencyTestStartTest& start, bool waitFlag = false) = 0;
+
+ // Used to set the value displayed on the LED display panel.
+ virtual bool SetDisplay(const LatencyTestDisplay& display, bool waitFlag = false) = 0;
+
+ virtual DeviceBase* GetDevice() { return this; }
+};
+
+} // namespace OVR
+
+#endif
diff --git a/LibOVR/Src/OVR_DeviceConstants.h b/LibOVR/Src/OVR_DeviceConstants.h
new file mode 100644
index 0000000..b729d5f
--- /dev/null
+++ b/LibOVR/Src/OVR_DeviceConstants.h
@@ -0,0 +1,38 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_DeviceConstants.h
+Content : Device constants
+Created : February 5, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_DeviceConstants_h
+#define OVR_DeviceConstants_h
+
+namespace OVR {
+
+
+//-------------------------------------------------------------------------------------
+// Different device types supported by OVR; this type is reported by DeviceBase::GetType.
+//
+enum DeviceType
+{
+ Device_None = 0,
+ Device_Manager = 1,
+ Device_HMD = 2,
+ Device_Sensor = 3,
+ Device_LatencyTester = 4,
+ Device_All = 0xFF // Set for enumeration only, to enumerate all device types.
+};
+
+} // namespace OVR
+
+#endif
diff --git a/LibOVR/Src/OVR_DeviceHandle.cpp b/LibOVR/Src/OVR_DeviceHandle.cpp
new file mode 100644
index 0000000..5a0fce6
--- /dev/null
+++ b/LibOVR/Src/OVR_DeviceHandle.cpp
@@ -0,0 +1,174 @@
+/************************************************************************************
+
+Filename : OVR_DeviceHandle.cpp
+Content : Implementation of device handle class
+Created : February 5, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#include "OVR_DeviceHandle.h"
+
+#include "OVR_DeviceImpl.h"
+
+namespace OVR {
+
+//-------------------------------------------------------------------------------------
+// ***** DeviceHandle
+
+DeviceHandle::DeviceHandle(DeviceCreateDesc* impl) : pImpl(impl)
+{
+ if (pImpl)
+ pImpl->AddRef();
+}
+
+DeviceHandle::DeviceHandle(const DeviceHandle& src) : pImpl(src.pImpl)
+{
+ if (pImpl)
+ pImpl->AddRef();
+}
+
+DeviceHandle::~DeviceHandle()
+{
+ if (pImpl)
+ pImpl->Release();
+}
+
+void DeviceHandle::operator = (const DeviceHandle& src)
+{
+ if (src.pImpl)
+ src.pImpl->AddRef();
+ if (pImpl)
+ pImpl->Release();
+ pImpl = src.pImpl;
+}
+
+DeviceBase* DeviceHandle::GetDevice_AddRef() const
+{
+ if (pImpl && pImpl->pDevice)
+ {
+ pImpl->pDevice->AddRef();
+ return pImpl->pDevice;
+ }
+ return NULL;
+}
+
+// Returns true, if the handle contains the same device ptr
+// as specified in the parameter.
+bool DeviceHandle::IsDevice(DeviceBase* pdev) const
+{
+ return (pdev && pImpl && pImpl->pDevice) ?
+ pImpl->pDevice == pdev : false;
+}
+
+DeviceType DeviceHandle::GetType() const
+{
+ return pImpl ? pImpl->Type : Device_None;
+}
+
+bool DeviceHandle::GetDeviceInfo(DeviceInfo* info) const
+{
+ return pImpl ? pImpl->GetDeviceInfo(info) : false;
+}
+bool DeviceHandle::IsAvailable() const
+{
+ // This isn't "atomically safe", but the function only returns the
+ // recent state that may change.
+ return pImpl ? (pImpl->Enumerated && pImpl->pLock->pManager) : false;
+}
+
+bool DeviceHandle::IsCreated() const
+{
+ return pImpl ? (pImpl->pDevice != 0) : false;
+}
+
+DeviceBase* DeviceHandle::CreateDevice()
+{
+ if (!pImpl)
+ return 0;
+
+ DeviceBase* device = 0;
+ Ptr<DeviceManagerImpl> manager= 0;
+
+ // Since both manager and device pointers can only be destroyed during a lock,
+ // hold it while checking for availability.
+ // AddRef to manager so that it doesn't get released on us.
+ {
+ Lock::Locker deviceLockScope(pImpl->GetLock());
+
+ if (pImpl->pDevice)
+ {
+ pImpl->pDevice->AddRef();
+ return pImpl->pDevice;
+ }
+ manager = pImpl->GetManagerImpl();
+ }
+
+ if (manager)
+ {
+ if (manager->GetThreadId() != OVR::GetCurrentThreadId())
+ {
+ // Queue up a CreateDevice request. This fills in '&device' with AddRefed value,
+ // or keep it at null.
+ manager->GetThreadQueue()->PushCallAndWaitResult(
+ manager.GetPtr(), &DeviceManagerImpl::CreateDevice_MgrThread,
+ &device, pImpl, (DeviceBase*)0);
+ }
+ else
+ device = manager->CreateDevice_MgrThread(pImpl, (DeviceBase*)0);
+ }
+ return device;
+}
+
+void DeviceHandle::Clear()
+{
+ if (pImpl)
+ {
+ pImpl->Release();
+ pImpl = 0;
+ }
+}
+
+bool DeviceHandle::enumerateNext(const DeviceEnumerationArgs& args)
+{
+ if (GetType() == Device_None)
+ return false;
+
+ Ptr<DeviceManagerImpl> managerKeepAlive;
+ Lock::Locker lockScope(pImpl->GetLock());
+
+ DeviceCreateDesc* next = pImpl;
+ // If manager was destroyed, we get removed from the list.
+ if (!pImpl->pNext)
+ return false;
+
+ managerKeepAlive = next->GetManagerImpl();
+ OVR_ASSERT(managerKeepAlive);
+
+ do {
+ next = next->pNext;
+
+ if (managerKeepAlive->Devices.IsNull(next))
+ {
+ pImpl->Release();
+ pImpl = 0;
+ return false;
+ }
+
+ } while(!args.MatchRule(next->Type, next->Enumerated));
+
+ next->AddRef();
+ pImpl->Release();
+ pImpl = next;
+
+ return true;
+}
+
+} // namespace OVR
+
diff --git a/LibOVR/Src/OVR_DeviceHandle.h b/LibOVR/Src/OVR_DeviceHandle.h
new file mode 100644
index 0000000..4ac4387
--- /dev/null
+++ b/LibOVR/Src/OVR_DeviceHandle.h
@@ -0,0 +1,97 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_DeviceHandle.h
+Content : Handle to a device that was enumerated
+Created : February 5, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_DeviceHandle_h
+#define OVR_DeviceHandle_h
+
+#include "OVR_DeviceConstants.h"
+
+namespace OVR {
+
+class DeviceBase;
+class DeviceInfo;
+
+// Internal
+class DeviceCreateDesc;
+class DeviceEnumerationArgs;
+
+
+//-------------------------------------------------------------------------------------
+// ***** DeviceHandle
+
+// DeviceHandle references a specific device that was enumerated; it can be assigned
+// directly from DeviceEnumerator.
+//
+// Devices represented by DeviceHandle are not necessarily created or available.
+// A device may become unavailable if, for example, it its unplugged. If the device
+// is available, it can be created by calling CreateDevice.
+//
+
+class DeviceHandle
+{
+ friend class DeviceManager;
+ friend class DeviceManagerImpl;
+ template<class B> friend class HIDDeviceImpl;
+
+public:
+ DeviceHandle() : pImpl(0) { }
+ DeviceHandle(const DeviceHandle& src);
+ ~DeviceHandle();
+
+ void operator = (const DeviceHandle& src);
+
+ bool operator == (const DeviceHandle& other) const { return pImpl == other.pImpl; }
+ bool operator != (const DeviceHandle& other) const { return pImpl != other.pImpl; }
+
+ // operator bool() returns true if Handle/Enumerator points to a valid device.
+ operator bool () const { return GetType() != Device_None; }
+
+ // Returns existing device, or NULL if !IsCreated. The returned ptr is
+ // addref-ed.
+ DeviceBase* GetDevice_AddRef() const;
+ DeviceType GetType() const;
+ bool GetDeviceInfo(DeviceInfo* info) const;
+ bool IsAvailable() const;
+ bool IsCreated() const;
+ // Returns true, if the handle contains the same device ptr
+ // as specified in the parameter.
+ bool IsDevice(DeviceBase*) const;
+
+ // Creates a device, or returns AddRefed pointer if one is already created.
+ // New devices start out with RefCount of 1.
+ DeviceBase* CreateDevice();
+
+ // Creates a device, or returns AddRefed pointer if one is already created.
+ // New devices start out with RefCount of 1. DeviceT is used to cast the
+ // DeviceBase* to a concreete type.
+ template <class DeviceT>
+ DeviceT* CreateDeviceTyped() const
+ {
+ return static_cast<DeviceT*>(DeviceHandle(*this).CreateDevice());
+ }
+
+ // Resets the device handle to uninitialized state.
+ void Clear();
+
+protected:
+ explicit DeviceHandle(DeviceCreateDesc* impl);
+ bool enumerateNext(const DeviceEnumerationArgs& args);
+ DeviceCreateDesc* pImpl;
+};
+
+} // namespace OVR
+
+#endif
diff --git a/LibOVR/Src/OVR_DeviceImpl.cpp b/LibOVR/Src/OVR_DeviceImpl.cpp
new file mode 100644
index 0000000..97cc2d4
--- /dev/null
+++ b/LibOVR/Src/OVR_DeviceImpl.cpp
@@ -0,0 +1,784 @@
+/************************************************************************************
+
+Filename : OVR_DeviceImpl.h
+Content : Partial back-end independent implementation of Device interfaces
+Created : October 10, 2012
+Authors : Michael Antonov
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#include "OVR_DeviceImpl.h"
+#include "Kernel/OVR_Atomic.h"
+#include "Kernel/OVR_Log.h"
+#include "Kernel/OVR_System.h"
+
+#include "OVR_DeviceImpl.h"
+#include "OVR_SensorImpl.h"
+
+namespace OVR {
+
+
+//-------------------------------------------------------------------------------------
+// ***** SharedLock
+
+// This is a general purpose globally shared Lock implementation that should probably be
+// moved to Kernel.
+// May in theory busy spin-wait if we hit contention on first lock creation,
+// but this shouldn't matter in practice since Lock* should be cached.
+
+
+enum { LockInitMarker = 0xFFFFFFFF };
+
+Lock* SharedLock::GetLockAddRef()
+{
+ int oldUseCount;
+
+ do {
+ oldUseCount = UseCount;
+ if (oldUseCount == LockInitMarker)
+ continue;
+
+ if (oldUseCount == 0)
+ {
+ // Initialize marker
+ if (AtomicOps<int>::CompareAndSet_Sync(&UseCount, 0, LockInitMarker))
+ {
+ Construct<Lock>(Buffer);
+ do { }
+ while (!AtomicOps<int>::CompareAndSet_Sync(&UseCount, LockInitMarker, 1));
+ return toLock();
+ }
+ continue;
+ }
+
+ } while (!AtomicOps<int>::CompareAndSet_NoSync(&UseCount, oldUseCount, oldUseCount + 1));
+
+ return toLock();
+}
+
+void SharedLock::ReleaseLock(Lock* plock)
+{
+ OVR_UNUSED(plock);
+ OVR_ASSERT((plock = toLock()) != 0);
+
+ int oldUseCount;
+
+ do {
+ oldUseCount = UseCount;
+ OVR_ASSERT(oldUseCount != LockInitMarker);
+
+ if (oldUseCount == 1)
+ {
+ // Initialize marker
+ if (AtomicOps<int>::CompareAndSet_Sync(&UseCount, 1, LockInitMarker))
+ {
+ Destruct<Lock>(toLock());
+
+ do { }
+ while (!AtomicOps<int>::CompareAndSet_Sync(&UseCount, LockInitMarker, 0));
+
+ return;
+ }
+ continue;
+ }
+
+ } while (!AtomicOps<int>::CompareAndSet_NoSync(&UseCount, oldUseCount, oldUseCount - 1));
+}
+
+
+
+//-------------------------------------------------------------------------------------
+// ***** MessageHandler
+
+// Threading notes:
+// The OnMessage() handler and SetMessageHandler are currently synchronized
+// through a separately stored shared Lock object to avoid calling the handler
+// from background thread while it's being removed.
+
+static SharedLock MessageHandlerSharedLock;
+
+
+class MessageHandlerImpl
+{
+public:
+ MessageHandlerImpl()
+ : pLock(MessageHandlerSharedLock.GetLockAddRef())
+ {
+ }
+ ~MessageHandlerImpl()
+ {
+ MessageHandlerSharedLock.ReleaseLock(pLock);
+ pLock = 0;
+ }
+
+ static MessageHandlerImpl* FromHandler(MessageHandler* handler)
+ { return (MessageHandlerImpl*)&handler->Internal; }
+ static const MessageHandlerImpl* FromHandler(const MessageHandler* handler)
+ { return (const MessageHandlerImpl*)&handler->Internal; }
+
+ // This lock is held while calling a handler and when we are applied/
+ // removed from a device.
+ Lock* pLock;
+ // List of device we are applied to.
+ List<MessageHandlerRef> UseList;
+};
+
+
+MessageHandlerRef::MessageHandlerRef(DeviceBase* device)
+ : pLock(MessageHandlerSharedLock.GetLockAddRef()), pDevice(device), pHandler(0)
+{
+}
+
+MessageHandlerRef::~MessageHandlerRef()
+{
+ {
+ Lock::Locker lockScope(pLock);
+ if (pHandler)
+ {
+ pHandler = 0;
+ RemoveNode();
+ }
+ }
+ MessageHandlerSharedLock.ReleaseLock(pLock);
+ pLock = 0;
+}
+
+void MessageHandlerRef::SetHandler(MessageHandler* handler)
+{
+ OVR_ASSERT(!handler ||
+ MessageHandlerImpl::FromHandler(handler)->pLock == pLock);
+ Lock::Locker lockScope(pLock);
+ SetHandler_NTS(handler);
+}
+
+void MessageHandlerRef::SetHandler_NTS(MessageHandler* handler)
+{
+ if (pHandler != handler)
+ {
+ if (pHandler)
+ RemoveNode();
+ pHandler = handler;
+
+ if (handler)
+ {
+ MessageHandlerImpl* handlerImpl = MessageHandlerImpl::FromHandler(handler);
+ handlerImpl->UseList.PushBack(this);
+ }
+ // TBD: Call notifier on device?
+ }
+}
+
+
+MessageHandler::MessageHandler()
+{
+ OVR_COMPILER_ASSERT(sizeof(Internal) > sizeof(MessageHandlerImpl));
+ Construct<MessageHandlerImpl>(Internal);
+}
+
+MessageHandler::~MessageHandler()
+{
+ MessageHandlerImpl* handlerImpl = MessageHandlerImpl::FromHandler(this);
+ {
+ Lock::Locker lockedScope(handlerImpl->pLock);
+ OVR_ASSERT_LOG(handlerImpl->UseList.IsEmpty(),
+ ("~MessageHandler %p - Handler still active; call RemoveHandlerFromDevices", this));
+ }
+
+ Destruct<MessageHandlerImpl>(handlerImpl);
+}
+
+bool MessageHandler::IsHandlerInstalled() const
+{
+ const MessageHandlerImpl* handlerImpl = MessageHandlerImpl::FromHandler(this);
+ Lock::Locker lockedScope(handlerImpl->pLock);
+ return handlerImpl->UseList.IsEmpty() != true;
+}
+
+
+void MessageHandler::RemoveHandlerFromDevices()
+{
+ MessageHandlerImpl* handlerImpl = MessageHandlerImpl::FromHandler(this);
+ Lock::Locker lockedScope(handlerImpl->pLock);
+
+ while(!handlerImpl->UseList.IsEmpty())
+ {
+ MessageHandlerRef* use = handlerImpl->UseList.GetFirst();
+ use->SetHandler_NTS(0);
+ }
+}
+
+Lock* MessageHandler::GetHandlerLock() const
+{
+ const MessageHandlerImpl* handlerImpl = MessageHandlerImpl::FromHandler(this);
+ return handlerImpl->pLock;
+}
+
+
+//-------------------------------------------------------------------------------------
+// ***** DeviceBase
+
+
+// Delegate relevant implementation to DeviceRectord to avoid re-implementation in
+// every derived Device.
+void DeviceBase::AddRef()
+{
+ getDeviceCommon()->DeviceAddRef();
+}
+void DeviceBase::Release()
+{
+ getDeviceCommon()->DeviceRelease();
+}
+DeviceBase* DeviceBase::GetParent() const
+{
+ return getDeviceCommon()->pParent.GetPtr();
+}
+DeviceManager* DeviceBase::GetManager() const
+{
+ return getDeviceCommon()->pCreateDesc->GetManagerImpl();
+}
+
+void DeviceBase::SetMessageHandler(MessageHandler* handler)
+{
+ getDeviceCommon()->HandlerRef.SetHandler(handler);
+}
+MessageHandler* DeviceBase::GetMessageHandler() const
+{
+ return getDeviceCommon()->HandlerRef.GetHandler();
+}
+
+DeviceType DeviceBase::GetType() const
+{
+ return getDeviceCommon()->pCreateDesc->Type;
+}
+
+bool DeviceBase::GetDeviceInfo(DeviceInfo* info) const
+{
+ return getDeviceCommon()->pCreateDesc->GetDeviceInfo(info);
+ //info->Name[0] = 0;
+ //return false;
+}
+
+// returns the MessageHandler's lock
+Lock* DeviceBase::GetHandlerLock() const
+{
+ return getDeviceCommon()->HandlerRef.GetLock();
+}
+
+// Derive DeviceManagerCreateDesc to provide abstract function implementation.
+class DeviceManagerCreateDesc : public DeviceCreateDesc
+{
+public:
+ DeviceManagerCreateDesc(DeviceFactory* factory)
+ : DeviceCreateDesc(factory, Device_Manager) { }
+
+ // We don't need there on Manager since it isn't assigned to DeviceHandle.
+ virtual DeviceCreateDesc* Clone() const { return 0; }
+ virtual MatchResult MatchDevice(const DeviceCreateDesc&,
+ DeviceCreateDesc**) const { return Match_None; }
+ virtual DeviceBase* NewDeviceInstance() { return 0; }
+ virtual bool GetDeviceInfo(DeviceInfo*) const { return false; }
+};
+
+//-------------------------------------------------------------------------------------
+// ***** DeviceManagerImpl
+
+DeviceManagerImpl::DeviceManagerImpl()
+ : DeviceImpl<OVR::DeviceManager>(CreateManagerDesc(), 0)
+ //,DeviceCreateDescList(pCreateDesc ? pCreateDesc->pLock : 0)
+{
+ if (pCreateDesc)
+ {
+ pCreateDesc->pLock->pManager = this;
+ }
+}
+
+DeviceManagerImpl::~DeviceManagerImpl()
+{
+ // Shutdown must've been called.
+ OVR_ASSERT(!pCreateDesc->pDevice);
+
+ // Remove all factories
+ while(!Factories.IsEmpty())
+ {
+ DeviceFactory* factory = Factories.GetFirst();
+ factory->RemovedFromManager();
+ factory->RemoveNode();
+ }
+}
+
+DeviceCreateDesc* DeviceManagerImpl::CreateManagerDesc()
+{
+ DeviceCreateDesc* managerDesc = new DeviceManagerCreateDesc(0);
+ if (managerDesc)
+ {
+ managerDesc->pLock = *new DeviceManagerLock;
+ }
+ return managerDesc;
+}
+
+bool DeviceManagerImpl::Initialize(DeviceBase* parent)
+{
+ OVR_UNUSED(parent);
+ if (!pCreateDesc || !pCreateDesc->pLock)
+ return false;
+ return true;
+}
+
+void DeviceManagerImpl::Shutdown()
+{
+ // Remove all device descriptors from list while the lock is held.
+ // Some descriptors may survive longer due to handles.
+ while(!Devices.IsEmpty())
+ {
+ DeviceCreateDesc* devDesc = Devices.GetFirst();
+ OVR_ASSERT(!devDesc->pDevice); // Manager shouldn't be dying while Device exists.
+ devDesc->Enumerated = false;
+ devDesc->RemoveNode();
+ devDesc->pNext = devDesc->pPrev = 0;
+
+ if (devDesc->HandleCount == 0)
+ {
+ delete devDesc;
+ }
+ }
+ Devices.Clear();
+
+ // These must've been cleared by caller.
+ OVR_ASSERT(pCreateDesc->pDevice == 0);
+ OVR_ASSERT(pCreateDesc->pLock->pManager == 0);
+}
+
+
+// Callbacks for DeviceCreation/Release
+DeviceBase* DeviceManagerImpl::CreateDevice_MgrThread(DeviceCreateDesc* createDesc, DeviceBase* parent)
+{
+ // Calls to DeviceManagerImpl::CreateDevice are enqueued with wait while holding pManager,
+ // so 'this' must remain valid.
+ OVR_ASSERT(createDesc->pLock->pManager);
+
+ Lock::Locker devicesLock(GetLock());
+
+ // If device already exists, just AddRef to it.
+ if (createDesc->pDevice)
+ {
+ createDesc->pDevice->AddRef();
+ return createDesc->pDevice;
+ }
+
+ if (!parent)
+ parent = this;
+
+ DeviceBase* device = createDesc->NewDeviceInstance();
+
+ if (device)
+ {
+ if (device->getDeviceCommon()->Initialize(parent))
+ {
+ createDesc->pDevice = device;
+ }
+ else
+ {
+ // Don't go through Release() to avoid PushCall behaviour,
+ // as it is not needed here.
+ delete device;
+ device = 0;
+ }
+ }
+
+ return device;
+}
+
+Void DeviceManagerImpl::ReleaseDevice_MgrThread(DeviceBase* device)
+{
+ // descKeepAlive will keep ManagerLock object alive as well,
+ // allowing us to exit gracefully.
+ Ptr<DeviceCreateDesc> descKeepAlive;
+ Lock::Locker devicesLock(GetLock());
+ DeviceCommon* devCommon = device->getDeviceCommon();
+
+ while(1)
+ {
+ UInt32 refCount = devCommon->RefCount;
+
+ if (refCount > 1)
+ {
+ if (devCommon->RefCount.CompareAndSet_NoSync(refCount, refCount-1))
+ {
+ // We decreented from initial count higher then 1;
+ // nothing else to do.
+ return 0;
+ }
+ }
+ else if (devCommon->RefCount.CompareAndSet_NoSync(1, 0))
+ {
+ // { 1 -> 0 } decrement succeded. Destroy this device.
+ break;
+ }
+ }
+
+ // At this point, may be releasing the device manager itself.
+ // This does not matter, however, since shutdown logic is the same
+ // in both cases. DeviceManager::Shutdown with begin shutdown process for
+ // the internal manager thread, which will eventually destroy itself.
+ // TBD: Clean thread shutdown.
+ descKeepAlive = devCommon->pCreateDesc;
+ descKeepAlive->pDevice = 0;
+ devCommon->Shutdown();
+ delete device;
+ return 0;
+}
+
+
+
+Void DeviceManagerImpl::EnumerateAllFactoryDevices()
+{
+ // 1. Mark matching devices as NOT enumerated.
+ // 2. Call factory to enumerate all HW devices, adding any device that
+ // was not matched.
+ // 3. Remove non-matching devices.
+
+ Lock::Locker deviceLock(GetLock());
+
+ DeviceCreateDesc* devDesc, *nextdevDesc;
+
+ // 1.
+ for(devDesc = Devices.GetFirst();
+ !Devices.IsNull(devDesc); devDesc = devDesc->pNext)
+ {
+ //if (devDesc->pFactory == factory)
+ devDesc->Enumerated = false;
+ }
+
+ // 2.
+ DeviceFactory* factory = Factories.GetFirst();
+ while(!Factories.IsNull(factory))
+ {
+ EnumerateFactoryDevices(factory);
+ factory = factory->pNext;
+ }
+
+
+ // 3.
+ for(devDesc = Devices.GetFirst();
+ !Devices.IsNull(devDesc); devDesc = nextdevDesc)
+ {
+ // In case 'devDesc' gets removed.
+ nextdevDesc = devDesc->pNext;
+
+ // Note, device might be not enumerated since it is opened and
+ // in use! Do NOT notify 'device removed' in this case (!AB)
+ if (!devDesc->Enumerated)
+ {
+ // This deletes the devDesc for HandleCount == 0 due to Release in DeviceHandle.
+ CallOnDeviceRemoved(devDesc);
+
+ /*
+ if (devDesc->HandleCount == 0)
+ {
+ // Device must be dead if it ever existed, since it AddRefs to us.
+ // ~DeviceCreateDesc removes its node from list.
+ OVR_ASSERT(!devDesc->pDevice);
+ delete devDesc;
+ }
+ */
+ }
+ }
+
+ return 0;
+}
+
+Ptr<DeviceCreateDesc> DeviceManagerImpl::AddDevice_NeedsLock(
+ const DeviceCreateDesc& createDesc)
+{
+ // If found, mark as enumerated and we are done.
+ DeviceCreateDesc* descCandidate = 0;
+
+ for(DeviceCreateDesc* devDesc = Devices.GetFirst();
+ !Devices.IsNull(devDesc); devDesc = devDesc->pNext)
+ {
+ DeviceCreateDesc::MatchResult mr = devDesc->MatchDevice(createDesc, &descCandidate);
+ if (mr == DeviceCreateDesc::Match_Found)
+ {
+ devDesc->Enumerated = true;
+ if (!devDesc->pDevice)
+ CallOnDeviceAdded(devDesc);
+ return devDesc;
+ }
+ }
+
+ // Update candidate (this may involve writing fields to HMDDevice createDesc).
+ if (descCandidate)
+ {
+ bool newDevice = false;
+ if (descCandidate->UpdateMatchedCandidate(createDesc, &newDevice))
+ {
+ descCandidate->Enumerated = true;
+ if (!descCandidate->pDevice || newDevice)
+ CallOnDeviceAdded(descCandidate);
+ return descCandidate;
+ }
+ }
+
+ // If not found, add new device.
+ // - This stores a new descriptor with
+ // {pDevice = 0, HandleCount = 1, Enumerated = true}
+ DeviceCreateDesc* desc = createDesc.Clone();
+ desc->pLock = pCreateDesc->pLock;
+ Devices.PushBack(desc);
+ desc->Enumerated = true;
+
+ CallOnDeviceAdded(desc);
+
+ return desc;
+}
+
+Ptr<DeviceCreateDesc> DeviceManagerImpl::FindDevice(
+ const String& path,
+ DeviceType deviceType)
+{
+ Lock::Locker deviceLock(GetLock());
+ DeviceCreateDesc* devDesc;
+
+ for (devDesc = Devices.GetFirst();
+ !Devices.IsNull(devDesc); devDesc = devDesc->pNext)
+ {
+ if ((deviceType == Device_None || deviceType == devDesc->Type) &&
+ devDesc->MatchDevice(path))
+ return devDesc;
+ }
+ return NULL;
+}
+
+Ptr<DeviceCreateDesc> DeviceManagerImpl::FindHIDDevice(const HIDDeviceDesc& hidDevDesc)
+{
+ Lock::Locker deviceLock(GetLock());
+ DeviceCreateDesc* devDesc;
+
+ for (devDesc = Devices.GetFirst();
+ !Devices.IsNull(devDesc); devDesc = devDesc->pNext)
+ {
+ if (devDesc->MatchHIDDevice(hidDevDesc))
+ return devDesc;
+ }
+ return NULL;
+}
+
+void DeviceManagerImpl::DetectHIDDevice(const HIDDeviceDesc& hidDevDesc)
+{
+ Lock::Locker deviceLock(GetLock());
+ DeviceFactory* factory = Factories.GetFirst();
+ while(!Factories.IsNull(factory))
+ {
+ if (factory->DetectHIDDevice(this, hidDevDesc))
+ break;
+ factory = factory->pNext;
+ }
+
+}
+
+// Enumerates devices for a particular factory.
+Void DeviceManagerImpl::EnumerateFactoryDevices(DeviceFactory* factory)
+{
+
+ class FactoryEnumerateVisitor : public DeviceFactory::EnumerateVisitor
+ {
+ DeviceManagerImpl* pManager;
+ DeviceFactory* pFactory;
+ public:
+ FactoryEnumerateVisitor(DeviceManagerImpl* manager, DeviceFactory* factory)
+ : pManager(manager), pFactory(factory) { }
+
+ virtual void Visit(const DeviceCreateDesc& createDesc)
+ {
+ pManager->AddDevice_NeedsLock(createDesc);
+ }
+ };
+
+ FactoryEnumerateVisitor newDeviceVisitor(this, factory);
+ factory->EnumerateDevices(newDeviceVisitor);
+
+
+ return 0;
+}
+
+
+DeviceEnumerator<> DeviceManagerImpl::EnumerateDevicesEx(const DeviceEnumerationArgs& args)
+{
+ Lock::Locker deviceLock(GetLock());
+
+ if (Devices.IsEmpty())
+ return DeviceEnumerator<>();
+
+ DeviceCreateDesc* firstDeviceDesc = Devices.GetFirst();
+ DeviceEnumerator<> e = enumeratorFromHandle(DeviceHandle(firstDeviceDesc), args);
+
+ if (!args.MatchRule(firstDeviceDesc->Type, firstDeviceDesc->Enumerated))
+ {
+ e.Next();
+ }
+
+ return e;
+}
+
+//-------------------------------------------------------------------------------------
+// ***** DeviceCommon
+
+void DeviceCommon::DeviceAddRef()
+{
+ RefCount++;
+}
+
+void DeviceCommon::DeviceRelease()
+{
+ while(1)
+ {
+ UInt32 refCount = RefCount;
+ OVR_ASSERT(refCount > 0);
+
+ if (refCount == 1)
+ {
+ DeviceManagerImpl* manager = pCreateDesc->GetManagerImpl();
+ ThreadCommandQueue* queue = manager->GetThreadQueue();
+
+ // Enqueue ReleaseDevice for {1 -> 0} transition with no wait.
+ // We pass our reference ownership into the queue to destroy.
+ // It's in theory possible for another thread to re-steal our device reference,
+ // but that is checked for atomically in DeviceManagerImpl::ReleaseDevice.
+ if (!queue->PushCall(manager, &DeviceManagerImpl::ReleaseDevice_MgrThread,
+ pCreateDesc->pDevice))
+ {
+ // PushCall shouldn't fail because background thread runs while manager is
+ // alive and we are holding Manager alive through pParent chain.
+ OVR_ASSERT(false);
+ }
+
+ // Warning! At his point everything, including manager, may be dead.
+ break;
+ }
+ else if (RefCount.CompareAndSet_NoSync(refCount, refCount-1))
+ {
+ break;
+ }
+ }
+}
+
+
+
+//-------------------------------------------------------------------------------------
+// ***** DeviceCreateDesc
+
+
+void DeviceCreateDesc::AddRef()
+{
+ // Technically, HandleCount { 0 -> 1 } transition can only happen during Lock,
+ // but we leave this to caller to worry about (happens during enumeration).
+ HandleCount++;
+}
+
+void DeviceCreateDesc::Release()
+{
+ while(1)
+ {
+ UInt32 handleCount = HandleCount;
+ // HandleCount must obviously be >= 1, since we are releasing it.
+ OVR_ASSERT(handleCount > 0);
+
+ // {1 -> 0} transition may cause us to be destroyed, so require a lock.
+ if (handleCount == 1)
+ {
+ Ptr<DeviceManagerLock> lockKeepAlive;
+ Lock::Locker deviceLockScope(GetLock());
+
+ if (!HandleCount.CompareAndSet_NoSync(handleCount, 0))
+ continue;
+
+ OVR_ASSERT(pDevice == 0);
+
+ // Destroy *this if the manager was destroyed already, or Enumerated
+ // is false (device no longer available).
+ if (!GetManagerImpl() || !Enumerated)
+ {
+ lockKeepAlive = pLock;
+
+ // Remove from manager list (only matters for !Enumerated).
+ if (pNext)
+ {
+ RemoveNode();
+ pNext = pPrev = 0;
+ }
+
+ delete this;
+ }
+
+ // Available DeviceCreateDesc may survive with { HandleCount == 0 },
+ // in case it might be enumerated again later.
+ break;
+ }
+ else if (HandleCount.CompareAndSet_NoSync(handleCount, handleCount-1))
+ {
+ break;
+ }
+ }
+}
+
+HMDDevice* HMDDevice::Disconnect(SensorDevice* psensor)
+{
+ if (!psensor)
+ return NULL;
+
+ OVR::DeviceManager* manager = GetManager();
+ if (manager)
+ {
+ //DeviceManagerImpl* mgrImpl = static_cast<DeviceManagerImpl*>(manager);
+ Ptr<DeviceCreateDesc> desc = getDeviceCommon()->pCreateDesc;
+ if (desc)
+ {
+ class Visitor : public DeviceFactory::EnumerateVisitor
+ {
+ Ptr<DeviceCreateDesc> Desc;
+ public:
+ Visitor(DeviceCreateDesc* desc) : Desc(desc) {}
+ virtual void Visit(const DeviceCreateDesc& createDesc)
+ {
+ Lock::Locker lock(Desc->GetLock());
+ Desc->UpdateMatchedCandidate(createDesc);
+ }
+ } visitor(desc);
+ //SensorDeviceImpl* sImpl = static_cast<SensorDeviceImpl*>(psensor);
+
+ SensorDisplayInfoImpl displayInfo;
+
+ if (psensor->GetFeatureReport(displayInfo.Buffer, SensorDisplayInfoImpl::PacketSize))
+ {
+ displayInfo.Unpack();
+
+ // If we got display info, try to match / create HMDDevice as well
+ // so that sensor settings give preference.
+ if (displayInfo.DistortionType & SensorDisplayInfoImpl::Mask_BaseFmt)
+ {
+ SensorDeviceImpl::EnumerateHMDFromSensorDisplayInfo(displayInfo, visitor);
+ }
+ }
+ }
+ }
+ return this;
+}
+
+bool HMDDevice::IsDisconnected() const
+{
+ OVR::HMDInfo info;
+ GetDeviceInfo(&info);
+ // if strlen(info.DisplayDeviceName) == 0 then
+ // this HMD is 'fake' (created using sensor).
+ return (strlen(info.DisplayDeviceName) == 0);
+}
+
+
+} // namespace OVR
+
diff --git a/LibOVR/Src/OVR_DeviceImpl.h b/LibOVR/Src/OVR_DeviceImpl.h
new file mode 100644
index 0000000..8b7bf0a
--- /dev/null
+++ b/LibOVR/Src/OVR_DeviceImpl.h
@@ -0,0 +1,425 @@
+/************************************************************************************
+
+Filename : OVR_DeviceImpl.h
+Content : Partial back-end independent implementation of Device interfaces
+Created : October 10, 2012
+Authors : Michael Antonov
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_DeviceImpl_h
+#define OVR_DeviceImpl_h
+
+#include "OVR_Device.h"
+#include "Kernel/OVR_Atomic.h"
+#include "Kernel/OVR_Log.h"
+#include "Kernel/OVR_System.h"
+
+#include "Kernel/OVR_Threads.h"
+#include "OVR_ThreadCommandQueue.h"
+#include "OVR_HIDDevice.h"
+
+namespace OVR {
+
+class DeviceManagerImpl;
+class DeviceFactory;
+
+enum
+{
+ Oculus_VendorId = 0x2833
+};
+
+//-------------------------------------------------------------------------------------
+// Globally shared Lock implementation used for MessageHandlers.
+
+class SharedLock
+{
+public:
+
+ Lock* GetLockAddRef();
+ void ReleaseLock(Lock* plock);
+
+private:
+ Lock* toLock() { return (Lock*)Buffer; }
+
+ // UseCount and max alignment.
+ volatile int UseCount;
+ UInt64 Buffer[(sizeof(Lock)+sizeof(UInt64)-1)/sizeof(UInt64)];
+};
+
+
+// Wrapper for MessageHandler that includes synchronization logic.
+// References to MessageHandlers are organized in a list to allow for them to
+// easily removed with MessageHandler::RemoveAllHandlers.
+class MessageHandlerRef : public ListNode<MessageHandlerRef>
+{
+public:
+ MessageHandlerRef(DeviceBase* device);
+ ~MessageHandlerRef();
+
+ void SetHandler(MessageHandler* hander);
+
+ // Not-thread-safe version
+ void SetHandler_NTS(MessageHandler* hander);
+
+ void Call(const Message& msg)
+ {
+ Lock::Locker lockScope(pLock);
+ if (pHandler)
+ pHandler->OnMessage(msg);
+ }
+
+ Lock* GetLock() const { return pLock; }
+
+ // GetHandler() is not thread safe if used out of order across threads; nothing can be done
+ // about that.
+ MessageHandler* GetHandler() const { return pHandler; }
+ DeviceBase* GetDevice() const { return pDevice; }
+
+private:
+ Lock* pLock; // Cached global handler lock.
+ DeviceBase* pDevice;
+ MessageHandler* pHandler;
+};
+
+
+
+//-------------------------------------------------------------------------------------
+
+// DeviceManagerLock is a synchronization lock used by DeviceManager for Devices
+// and is allocated separately for potentially longer lifetime.
+//
+// DeviceManagerLock is used for all of the following:
+// - Adding/removing devices
+// - Reporting manager lifetime (pManager != 0) for DeviceHandles
+// - Protecting device creation/shutdown.
+
+class DeviceManagerLock : public RefCountBase<DeviceManagerLock>
+{
+public:
+ Lock CreateLock;
+ DeviceManagerImpl* pManager;
+
+ DeviceManagerLock() : pManager(0) { }
+};
+
+
+// DeviceCreateDesc provides all of the information needed to create any device, a derived
+// instance of this class is created by DeviceFactory during enumeration.
+// - DeviceCreateDesc may or may not be a part of DeviceManager::Devices list (check pNext != 0).
+// - Referenced and kept alive by DeviceHandle.
+
+class DeviceCreateDesc : public ListNode<DeviceCreateDesc>, public NewOverrideBase
+{
+ void operator = (const DeviceCreateDesc&) { } // Assign not supported; suppress MSVC warning.
+public:
+ DeviceCreateDesc(DeviceFactory* factory, DeviceType type)
+ : pFactory(factory), Type(type), pLock(0), HandleCount(0), pDevice(0), Enumerated(true)
+ {
+ pNext = pPrev = 0;
+ }
+
+ virtual ~DeviceCreateDesc()
+ {
+ OVR_ASSERT(!pDevice);
+ if (pNext)
+ RemoveNode();
+ }
+
+ DeviceManagerImpl* GetManagerImpl() { return pLock->pManager; }
+ Lock* GetLock() const { return &pLock->CreateLock; }
+
+ // DeviceCreateDesc reference counting is tied to Devices list management,
+ // see comments for HandleCount.
+ void AddRef();
+ void Release();
+
+
+ // *** Device creation/matching Interface
+
+
+ // Cloning copies us to an allocated object when new device is enumerated.
+ virtual DeviceCreateDesc* Clone() const = 0;
+ // Creates a new device instance without Initializing it; the
+ // later is done my Initialize()/Shutdown() methods of the device itself.
+ virtual DeviceBase* NewDeviceInstance() = 0;
+ // Override to return device-specific info.
+ virtual bool GetDeviceInfo(DeviceInfo* info) const = 0;
+
+
+ enum MatchResult
+ {
+ Match_None,
+ Match_Found,
+ Match_Candidate
+ };
+
+ // Override to return Match_Found if descriptor matches our device.
+ // Match_Candidate can be returned, with pcandicate update, if this may be a match
+ // but more searching is necessary. If this is the case UpdateMatchedCandidate will be called.
+ virtual MatchResult MatchDevice(const DeviceCreateDesc& other,
+ DeviceCreateDesc** pcandidate) const = 0;
+
+ // Called for matched candidate after all potential matches are iterated.
+ // Used to update HMDevice creation arguments from Sensor.
+ // Optional return param 'newDeviceFlag' will be set to true if the
+ // 'desc' refers to a new device; false, otherwise.
+ // Return 'false' to create new object, 'true' if done with this argument.
+ virtual bool UpdateMatchedCandidate(
+ const DeviceCreateDesc& desc, bool* newDeviceFlag = NULL)
+ { OVR_UNUSED2(desc, newDeviceFlag); return false; }
+
+ // Matches HID device to the descriptor.
+ virtual bool MatchHIDDevice(const HIDDeviceDesc&) const { return false; }
+
+ // Matches device by path.
+ virtual bool MatchDevice(const String& /*path*/) { return false; }
+//protected:
+ DeviceFactory* const pFactory;
+ const DeviceType Type;
+
+ // List in which this descriptor lives. pList->CreateLock required if added/removed.
+ Ptr<DeviceManagerLock> pLock;
+
+ // Strong references to us: Incremented by Device, DeviceHandles & Enumerators.
+ // May be 0 if device not created and there are no handles.
+ // Following transitions require pList->CreateLock:
+ // {1 -> 0}: May delete & remove handle if no longer available.
+ // {0 -> 1}: Device creation is only possible if manager is still alive.
+ AtomicInt<UInt32> HandleCount;
+ // If not null, points to our created device instance. Modified during lock only.
+ DeviceBase* pDevice;
+ // True if device is marked as available during enumeration.
+ bool Enumerated;
+};
+
+
+
+// Common data present in the implementation of every DeviceBase.
+// Injected by DeviceImpl.
+class DeviceCommon
+{
+public:
+ AtomicInt<UInt32> RefCount;
+ Ptr<DeviceCreateDesc> pCreateDesc;
+ Ptr<DeviceBase> pParent;
+ MessageHandlerRef HandlerRef;
+
+ DeviceCommon(DeviceCreateDesc* createDesc, DeviceBase* device, DeviceBase* parent)
+ : RefCount(1), pCreateDesc(createDesc), pParent(parent), HandlerRef(device)
+ {
+ }
+
+ // Device reference counting delegates to Manager thread to actually kill devices.
+ void DeviceAddRef();
+ void DeviceRelease();
+
+ Lock* GetLock() const { return pCreateDesc->GetLock(); }
+
+ virtual bool Initialize(DeviceBase* parent) = 0;
+ virtual void Shutdown() = 0;
+};
+
+
+//-------------------------------------------------------------------------------------
+// DeviceImpl address DeviceRecord implementation to a device base class B.
+// B must be derived form DeviceBase.
+
+template<class B>
+class DeviceImpl : public B, public DeviceCommon
+{
+public:
+ DeviceImpl(DeviceCreateDesc* createDesc, DeviceBase* parent)
+ : DeviceCommon(createDesc, getThis(), parent)
+ {
+ }
+
+ // Convenience method to avoid manager access typecasts.
+ DeviceManagerImpl* GetManagerImpl() const { return pCreateDesc->pLock->pManager; }
+
+ // Inline to avoid warnings.
+ DeviceImpl* getThis() { return this; }
+
+ // Common implementation delegate to avoid virtual inheritance and dynamic casts.
+ virtual DeviceCommon* getDeviceCommon() const { return (DeviceCommon*)this; }
+
+ /*
+ virtual void AddRef() { pCreateDesc->DeviceAddRef(); }
+ virtual void Release() { pCreateDesc->DeviceRelease(); }
+ virtual DeviceBase* GetParent() const { return pParent.GetPtr(); }
+ virtual DeviceManager* GetManager() const { return pCreateDesc->pLock->pManager;}
+ virtual void SetMessageHandler(MessageHandler* handler) { HanderRef.SetHandler(handler); }
+ virtual MessageHandler* GetMessageHandler() const { return HanderRef.GetHandler(); }
+ virtual DeviceType GetType() const { return pCreateDesc->Type; }
+ virtual DeviceType GetType() const { return pCreateDesc->Type; }
+ */
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** DeviceFactory
+
+// DeviceFactory is maintained in DeviceManager for each separately-enumerable
+// device type; factories allow separation of unrelated enumeration code.
+
+class DeviceFactory : public ListNode<DeviceFactory>, public NewOverrideBase
+{
+public:
+
+ DeviceFactory() : pManager(0)
+ {
+ pNext = pPrev = 0;
+ }
+ virtual ~DeviceFactory() { }
+
+ DeviceManagerImpl* GetManagerImpl() { return pManager; }
+
+ // Notifiers called when we are added to/removed from a device.
+ virtual bool AddedToManager(DeviceManagerImpl* manager)
+ {
+ OVR_ASSERT(pManager == 0);
+ pManager = manager;
+ return true;
+ }
+
+ virtual void RemovedFromManager()
+ {
+ pManager = 0;
+ }
+
+
+ // *** Device Enumeration/Creation Support
+
+ // Passed to EnumerateDevices to be informed of every device detected.
+ class EnumerateVisitor
+ {
+ public:
+ virtual void Visit(const DeviceCreateDesc& createDesc) = 0;
+ };
+
+ // Enumerates factory devices by notifying EnumerateVisitor about every
+ // device that is present.
+ virtual void EnumerateDevices(EnumerateVisitor& visitor) = 0;
+
+ // Matches vendorId/productId pair with the factory; returns 'true'
+ // if the factory can handle the device.
+ virtual bool MatchVendorProduct(UInt16 vendorId, UInt16 productId) const
+ {
+ OVR_UNUSED2(vendorId, productId);
+ return false;
+ }
+
+ // Detects the HID device and adds the DeviceCreateDesc into Devices list, if
+ // the device belongs to this factory. Returns 'false', if not.
+ virtual bool DetectHIDDevice(DeviceManager* pdevMgr, const HIDDeviceDesc& desc)
+ {
+ OVR_UNUSED2(pdevMgr, desc);
+ return false;
+ }
+
+protected:
+ DeviceManagerImpl* pManager;
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** DeviceManagerImpl
+
+// DeviceManagerImpl is a partial default DeviceManager implementation that
+// maintains a list of devices and supports their enumeration.
+
+class DeviceManagerImpl : public DeviceImpl<OVR::DeviceManager>, public ThreadCommandQueue
+{
+public:
+ DeviceManagerImpl();
+ ~DeviceManagerImpl();
+
+ // Constructor helper function to create Descriptor and manager lock during initialization.
+ static DeviceCreateDesc* CreateManagerDesc();
+
+ // DeviceManagerImpl provides partial implementation of Initialize/Shutdown that must
+ // be called by the platform-specific derived class.
+ virtual bool Initialize(DeviceBase* parent);
+ virtual void Shutdown();
+
+ // Override to return ThreadCommandQueue implementation used to post commands
+ // to the background device manager thread (that must be created by Initialize).
+ virtual ThreadCommandQueue* GetThreadQueue() = 0;
+
+ // Returns the thread id of the DeviceManager.
+ virtual ThreadId GetThreadId() const = 0;
+
+ virtual DeviceEnumerator<> EnumerateDevicesEx(const DeviceEnumerationArgs& args);
+
+
+ //
+ void AddFactory(DeviceFactory* factory)
+ {
+ // This lock is only needed if we call AddFactory after manager thread creation.
+ Lock::Locker scopeLock(GetLock());
+ Factories.PushBack(factory);
+ factory->AddedToManager(this);
+ }
+
+ void CallOnDeviceAdded(DeviceCreateDesc* desc)
+ {
+ HandlerRef.Call(MessageDeviceStatus(Message_DeviceAdded, this, DeviceHandle(desc)));
+ }
+ void CallOnDeviceRemoved(DeviceCreateDesc* desc)
+ {
+ HandlerRef.Call(MessageDeviceStatus(Message_DeviceRemoved, this, DeviceHandle(desc)));
+ }
+
+ // Helper to access Common data for a device.
+ static DeviceCommon* GetDeviceCommon(DeviceBase* device)
+ {
+ return device->getDeviceCommon();
+ }
+
+
+ // Background-thread callbacks for DeviceCreation/Release. These
+ DeviceBase* CreateDevice_MgrThread(DeviceCreateDesc* createDesc, DeviceBase* parent = 0);
+ Void ReleaseDevice_MgrThread(DeviceBase* device);
+
+
+ // Calls EnumerateDevices() on all factories
+ virtual Void EnumerateAllFactoryDevices();
+ // Enumerates devices for a particular factory.
+ virtual Void EnumerateFactoryDevices(DeviceFactory* factory);
+
+ virtual HIDDeviceManager* GetHIDDeviceManager() const
+ {
+ return HidDeviceManager;
+ }
+
+ // Adds device (DeviceCreateDesc*) into Devices. Returns NULL,
+ // if unsuccessful or device is already in the list.
+ virtual Ptr<DeviceCreateDesc> AddDevice_NeedsLock(const DeviceCreateDesc& createDesc);
+
+ // Finds a device descriptor by path and optional type.
+ Ptr<DeviceCreateDesc> FindDevice(const String& path, DeviceType = Device_None);
+
+ // Finds HID device by HIDDeviceDesc.
+ Ptr<DeviceCreateDesc> FindHIDDevice(const HIDDeviceDesc&);
+ void DetectHIDDevice(const HIDDeviceDesc&);
+
+ // Manager Lock-protected list of devices.
+ List<DeviceCreateDesc> Devices;
+
+ // Factories used to detect and manage devices.
+ List<DeviceFactory> Factories;
+
+protected:
+ Ptr<HIDDeviceManager> HidDeviceManager;
+};
+
+
+} // namespace OVR
+
+#endif
diff --git a/LibOVR/Src/OVR_DeviceMessages.h b/LibOVR/Src/OVR_DeviceMessages.h
new file mode 100644
index 0000000..97eb907
--- /dev/null
+++ b/LibOVR/Src/OVR_DeviceMessages.h
@@ -0,0 +1,162 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_DeviceMessages.h
+Content : Definition of messages generated by devices
+Created : February 5, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_DeviceMessages_h
+#define OVR_DeviceMessages_h
+
+#include "OVR_DeviceConstants.h"
+#include "OVR_DeviceHandle.h"
+
+#include "Kernel/OVR_Math.h"
+#include "Kernel/OVR_Array.h"
+#include "Kernel/OVR_Color.h"
+
+namespace OVR {
+
+class DeviceBase;
+class DeviceHandle;
+
+
+#define OVR_MESSAGETYPE(devName, msgIndex) ((Device_##devName << 8) | msgIndex)
+
+// MessageType identifies the structure of the Message class; based on the message,
+// casting can be used to obtain the exact value.
+enum MessageType
+{
+ // Used for unassigned message types.
+ Message_None = 0,
+
+ // Device Manager Messages
+ Message_DeviceAdded = OVR_MESSAGETYPE(Manager, 0), // A new device is detected by manager.
+ Message_DeviceRemoved = OVR_MESSAGETYPE(Manager, 1), // Existing device has been plugged/unplugged.
+ // Sensor Messages
+ Message_BodyFrame = OVR_MESSAGETYPE(Sensor, 0), // Emitted by sensor at regular intervals.
+ // Latency Tester Messages
+ Message_LatencyTestSamples = OVR_MESSAGETYPE(LatencyTester, 0),
+ Message_LatencyTestColorDetected = OVR_MESSAGETYPE(LatencyTester, 1),
+ Message_LatencyTestStarted = OVR_MESSAGETYPE(LatencyTester, 2),
+ Message_LatencyTestButton = OVR_MESSAGETYPE(LatencyTester, 3),
+
+};
+
+//-------------------------------------------------------------------------------------
+// Base class for all messages.
+class Message
+{
+public:
+ Message(MessageType type = Message_None,
+ DeviceBase* pdev = 0) : Type(type), pDevice(pdev)
+ { }
+
+ MessageType Type; // What kind of message this is.
+ DeviceBase* pDevice; // Device that emitted the message.
+};
+
+
+// Sensor BodyFrame notification.
+// Sensor uses Right-Handed coordinate system to return results, with the following
+// axis definitions:
+// - Y Up positive
+// - X Right Positive
+// - Z Back Positive
+// Rotations a counter-clockwise (CCW) while looking in the negative direction
+// of the axis. This means they are interpreted as follows:
+// - Roll is rotation around Z, counter-clockwise (tilting left) in XY plane.
+// - Yaw is rotation around Y, positive for turning left.
+// - Pitch is rotation around X, positive for pitching up.
+
+class MessageBodyFrame : public Message
+{
+public:
+ MessageBodyFrame(DeviceBase* dev)
+ : Message(Message_BodyFrame, dev), Temperature(0.0f), TimeDelta(0.0f)
+ {
+ }
+
+ Vector3f Acceleration; // Acceleration in m/s^2.
+ Vector3f RotationRate; // Angular velocity in rad/s^2.
+ Vector3f MagneticField; // Magnetic field strength in Gauss.
+ float Temperature; // Temperature reading on sensor surface, in degrees Celsius.
+ float TimeDelta; // Time passed since last Body Frame, in seconds.
+};
+
+// Sent when we receive a device status changes (e.g.:
+// Message_DeviceAdded, Message_DeviceRemoved).
+class MessageDeviceStatus : public Message
+{
+public:
+ MessageDeviceStatus(MessageType type, DeviceBase* dev, const DeviceHandle &hdev)
+ : Message(type, dev), Handle(hdev) { }
+
+ DeviceHandle Handle;
+};
+
+//-------------------------------------------------------------------------------------
+// ***** Latency Tester
+
+// Sent when we receive Latency Tester samples.
+class MessageLatencyTestSamples : public Message
+{
+public:
+ MessageLatencyTestSamples(DeviceBase* dev)
+ : Message(Message_LatencyTestSamples, dev)
+ {
+ }
+
+ Array<Color> Samples;
+};
+
+// Sent when a Latency Tester 'color detected' event occurs.
+class MessageLatencyTestColorDetected : public Message
+{
+public:
+ MessageLatencyTestColorDetected(DeviceBase* dev)
+ : Message(Message_LatencyTestColorDetected, dev)
+ {
+ }
+
+ UInt16 Elapsed;
+ Color DetectedValue;
+ Color TargetValue;
+};
+
+// Sent when a Latency Tester 'change color' event occurs.
+class MessageLatencyTestStarted : public Message
+{
+public:
+ MessageLatencyTestStarted(DeviceBase* dev)
+ : Message(Message_LatencyTestStarted, dev)
+ {
+ }
+
+ Color TargetValue;
+};
+
+// Sent when a Latency Tester 'button' event occurs.
+class MessageLatencyTestButton : public Message
+{
+public:
+ MessageLatencyTestButton(DeviceBase* dev)
+ : Message(Message_LatencyTestButton, dev)
+ {
+ }
+
+};
+
+
+} // namespace OVR
+
+#endif
diff --git a/LibOVR/Src/OVR_HIDDevice.h b/LibOVR/Src/OVR_HIDDevice.h
new file mode 100644
index 0000000..b7ef089
--- /dev/null
+++ b/LibOVR/Src/OVR_HIDDevice.h
@@ -0,0 +1,143 @@
+/************************************************************************************
+
+Filename : OVR_HIDDevice.h
+Content : Cross platform HID device interface.
+Created : February 22, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_HIDDevice_h
+#define OVR_HIDDevice_h
+
+#include "OVR_HIDDeviceBase.h"
+
+#include "Kernel/OVR_RefCount.h"
+#include "Kernel/OVR_String.h"
+#include "Kernel/OVR_Timer.h"
+
+namespace OVR {
+
+class HIDDevice;
+class DeviceManager;
+
+// HIDDeviceDesc contains interesting attributes of a HID device, including a Path
+// that can be used to create it.
+struct HIDDeviceDesc
+{
+ UInt16 VendorId;
+ UInt16 ProductId;
+ UInt16 VersionNumber;
+ UInt16 Usage;
+ UInt16 UsagePage;
+ String Path; // Platform specific.
+ String Manufacturer;
+ String Product;
+ String SerialNumber;
+};
+
+// HIDEnumerateVisitor exposes a Visit interface called for every detected device
+// by HIDDeviceManager::Enumerate.
+class HIDEnumerateVisitor
+{
+public:
+
+ // Should return true if we are interested in supporting
+ // this HID VendorId and ProductId pair.
+ virtual bool MatchVendorProduct(UInt16 vendorId, UInt16 productId)
+ { OVR_UNUSED2(vendorId, productId); return true; }
+
+ // Override to get notified about available device. Will only be called for
+ // devices that matched MatchVendorProduct.
+ virtual void Visit(HIDDevice&, const HIDDeviceDesc&) { }
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** HIDDeviceManager
+
+// Internal manager for enumerating and opening HID devices.
+// If an OVR::DeviceManager is created then an OVR::HIDDeviceManager will automatically be created and can be accessed from the
+// DeviceManager by calling 'GetHIDDeviceManager()'. When using HIDDeviceManager in standalone mode, the client must call
+// 'Create' below.
+class HIDDeviceManager : public RefCountBase<HIDDeviceManager>
+{
+public:
+
+ // Creates a new HIDDeviceManager. Only one instance of HIDDeviceManager should be created at a time.
+ static HIDDeviceManager* Create();
+
+ // Enumerate HID devices using a HIDEnumerateVisitor derived visitor class.
+ virtual bool Enumerate(HIDEnumerateVisitor* enumVisitor) = 0;
+
+ // Open a HID device with the specified path.
+ virtual HIDDevice* Open(const String& path) = 0;
+
+protected:
+ HIDDeviceManager()
+ { }
+};
+
+//-------------------------------------------------------------------------------------
+// ***** HIDDevice
+
+// HID device object. This is designed to be operated in synchronous
+// and asynchronous modes. With no handler set, input messages will be
+// stored and can be retrieved by calling 'Read' or 'ReadBlocking'.
+class HIDDevice : public RefCountBase<HIDDevice>, public HIDDeviceBase
+{
+public:
+
+ HIDDevice()
+ : Handler(NULL)
+ {
+ }
+
+ virtual ~HIDDevice() {}
+
+ virtual bool SetFeatureReport(UByte* data, UInt32 length) = 0;
+ virtual bool GetFeatureReport(UByte* data, UInt32 length) = 0;
+
+// Not yet implemented.
+/*
+ virtual bool Write(UByte* data, UInt32 length) = 0;
+
+ virtual bool Read(UByte* pData, UInt32 length, UInt32 timeoutMilliS) = 0;
+ virtual bool ReadBlocking(UByte* pData, UInt32 length) = 0;
+*/
+
+ class HIDHandler
+ {
+ public:
+ virtual void OnInputReport(UByte* pData, UInt32 length)
+ { OVR_UNUSED2(pData, length); }
+
+ virtual UInt64 OnTicks(UInt64 ticksMks)
+ { OVR_UNUSED1(ticksMks); return Timer::MksPerSecond * 1000; ; }
+
+ enum HIDDeviceMessageType
+ {
+ HIDDeviceMessage_DeviceAdded = 0,
+ HIDDeviceMessage_DeviceRemoved = 1
+ };
+
+ virtual void OnDeviceMessage(HIDDeviceMessageType messageType)
+ { OVR_UNUSED1(messageType); }
+ };
+
+ void SetHandler(HIDHandler* handler)
+ { Handler = handler; }
+
+protected:
+ HIDHandler* Handler;
+};
+
+} // namespace OVR
+
+#endif
diff --git a/LibOVR/Src/OVR_HIDDeviceBase.h b/LibOVR/Src/OVR_HIDDeviceBase.h
new file mode 100644
index 0000000..42fdadc
--- /dev/null
+++ b/LibOVR/Src/OVR_HIDDeviceBase.h
@@ -0,0 +1,40 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_HIDDeviceBase.h
+Content : Definition of HID device interface.
+Created : March 11, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_HIDDeviceBase_h
+#define OVR_HIDDeviceBase_h
+
+#include "Kernel/OVR_Types.h"
+
+namespace OVR {
+
+//-------------------------------------------------------------------------------------
+// ***** HIDDeviceBase
+
+// Base interface for HID devices.
+class HIDDeviceBase
+{
+public:
+
+ virtual ~HIDDeviceBase() { }
+
+ virtual bool SetFeatureReport(UByte* data, UInt32 length) = 0;
+ virtual bool GetFeatureReport(UByte* data, UInt32 length) = 0;
+};
+
+} // namespace OVR
+
+#endif
diff --git a/LibOVR/Src/OVR_HIDDeviceImpl.h b/LibOVR/Src/OVR_HIDDeviceImpl.h
new file mode 100644
index 0000000..c5163b4
--- /dev/null
+++ b/LibOVR/Src/OVR_HIDDeviceImpl.h
@@ -0,0 +1,203 @@
+/************************************************************************************
+
+Filename : OVR_HIDDeviceImpl.h
+Content : Implementation of HIDDevice.
+Created : March 7, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_HIDDeviceImpl_h
+#define OVR_HIDDeviceImpl_h
+
+//#include "OVR_Device.h"
+#include "OVR_DeviceImpl.h"
+
+namespace OVR {
+
+//-------------------------------------------------------------------------------------
+class HIDDeviceCreateDesc : public DeviceCreateDesc
+{
+public:
+ HIDDeviceCreateDesc(DeviceFactory* factory, DeviceType type, const HIDDeviceDesc& hidDesc)
+ : DeviceCreateDesc(factory, type), HIDDesc(hidDesc) { }
+ HIDDeviceCreateDesc(const HIDDeviceCreateDesc& other)
+ : DeviceCreateDesc(other.pFactory, other.Type), HIDDesc(other.HIDDesc) { }
+
+ virtual bool MatchDevice(const String& path)
+ {
+ // should it be case insensitive?
+ return HIDDesc.Path.CompareNoCase(path) == 0;
+ }
+
+ HIDDeviceDesc HIDDesc;
+};
+
+//-------------------------------------------------------------------------------------
+template<class B>
+class HIDDeviceImpl : public DeviceImpl<B>, public HIDDevice::HIDHandler
+{
+public:
+ HIDDeviceImpl(HIDDeviceCreateDesc* createDesc, DeviceBase* parent)
+ : DeviceImpl<B>(createDesc, parent)
+ {
+ }
+
+ // HIDDevice::Handler interface.
+ virtual void OnDeviceMessage(HIDDeviceMessageType messageType)
+ {
+ MessageType handlerMessageType;
+ switch (messageType) {
+ case HIDDeviceMessage_DeviceAdded:
+ handlerMessageType = Message_DeviceAdded;
+ break;
+
+ case HIDDeviceMessage_DeviceRemoved:
+ handlerMessageType = Message_DeviceRemoved;
+ break;
+
+ default: OVR_ASSERT(0); return;
+ }
+
+ // Do device notification.
+ {
+ Lock::Locker scopeLock(this->HandlerRef.GetLock());
+
+ if (this->HandlerRef.GetHandler())
+ {
+ MessageDeviceStatus status(handlerMessageType, this, OVR::DeviceHandle(this->pCreateDesc));
+ this->HandlerRef.GetHandler()->OnMessage(status);
+ }
+ }
+
+ // Do device manager notification.
+ DeviceManagerImpl* manager = this->GetManagerImpl();
+ switch (handlerMessageType) {
+ case Message_DeviceAdded:
+ manager->CallOnDeviceAdded(this->pCreateDesc);
+ break;
+
+ case Message_DeviceRemoved:
+ manager->CallOnDeviceRemoved(this->pCreateDesc);
+ break;
+
+ default:;
+ }
+ }
+
+ virtual bool Initialize(DeviceBase* parent)
+ {
+ // Open HID device.
+ HIDDeviceDesc& hidDesc = *getHIDDesc();
+ HIDDeviceManager* pManager = GetHIDDeviceManager();
+
+
+ HIDDevice* device = pManager->Open(hidDesc.Path);
+ if (!device)
+ {
+ return false;
+ }
+
+ InternalDevice = *device;
+ InternalDevice->SetHandler(this);
+
+ // AddRef() to parent, forcing chain to stay alive.
+ DeviceImpl<B>::pParent = parent;
+
+ return true;
+ }
+
+ virtual void Shutdown()
+ {
+ InternalDevice->SetHandler(NULL);
+
+ // Remove the handler, if any.
+ this->HandlerRef.SetHandler(0);
+
+ DeviceImpl<B>::pParent.Clear();
+ }
+
+ DeviceManager* GetDeviceManager()
+ {
+ return DeviceImpl<B>::pCreateDesc->GetManagerImpl();
+ }
+
+ HIDDeviceManager* GetHIDDeviceManager()
+ {
+ return DeviceImpl<B>::pCreateDesc->GetManagerImpl()->GetHIDDeviceManager();
+ }
+
+
+ struct WriteData
+ {
+ enum { BufferSize = 64 };
+ UByte Buffer[64];
+ UPInt Size;
+
+ WriteData(UByte* data, UPInt size) : Size(size)
+ {
+ OVR_ASSERT(size <= BufferSize);
+ memcpy(Buffer, data, size);
+ }
+ };
+
+ bool SetFeatureReport(UByte* data, UInt32 length)
+ {
+ WriteData writeData(data, length);
+
+ // Push call with wait.
+ bool result = false;
+
+ ThreadCommandQueue* pQueue = this->GetManagerImpl()->GetThreadQueue();
+ if (!pQueue->PushCallAndWaitResult(this, &HIDDeviceImpl::setFeatureReport, &result, writeData))
+ return false;
+
+ return result;
+ }
+
+ bool setFeatureReport(const WriteData& data)
+ {
+ return InternalDevice->SetFeatureReport((UByte*) data.Buffer, (UInt32) data.Size);
+ }
+
+ bool GetFeatureReport(UByte* data, UInt32 length)
+ {
+ bool result = false;
+
+ ThreadCommandQueue* pQueue = this->GetManagerImpl()->GetThreadQueue();
+ if (!pQueue->PushCallAndWaitResult(this, &HIDDeviceImpl::getFeatureReport, &result, data, length))
+ return false;
+
+ return result;
+ }
+
+ bool getFeatureReport(UByte* data, UInt32 length)
+ {
+ return InternalDevice->GetFeatureReport(data, length);
+ }
+
+protected:
+ HIDDevice* GetInternalDevice() const
+ {
+ return InternalDevice;
+ }
+
+ HIDDeviceDesc* getHIDDesc() const
+ { return &getCreateDesc()->HIDDesc; }
+
+ HIDDeviceCreateDesc* getCreateDesc() const
+ { return (HIDDeviceCreateDesc*) &(*DeviceImpl<B>::pCreateDesc); }
+
+private:
+ Ptr<HIDDevice> InternalDevice;
+};
+
+} // namespace OVR
+
+#endif
diff --git a/LibOVR/Src/OVR_LatencyTestImpl.cpp b/LibOVR/Src/OVR_LatencyTestImpl.cpp
new file mode 100644
index 0000000..ff420a2
--- /dev/null
+++ b/LibOVR/Src/OVR_LatencyTestImpl.cpp
@@ -0,0 +1,798 @@
+/************************************************************************************
+
+Filename : OVR_LatencyTestImpl.cpp
+Content : Oculus Latency Tester device implementation.
+Created : March 7, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#include "OVR_LatencyTestImpl.h"
+
+namespace OVR {
+
+//-------------------------------------------------------------------------------------
+// ***** Oculus Latency Tester specific packet data structures
+
+enum {
+ LatencyTester_VendorId = Oculus_VendorId,
+ LatencyTester_ProductId = 0x0101,
+};
+
+// Reported data is little-endian now
+static UInt16 DecodeUInt16(const UByte* buffer)
+{
+ return (UInt16(buffer[1]) << 8) | UInt16(buffer[0]);
+}
+
+/* Unreferenced
+static SInt16 DecodeSInt16(const UByte* buffer)
+{
+ return (SInt16(buffer[1]) << 8) | SInt16(buffer[0]);
+}*/
+
+static void UnpackSamples(const UByte* buffer, UByte* r, UByte* g, UByte* b)
+{
+ *r = buffer[0];
+ *g = buffer[1];
+ *b = buffer[2];
+}
+
+// Messages we handle.
+enum LatencyTestMessageType
+{
+ LatencyTestMessage_None = 0,
+ LatencyTestMessage_Samples = 1,
+ LatencyTestMessage_ColorDetected = 2,
+ LatencyTestMessage_TestStarted = 3,
+ LatencyTestMessage_Button = 4,
+ LatencyTestMessage_Unknown = 0x100,
+ LatencyTestMessage_SizeError = 0x101,
+};
+
+struct LatencyTestSample
+{
+ UByte Value[3];
+};
+
+struct LatencyTestSamples
+{
+ UByte SampleCount;
+ UInt16 Timestamp;
+
+ LatencyTestSample Samples[20];
+
+ LatencyTestMessageType Decode(const UByte* buffer, int size)
+ {
+ if (size < 64)
+ {
+ return LatencyTestMessage_SizeError;
+ }
+
+ SampleCount = buffer[1];
+ Timestamp = DecodeUInt16(buffer + 2);
+
+ for (UByte i = 0; i < SampleCount; i++)
+ {
+ UnpackSamples(buffer + 4 + (3 * i), &Samples[i].Value[0], &Samples[i].Value[1], &Samples[i].Value[2]);
+ }
+
+ return LatencyTestMessage_Samples;
+ }
+};
+
+struct LatencyTestSamplesMessage
+{
+ LatencyTestMessageType Type;
+ LatencyTestSamples Samples;
+};
+
+bool DecodeLatencyTestSamplesMessage(LatencyTestSamplesMessage* message, UByte* buffer, int size)
+{
+ memset(message, 0, sizeof(LatencyTestSamplesMessage));
+
+ if (size < 64)
+ {
+ message->Type = LatencyTestMessage_SizeError;
+ return false;
+ }
+
+ switch (buffer[0])
+ {
+ case LatencyTestMessage_Samples:
+ message->Type = message->Samples.Decode(buffer, size);
+ break;
+
+ default:
+ message->Type = LatencyTestMessage_Unknown;
+ break;
+ }
+
+ return (message->Type < LatencyTestMessage_Unknown) && (message->Type != LatencyTestMessage_None);
+}
+
+struct LatencyTestColorDetected
+{
+ UInt16 CommandID;
+ UInt16 Timestamp;
+ UInt16 Elapsed;
+ UByte TriggerValue[3];
+ UByte TargetValue[3];
+
+ LatencyTestMessageType Decode(const UByte* buffer, int size)
+ {
+ if (size < 13)
+ return LatencyTestMessage_SizeError;
+
+ CommandID = DecodeUInt16(buffer + 1);
+ Timestamp = DecodeUInt16(buffer + 3);
+ Elapsed = DecodeUInt16(buffer + 5);
+ memcpy(TriggerValue, buffer + 7, 3);
+ memcpy(TargetValue, buffer + 10, 3);
+
+ return LatencyTestMessage_ColorDetected;
+ }
+};
+
+struct LatencyTestColorDetectedMessage
+{
+ LatencyTestMessageType Type;
+ LatencyTestColorDetected ColorDetected;
+};
+
+bool DecodeLatencyTestColorDetectedMessage(LatencyTestColorDetectedMessage* message, UByte* buffer, int size)
+{
+ memset(message, 0, sizeof(LatencyTestColorDetectedMessage));
+
+ if (size < 13)
+ {
+ message->Type = LatencyTestMessage_SizeError;
+ return false;
+ }
+
+ switch (buffer[0])
+ {
+ case LatencyTestMessage_ColorDetected:
+ message->Type = message->ColorDetected.Decode(buffer, size);
+ break;
+
+ default:
+ message->Type = LatencyTestMessage_Unknown;
+ break;
+ }
+
+ return (message->Type < LatencyTestMessage_Unknown) && (message->Type != LatencyTestMessage_None);
+}
+
+struct LatencyTestStarted
+{
+ UInt16 CommandID;
+ UInt16 Timestamp;
+ UByte TargetValue[3];
+
+ LatencyTestMessageType Decode(const UByte* buffer, int size)
+ {
+ if (size < 8)
+ return LatencyTestMessage_SizeError;
+
+ CommandID = DecodeUInt16(buffer + 1);
+ Timestamp = DecodeUInt16(buffer + 3);
+ memcpy(TargetValue, buffer + 5, 3);
+
+ return LatencyTestMessage_TestStarted;
+ }
+};
+
+struct LatencyTestStartedMessage
+{
+ LatencyTestMessageType Type;
+ LatencyTestStarted TestStarted;
+};
+
+bool DecodeLatencyTestStartedMessage(LatencyTestStartedMessage* message, UByte* buffer, int size)
+{
+ memset(message, 0, sizeof(LatencyTestStartedMessage));
+
+ if (size < 8)
+ {
+ message->Type = LatencyTestMessage_SizeError;
+ return false;
+ }
+
+ switch (buffer[0])
+ {
+ case LatencyTestMessage_TestStarted:
+ message->Type = message->TestStarted.Decode(buffer, size);
+ break;
+
+ default:
+ message->Type = LatencyTestMessage_Unknown;
+ break;
+ }
+
+ return (message->Type < LatencyTestMessage_Unknown) && (message->Type != LatencyTestMessage_None);
+}
+
+struct LatencyTestButton
+{
+ UInt16 CommandID;
+ UInt16 Timestamp;
+
+ LatencyTestMessageType Decode(const UByte* buffer, int size)
+ {
+ if (size < 5)
+ return LatencyTestMessage_SizeError;
+
+ CommandID = DecodeUInt16(buffer + 1);
+ Timestamp = DecodeUInt16(buffer + 3);
+
+ return LatencyTestMessage_Button;
+ }
+};
+
+struct LatencyTestButtonMessage
+{
+ LatencyTestMessageType Type;
+ LatencyTestButton Button;
+};
+
+bool DecodeLatencyTestButtonMessage(LatencyTestButtonMessage* message, UByte* buffer, int size)
+{
+ memset(message, 0, sizeof(LatencyTestButtonMessage));
+
+ if (size < 5)
+ {
+ message->Type = LatencyTestMessage_SizeError;
+ return false;
+ }
+
+ switch (buffer[0])
+ {
+ case LatencyTestMessage_Button:
+ message->Type = message->Button.Decode(buffer, size);
+ break;
+
+ default:
+ message->Type = LatencyTestMessage_Unknown;
+ break;
+ }
+
+ return (message->Type < LatencyTestMessage_Unknown) && (message->Type != LatencyTestMessage_None);
+}
+
+struct LatencyTestConfigurationImpl
+{
+ enum { PacketSize = 5 };
+ UByte Buffer[PacketSize];
+
+ OVR::LatencyTestConfiguration Configuration;
+
+ LatencyTestConfigurationImpl(const OVR::LatencyTestConfiguration& configuration)
+ : Configuration(configuration)
+ {
+ Pack();
+ }
+
+ void Pack()
+ {
+ Buffer[0] = 5;
+ Buffer[1] = UByte(Configuration.SendSamples);
+ Buffer[2] = Configuration.Threshold.R;
+ Buffer[3] = Configuration.Threshold.G;
+ Buffer[4] = Configuration.Threshold.B;
+ }
+
+ void Unpack()
+ {
+ Configuration.SendSamples = Buffer[1] != 0 ? true : false;
+ Configuration.Threshold.R = Buffer[2];
+ Configuration.Threshold.G = Buffer[3];
+ Configuration.Threshold.B = Buffer[4];
+ }
+};
+
+struct LatencyTestCalibrateImpl
+{
+ enum { PacketSize = 4 };
+ UByte Buffer[PacketSize];
+
+ OVR::LatencyTestCalibrate Calibrate;
+
+ LatencyTestCalibrateImpl(const OVR::LatencyTestCalibrate& calibrate)
+ : Calibrate(calibrate)
+ {
+ Pack();
+ }
+
+ void Pack()
+ {
+ Buffer[0] = 7;
+ Buffer[1] = Calibrate.Value.R;
+ Buffer[2] = Calibrate.Value.G;
+ Buffer[3] = Calibrate.Value.B;
+ }
+
+ void Unpack()
+ {
+ Calibrate.Value.R = Buffer[1];
+ Calibrate.Value.G = Buffer[2];
+ Calibrate.Value.B = Buffer[3];
+ }
+};
+
+struct LatencyTestStartTestImpl
+{
+ enum { PacketSize = 6 };
+ UByte Buffer[PacketSize];
+
+ OVR::LatencyTestStartTest StartTest;
+
+ LatencyTestStartTestImpl(const OVR::LatencyTestStartTest& startTest)
+ : StartTest(startTest)
+ {
+ Pack();
+ }
+
+ void Pack()
+ {
+ UInt16 commandID = 1;
+
+ Buffer[0] = 8;
+ Buffer[1] = UByte(commandID & 0xFF);
+ Buffer[2] = UByte(commandID >> 8);
+ Buffer[3] = StartTest.TargetValue.R;
+ Buffer[4] = StartTest.TargetValue.G;
+ Buffer[5] = StartTest.TargetValue.B;
+ }
+
+ void Unpack()
+ {
+// UInt16 commandID = Buffer[1] | (UInt16(Buffer[2]) << 8);
+ StartTest.TargetValue.R = Buffer[3];
+ StartTest.TargetValue.G = Buffer[4];
+ StartTest.TargetValue.B = Buffer[5];
+ }
+};
+
+struct LatencyTestDisplayImpl
+{
+ enum { PacketSize = 6 };
+ UByte Buffer[PacketSize];
+
+ OVR::LatencyTestDisplay Display;
+
+ LatencyTestDisplayImpl(const OVR::LatencyTestDisplay& display)
+ : Display(display)
+ {
+ Pack();
+ }
+
+ void Pack()
+ {
+ Buffer[0] = 9;
+ Buffer[1] = Display.Mode;
+ Buffer[2] = UByte(Display.Value & 0xFF);
+ Buffer[3] = UByte((Display.Value >> 8) & 0xFF);
+ Buffer[4] = UByte((Display.Value >> 16) & 0xFF);
+ Buffer[5] = UByte((Display.Value >> 24) & 0xFF);
+ }
+
+ void Unpack()
+ {
+ Display.Mode = Buffer[1];
+ Display.Value = UInt32(Buffer[2]) |
+ (UInt32(Buffer[3]) << 8) |
+ (UInt32(Buffer[4]) << 16) |
+ (UInt32(Buffer[5]) << 24);
+ }
+};
+
+//-------------------------------------------------------------------------------------
+// ***** LatencyTestDeviceFactory
+
+LatencyTestDeviceFactory LatencyTestDeviceFactory::Instance;
+
+void LatencyTestDeviceFactory::EnumerateDevices(EnumerateVisitor& visitor)
+{
+
+ class LatencyTestEnumerator : public HIDEnumerateVisitor
+ {
+ // Assign not supported; suppress MSVC warning.
+ void operator = (const LatencyTestEnumerator&) { }
+
+ DeviceFactory* pFactory;
+ EnumerateVisitor& ExternalVisitor;
+ public:
+ LatencyTestEnumerator(DeviceFactory* factory, EnumerateVisitor& externalVisitor)
+ : pFactory(factory), ExternalVisitor(externalVisitor) { }
+
+ virtual bool MatchVendorProduct(UInt16 vendorId, UInt16 productId)
+ {
+ return pFactory->MatchVendorProduct(vendorId, productId);
+ }
+
+ virtual void Visit(HIDDevice& device, const HIDDeviceDesc& desc)
+ {
+ OVR_UNUSED(device);
+
+ LatencyTestDeviceCreateDesc createDesc(pFactory, desc);
+ ExternalVisitor.Visit(createDesc);
+ }
+ };
+
+ LatencyTestEnumerator latencyTestEnumerator(this, visitor);
+ GetManagerImpl()->GetHIDDeviceManager()->Enumerate(&latencyTestEnumerator);
+}
+
+bool LatencyTestDeviceFactory::MatchVendorProduct(UInt16 vendorId, UInt16 productId) const
+{
+ return ((vendorId == LatencyTester_VendorId) && (productId == LatencyTester_ProductId));
+}
+
+bool LatencyTestDeviceFactory::DetectHIDDevice(DeviceManager* pdevMgr,
+ const HIDDeviceDesc& desc)
+{
+ if (MatchVendorProduct(desc.VendorId, desc.ProductId))
+ {
+ LatencyTestDeviceCreateDesc createDesc(this, desc);
+ return pdevMgr->AddDevice_NeedsLock(createDesc).GetPtr() != NULL;
+ }
+ return false;
+}
+
+//-------------------------------------------------------------------------------------
+// ***** LatencyTestDeviceCreateDesc
+
+DeviceBase* LatencyTestDeviceCreateDesc::NewDeviceInstance()
+{
+ return new LatencyTestDeviceImpl(this);
+}
+
+bool LatencyTestDeviceCreateDesc::GetDeviceInfo(DeviceInfo* info) const
+{
+ if ((info->InfoClassType != Device_LatencyTester) &&
+ (info->InfoClassType != Device_None))
+ return false;
+
+ OVR_strcpy(info->ProductName, DeviceInfo::MaxNameLength, HIDDesc.Product.ToCStr());
+ OVR_strcpy(info->Manufacturer, DeviceInfo::MaxNameLength, HIDDesc.Manufacturer.ToCStr());
+ info->Type = Device_LatencyTester;
+ info->Version = 0;
+
+ if (info->InfoClassType == Device_LatencyTester)
+ {
+ SensorInfo* sinfo = (SensorInfo*)info;
+ sinfo->VendorId = HIDDesc.VendorId;
+ sinfo->ProductId = HIDDesc.ProductId;
+ OVR_strcpy(sinfo->SerialNumber, sizeof(sinfo->SerialNumber),HIDDesc.SerialNumber.ToCStr());
+ }
+ return true;
+}
+
+//-------------------------------------------------------------------------------------
+// ***** LatencyTestDevice
+
+LatencyTestDeviceImpl::LatencyTestDeviceImpl(LatencyTestDeviceCreateDesc* createDesc)
+ : OVR::HIDDeviceImpl<OVR::LatencyTestDevice>(createDesc, 0)
+{
+}
+
+LatencyTestDeviceImpl::~LatencyTestDeviceImpl()
+{
+ // Check that Shutdown() was called.
+ OVR_ASSERT(!pCreateDesc->pDevice);
+}
+
+// Internal creation APIs.
+bool LatencyTestDeviceImpl::Initialize(DeviceBase* parent)
+{
+ if (HIDDeviceImpl<OVR::LatencyTestDevice>::Initialize(parent))
+ {
+ LogText("OVR::LatencyTestDevice initialized.\n");
+ return true;
+ }
+
+ return false;
+}
+
+void LatencyTestDeviceImpl::Shutdown()
+{
+ HIDDeviceImpl<OVR::LatencyTestDevice>::Shutdown();
+
+ LogText("OVR::LatencyTestDevice - Closed '%s'\n", getHIDDesc()->Path.ToCStr());
+}
+
+void LatencyTestDeviceImpl::OnInputReport(UByte* pData, UInt32 length)
+{
+
+ bool processed = false;
+ if (!processed)
+ {
+ LatencyTestSamplesMessage message;
+ if (DecodeLatencyTestSamplesMessage(&message, pData, length))
+ {
+ processed = true;
+ onLatencyTestSamplesMessage(&message);
+ }
+ }
+
+ if (!processed)
+ {
+ LatencyTestColorDetectedMessage message;
+ if (DecodeLatencyTestColorDetectedMessage(&message, pData, length))
+ {
+ processed = true;
+ onLatencyTestColorDetectedMessage(&message);
+ }
+ }
+
+ if (!processed)
+ {
+ LatencyTestStartedMessage message;
+ if (DecodeLatencyTestStartedMessage(&message, pData, length))
+ {
+ processed = true;
+ onLatencyTestStartedMessage(&message);
+ }
+ }
+
+ if (!processed)
+ {
+ LatencyTestButtonMessage message;
+ if (DecodeLatencyTestButtonMessage(&message, pData, length))
+ {
+ processed = true;
+ onLatencyTestButtonMessage(&message);
+ }
+ }
+}
+
+bool LatencyTestDeviceImpl::SetConfiguration(const OVR::LatencyTestConfiguration& configuration, bool waitFlag)
+{
+ bool result = false;
+ ThreadCommandQueue* queue = GetManagerImpl()->GetThreadQueue();
+
+ if (GetManagerImpl()->GetThreadId() != OVR::GetCurrentThreadId())
+ {
+ if (!waitFlag)
+ {
+ return queue->PushCall(this, &LatencyTestDeviceImpl::setConfiguration, configuration);
+ }
+
+ if (!queue->PushCallAndWaitResult( this,
+ &LatencyTestDeviceImpl::setConfiguration,
+ &result,
+ configuration))
+ {
+ return false;
+ }
+ }
+ else
+ return setConfiguration(configuration);
+
+ return result;
+}
+
+bool LatencyTestDeviceImpl::setConfiguration(const OVR::LatencyTestConfiguration& configuration)
+{
+ LatencyTestConfigurationImpl ltc(configuration);
+ return GetInternalDevice()->SetFeatureReport(ltc.Buffer, LatencyTestConfigurationImpl::PacketSize);
+}
+
+bool LatencyTestDeviceImpl::GetConfiguration(OVR::LatencyTestConfiguration* configuration)
+{
+ bool result = false;
+
+ ThreadCommandQueue* pQueue = this->GetManagerImpl()->GetThreadQueue();
+ if (!pQueue->PushCallAndWaitResult(this, &LatencyTestDeviceImpl::getConfiguration, &result, configuration))
+ return false;
+
+ return result;
+}
+
+bool LatencyTestDeviceImpl::getConfiguration(OVR::LatencyTestConfiguration* configuration)
+{
+ LatencyTestConfigurationImpl ltc(*configuration);
+ if (GetInternalDevice()->GetFeatureReport(ltc.Buffer, LatencyTestConfigurationImpl::PacketSize))
+ {
+ ltc.Unpack();
+ *configuration = ltc.Configuration;
+ return true;
+ }
+
+ return false;
+}
+
+bool LatencyTestDeviceImpl::SetCalibrate(const OVR::LatencyTestCalibrate& calibrate, bool waitFlag)
+{
+ bool result = false;
+ ThreadCommandQueue* queue = GetManagerImpl()->GetThreadQueue();
+
+ if (!waitFlag)
+ {
+ return queue->PushCall(this, &LatencyTestDeviceImpl::setCalibrate, calibrate);
+ }
+
+ if (!queue->PushCallAndWaitResult( this,
+ &LatencyTestDeviceImpl::setCalibrate,
+ &result,
+ calibrate))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool LatencyTestDeviceImpl::setCalibrate(const OVR::LatencyTestCalibrate& calibrate)
+{
+ LatencyTestCalibrateImpl ltc(calibrate);
+ return GetInternalDevice()->SetFeatureReport(ltc.Buffer, LatencyTestCalibrateImpl::PacketSize);
+}
+
+bool LatencyTestDeviceImpl::GetCalibrate(OVR::LatencyTestCalibrate* calibrate)
+{
+ bool result = false;
+
+ ThreadCommandQueue* pQueue = this->GetManagerImpl()->GetThreadQueue();
+ if (!pQueue->PushCallAndWaitResult(this, &LatencyTestDeviceImpl::getCalibrate, &result, calibrate))
+ return false;
+
+ return result;
+}
+
+bool LatencyTestDeviceImpl::getCalibrate(OVR::LatencyTestCalibrate* calibrate)
+{
+ LatencyTestCalibrateImpl ltc(*calibrate);
+ if (GetInternalDevice()->GetFeatureReport(ltc.Buffer, LatencyTestCalibrateImpl::PacketSize))
+ {
+ ltc.Unpack();
+ *calibrate = ltc.Calibrate;
+ return true;
+ }
+
+ return false;
+}
+
+bool LatencyTestDeviceImpl::SetStartTest(const OVR::LatencyTestStartTest& start, bool waitFlag)
+{
+ bool result = false;
+ ThreadCommandQueue* queue = GetManagerImpl()->GetThreadQueue();
+
+ if (!waitFlag)
+ {
+ return queue->PushCall(this, &LatencyTestDeviceImpl::setStartTest, start);
+ }
+
+ if (!queue->PushCallAndWaitResult( this,
+ &LatencyTestDeviceImpl::setStartTest,
+ &result,
+ start))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool LatencyTestDeviceImpl::setStartTest(const OVR::LatencyTestStartTest& start)
+{
+ LatencyTestStartTestImpl ltst(start);
+ return GetInternalDevice()->SetFeatureReport(ltst.Buffer, LatencyTestStartTestImpl::PacketSize);
+}
+
+bool LatencyTestDeviceImpl::SetDisplay(const OVR::LatencyTestDisplay& display, bool waitFlag)
+{
+ bool result = false;
+ ThreadCommandQueue * queue = GetManagerImpl()->GetThreadQueue();
+
+ if (!waitFlag)
+ {
+ return queue->PushCall(this, &LatencyTestDeviceImpl::setDisplay, display);
+ }
+
+ if (!queue->PushCallAndWaitResult( this,
+ &LatencyTestDeviceImpl::setDisplay,
+ &result,
+ display))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool LatencyTestDeviceImpl::setDisplay(const OVR::LatencyTestDisplay& display)
+{
+ LatencyTestDisplayImpl ltd(display);
+ return GetInternalDevice()->SetFeatureReport(ltd.Buffer, LatencyTestDisplayImpl::PacketSize);
+}
+
+void LatencyTestDeviceImpl::onLatencyTestSamplesMessage(LatencyTestSamplesMessage* message)
+{
+
+ if (message->Type != LatencyTestMessage_Samples)
+ return;
+
+ LatencyTestSamples& s = message->Samples;
+
+ // Call OnMessage() within a lock to avoid conflicts with handlers.
+ Lock::Locker scopeLock(HandlerRef.GetLock());
+
+ if (HandlerRef.GetHandler())
+ {
+ MessageLatencyTestSamples samples(this);
+ for (UByte i = 0; i < s.SampleCount; i++)
+ {
+ samples.Samples.PushBack(Color(s.Samples[i].Value[0], s.Samples[i].Value[1], s.Samples[i].Value[2]));
+ }
+
+ HandlerRef.GetHandler()->OnMessage(samples);
+ }
+}
+
+void LatencyTestDeviceImpl::onLatencyTestColorDetectedMessage(LatencyTestColorDetectedMessage* message)
+{
+ if (message->Type != LatencyTestMessage_ColorDetected)
+ return;
+
+ LatencyTestColorDetected& s = message->ColorDetected;
+
+ // Call OnMessage() within a lock to avoid conflicts with handlers.
+ Lock::Locker scopeLock(HandlerRef.GetLock());
+
+ if (HandlerRef.GetHandler())
+ {
+ MessageLatencyTestColorDetected detected(this);
+ detected.Elapsed = s.Elapsed;
+ detected.DetectedValue = Color(s.TriggerValue[0], s.TriggerValue[1], s.TriggerValue[2]);
+ detected.TargetValue = Color(s.TargetValue[0], s.TargetValue[1], s.TargetValue[2]);
+
+ HandlerRef.GetHandler()->OnMessage(detected);
+ }
+}
+
+void LatencyTestDeviceImpl::onLatencyTestStartedMessage(LatencyTestStartedMessage* message)
+{
+ if (message->Type != LatencyTestMessage_TestStarted)
+ return;
+
+ LatencyTestStarted& ts = message->TestStarted;
+
+ // Call OnMessage() within a lock to avoid conflicts with handlers.
+ Lock::Locker scopeLock(HandlerRef.GetLock());
+
+ if (HandlerRef.GetHandler())
+ {
+ MessageLatencyTestStarted started(this);
+ started.TargetValue = Color(ts.TargetValue[0], ts.TargetValue[1], ts.TargetValue[2]);
+
+ HandlerRef.GetHandler()->OnMessage(started);
+ }
+}
+
+void LatencyTestDeviceImpl::onLatencyTestButtonMessage(LatencyTestButtonMessage* message)
+{
+ if (message->Type != LatencyTestMessage_Button)
+ return;
+
+// LatencyTestButton& s = message->Button;
+
+ // Call OnMessage() within a lock to avoid conflicts with handlers.
+ Lock::Locker scopeLock(HandlerRef.GetLock());
+
+ if (HandlerRef.GetHandler())
+ {
+ MessageLatencyTestButton button(this);
+
+ HandlerRef.GetHandler()->OnMessage(button);
+ }
+}
+
+} // namespace OVR
diff --git a/LibOVR/Src/OVR_LatencyTestImpl.h b/LibOVR/Src/OVR_LatencyTestImpl.h
new file mode 100644
index 0000000..9217f57
--- /dev/null
+++ b/LibOVR/Src/OVR_LatencyTestImpl.h
@@ -0,0 +1,135 @@
+/************************************************************************************
+
+Filename : OVR_LatencyTestImpl.h
+Content : Latency Tester specific implementation.
+Created : March 7, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_LatencyTestImpl_h
+#define OVR_LatencyTestImpl_h
+
+#include "OVR_HIDDeviceImpl.h"
+
+namespace OVR {
+
+struct LatencyTestSamplesMessage;
+struct LatencyTestButtonMessage;
+struct LatencyTestStartedMessage;
+struct LatencyTestColorDetectedMessage;
+
+//-------------------------------------------------------------------------------------
+// LatencyTestDeviceFactory enumerates Oculus Latency Tester devices.
+class LatencyTestDeviceFactory : public DeviceFactory
+{
+public:
+ static LatencyTestDeviceFactory Instance;
+
+ // Enumerates devices, creating and destroying relevant objects in manager.
+ virtual void EnumerateDevices(EnumerateVisitor& visitor);
+
+ virtual bool MatchVendorProduct(UInt16 vendorId, UInt16 productId) const;
+ virtual bool DetectHIDDevice(DeviceManager* pdevMgr, const HIDDeviceDesc& desc);
+
+protected:
+ DeviceManager* getManager() const { return (DeviceManager*) pManager; }
+};
+
+
+// Describes a single a Oculus Latency Tester device and supports creating its instance.
+class LatencyTestDeviceCreateDesc : public HIDDeviceCreateDesc
+{
+public:
+ LatencyTestDeviceCreateDesc(DeviceFactory* factory, const HIDDeviceDesc& hidDesc)
+ : HIDDeviceCreateDesc(factory, Device_LatencyTester, hidDesc) { }
+
+ virtual DeviceCreateDesc* Clone() const
+ {
+ return new LatencyTestDeviceCreateDesc(*this);
+ }
+
+ virtual DeviceBase* NewDeviceInstance();
+
+ virtual MatchResult MatchDevice(const DeviceCreateDesc& other,
+ DeviceCreateDesc**) const
+ {
+ if ((other.Type == Device_LatencyTester) && (pFactory == other.pFactory))
+ {
+ const LatencyTestDeviceCreateDesc& s2 = (const LatencyTestDeviceCreateDesc&) other;
+ if (MatchHIDDevice(s2.HIDDesc))
+ return Match_Found;
+ }
+ return Match_None;
+ }
+
+ virtual bool MatchHIDDevice(const HIDDeviceDesc& hidDesc) const
+ {
+ // should paths comparison be case insensitive?
+ return ((HIDDesc.Path.CompareNoCase(hidDesc.Path) == 0) &&
+ (HIDDesc.SerialNumber == hidDesc.SerialNumber));
+ }
+ virtual bool GetDeviceInfo(DeviceInfo* info) const;
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** OVR::LatencyTestDeviceImpl
+
+// Oculus Latency Tester interface.
+
+class LatencyTestDeviceImpl : public HIDDeviceImpl<OVR::LatencyTestDevice>
+{
+public:
+ LatencyTestDeviceImpl(LatencyTestDeviceCreateDesc* createDesc);
+ ~LatencyTestDeviceImpl();
+
+ // DeviceCommon interface.
+ virtual bool Initialize(DeviceBase* parent);
+ virtual void Shutdown();
+
+ // DeviceManagerThread::Notifier interface.
+ virtual void OnInputReport(UByte* pData, UInt32 length);
+
+ // LatencyTesterDevice interface
+ virtual bool SetConfiguration(const OVR::LatencyTestConfiguration& configuration, bool waitFlag = false);
+ virtual bool GetConfiguration(OVR::LatencyTestConfiguration* configuration);
+
+ virtual bool SetCalibrate(const OVR::LatencyTestCalibrate& calibrate, bool waitFlag = false);
+ virtual bool GetCalibrate(OVR::LatencyTestCalibrate* calibrate);
+
+ virtual bool SetStartTest(const OVR::LatencyTestStartTest& start, bool waitFlag = false);
+ virtual bool SetDisplay(const LatencyTestDisplay& display, bool waitFlag = false);
+
+protected:
+ bool openDevice(const char** errorFormatString);
+ void closeDevice();
+ void closeDeviceOnIOError();
+
+ bool initializeRead();
+ bool processReadResult();
+
+ bool setConfiguration(const OVR::LatencyTestConfiguration& configuration);
+ bool getConfiguration(OVR::LatencyTestConfiguration* configuration);
+ bool setCalibrate(const OVR::LatencyTestCalibrate& calibrate);
+ bool getCalibrate(OVR::LatencyTestCalibrate* calibrate);
+ bool setStartTest(const OVR::LatencyTestStartTest& start);
+ bool setDisplay(const OVR::LatencyTestDisplay& display);
+
+ // Called for decoded messages
+ void onLatencyTestSamplesMessage(LatencyTestSamplesMessage* message);
+ void onLatencyTestButtonMessage(LatencyTestButtonMessage* message);
+ void onLatencyTestStartedMessage(LatencyTestStartedMessage* message);
+ void onLatencyTestColorDetectedMessage(LatencyTestColorDetectedMessage* message);
+
+};
+
+} // namespace OVR
+
+#endif // OVR_LatencyTestImpl_h
diff --git a/LibOVR/Src/OVR_OSX_DeviceManager.cpp b/LibOVR/Src/OVR_OSX_DeviceManager.cpp
new file mode 100644
index 0000000..b2403ed
--- /dev/null
+++ b/LibOVR/Src/OVR_OSX_DeviceManager.cpp
@@ -0,0 +1,349 @@
+/************************************************************************************
+
+Filename : OVR_OSX_DeviceManager.cpp
+Content : OSX specific DeviceManager implementation.
+Created : March 14, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#include "OVR_OSX_DeviceManager.h"
+
+// Sensor & HMD Factories
+#include "OVR_LatencyTestImpl.h"
+#include "OVR_SensorImpl.h"
+#include "OVR_OSX_HMDDevice.h"
+#include "OVR_OSX_HIDDevice.h"
+
+#include "Kernel/OVR_Timer.h"
+#include "Kernel/OVR_Std.h"
+#include "Kernel/OVR_Log.h"
+
+#include <IOKit/hid/IOHIDManager.h>
+#include <IOKit/hid/IOHIDKeys.h>
+
+
+namespace OVR { namespace OSX {
+
+//-------------------------------------------------------------------------------------
+// **** OSX::DeviceManager
+
+DeviceManager::DeviceManager()
+{
+}
+
+DeviceManager::~DeviceManager()
+{
+ OVR_DEBUG_LOG(("OSX::DeviceManager::~DeviceManager was called"));
+}
+
+bool DeviceManager::Initialize(DeviceBase*)
+{
+ if (!DeviceManagerImpl::Initialize(0))
+ return false;
+
+ // Start the background thread.
+ pThread = *new DeviceManagerThread();
+ if (!pThread || !pThread->Start())
+ return false;
+
+ // Wait for the thread to be fully up and running.
+ pThread->StartupEvent.Wait();
+
+ // Do this now that we know the thread's run loop.
+ HidDeviceManager = *HIDDeviceManager::CreateInternal(this);
+
+ CGDisplayRegisterReconfigurationCallback(displayReconfigurationCallBack, this);
+
+ pCreateDesc->pDevice = this;
+ LogText("OVR::DeviceManager - initialized.\n");
+
+ return true;
+}
+
+void DeviceManager::Shutdown()
+{
+ LogText("OVR::DeviceManager - shutting down.\n");
+
+ CGDisplayRemoveReconfigurationCallback(displayReconfigurationCallBack, this);
+
+ // Set Manager shutdown marker variable; this prevents
+ // any existing DeviceHandle objects from accessing device.
+ pCreateDesc->pLock->pManager = 0;
+
+ // Push for thread shutdown *WITH NO WAIT*.
+ // This will have the following effect:
+ // - Exit command will get enqueued, which will be executed later on the thread itself.
+ // - Beyond this point, this DeviceManager object may be deleted by our caller.
+ // - Other commands, such as CreateDevice, may execute before ExitCommand, but they will
+ // fail gracefully due to pLock->pManager == 0. Future commands can't be enqued
+ // after pManager is null.
+ // - Once ExitCommand executes, ThreadCommand::Run loop will exit and release the last
+ // reference to the thread object.
+ pThread->Shutdown();
+ pThread.Clear();
+
+ DeviceManagerImpl::Shutdown();
+}
+
+ThreadCommandQueue* DeviceManager::GetThreadQueue()
+{
+ return pThread;
+}
+
+ThreadId DeviceManager::GetThreadId() const
+{
+ return pThread->GetThreadId();
+}
+
+bool DeviceManager::GetDeviceInfo(DeviceInfo* info) const
+{
+ if ((info->InfoClassType != Device_Manager) &&
+ (info->InfoClassType != Device_None))
+ return false;
+
+ info->Type = Device_Manager;
+ info->Version = 0;
+ OVR_strcpy(info->ProductName, DeviceInfo::MaxNameLength, "DeviceManager");
+ OVR_strcpy(info->Manufacturer,DeviceInfo::MaxNameLength, "Oculus VR, Inc.");
+ return true;
+}
+
+DeviceEnumerator<> DeviceManager::EnumerateDevicesEx(const DeviceEnumerationArgs& args)
+{
+ // TBD: Can this be avoided in the future, once proper device notification is in place?
+ pThread->PushCall((DeviceManagerImpl*)this,
+ &DeviceManager::EnumerateAllFactoryDevices, true);
+
+ return DeviceManagerImpl::EnumerateDevicesEx(args);
+}
+
+void DeviceManager::displayReconfigurationCallBack (CGDirectDisplayID display,
+ CGDisplayChangeSummaryFlags flags,
+ void *userInfo)
+{
+ DeviceManager* manager = reinterpret_cast<DeviceManager*>(userInfo);
+ OVR_UNUSED(manager);
+
+ if (flags & kCGDisplayAddFlag)
+ {
+ LogText("Display Added, id = %d\n", int(display));
+ manager->EnumerateDevices<HMDDevice>();
+ }
+ else if (flags & kCGDisplayRemoveFlag)
+ {
+ LogText("Display Removed, id = %d\n", int(display));
+ manager->EnumerateDevices<HMDDevice>();
+ }
+}
+
+//-------------------------------------------------------------------------------------
+// ***** DeviceManager Thread
+
+DeviceManagerThread::DeviceManagerThread()
+ : Thread(ThreadStackSize)
+{
+}
+
+DeviceManagerThread::~DeviceManagerThread()
+{
+}
+
+int DeviceManagerThread::Run()
+{
+
+ SetThreadName("OVR::DeviceManagerThread");
+ LogText("OVR::DeviceManagerThread - running (ThreadId=0x%p).\n", GetThreadId());
+
+ // Store out the run loop ref.
+ RunLoop = CFRunLoopGetCurrent();
+
+ // Create a 'source' to enable us to signal the run loop to process the command queue.
+ CFRunLoopSourceContext sourceContext;
+ memset(&sourceContext, 0, sizeof(sourceContext));
+ sourceContext.version = 0;
+ sourceContext.info = this;
+ sourceContext.perform = &staticCommandQueueSourceCallback;
+
+ CommandQueueSource = CFRunLoopSourceCreate(kCFAllocatorDefault, 0 , &sourceContext);
+
+ CFRunLoopAddSource(RunLoop, CommandQueueSource, kCFRunLoopDefaultMode);
+
+
+ // Signal to the parent thread that initialization has finished.
+ StartupEvent.SetEvent();
+
+
+ ThreadCommand::PopBuffer command;
+
+ while(!IsExiting())
+ {
+ // PopCommand will reset event on empty queue.
+ if (PopCommand(&command))
+ {
+ command.Execute();
+ }
+ else
+ {
+ SInt32 exitReason = 0;
+ do {
+
+ UInt32 waitMs = INT_MAX;
+
+ // If devices have time-dependent logic registered, get the longest wait
+ // allowed based on current ticks.
+ if (!TicksNotifiers.IsEmpty())
+ {
+ UInt64 ticksMks = Timer::GetTicks();
+ UInt32 waitAllowed;
+
+ for (UPInt j = 0; j < TicksNotifiers.GetSize(); j++)
+ {
+ waitAllowed = (UInt32)(TicksNotifiers[j]->OnTicks(ticksMks) / Timer::MksPerMs);
+ if (waitAllowed < waitMs)
+ waitMs = waitAllowed;
+ }
+ }
+
+ // Enter blocking run loop. We may continue until we timeout in which
+ // case it's time to service the ticks. Or if commands arrive in the command
+ // queue then the source callback will call 'CFRunLoopStop' causing this
+ // to return.
+ CFTimeInterval blockInterval = 0.001 * (double) waitMs;
+ exitReason = CFRunLoopRunInMode(kCFRunLoopDefaultMode, blockInterval, false);
+
+ if (exitReason == kCFRunLoopRunFinished)
+ {
+ // Maybe this will occur during shutdown?
+ break;
+ }
+ else if (exitReason == kCFRunLoopRunStopped )
+ {
+ // Commands need processing or we're being shutdown.
+ break;
+ }
+ else if (exitReason == kCFRunLoopRunTimedOut)
+ {
+ // Timed out so that we can service our ticks callbacks.
+ continue;
+ }
+ else if (exitReason == kCFRunLoopRunHandledSource)
+ {
+ // Should never occur since the last param when we call
+ // 'CFRunLoopRunInMode' is false.
+ OVR_ASSERT(false);
+ break;
+ }
+ else
+ {
+ OVR_ASSERT_LOG(false, ("CFRunLoopRunInMode returned unexpected code"));
+ break;
+ }
+ }
+ while(true);
+ }
+ }
+
+
+ CFRunLoopRemoveSource(RunLoop, CommandQueueSource, kCFRunLoopDefaultMode);
+ CFRelease(CommandQueueSource);
+
+ LogText("OVR::DeviceManagerThread - exiting (ThreadId=0x%p).\n", GetThreadId());
+
+ return 0;
+}
+
+void DeviceManagerThread::staticCommandQueueSourceCallback(void* pContext)
+{
+ DeviceManagerThread* pThread = (DeviceManagerThread*) pContext;
+ pThread->commandQueueSourceCallback();
+}
+
+void DeviceManagerThread::commandQueueSourceCallback()
+{
+ CFRunLoopStop(RunLoop);
+}
+
+bool DeviceManagerThread::AddTicksNotifier(Notifier* notify)
+{
+ TicksNotifiers.PushBack(notify);
+ return true;
+}
+
+bool DeviceManagerThread::RemoveTicksNotifier(Notifier* notify)
+{
+ for (UPInt i = 0; i < TicksNotifiers.GetSize(); i++)
+ {
+ if (TicksNotifiers[i] == notify)
+ {
+ TicksNotifiers.RemoveAt(i);
+ return true;
+ }
+ }
+ return false;
+}
+
+void DeviceManagerThread::Shutdown()
+{
+ // Push for thread shutdown *WITH NO WAIT*.
+ // This will have the following effect:
+ // - Exit command will get enqueued, which will be executed later on the thread itself.
+ // - Beyond this point, this DeviceManager object may be deleted by our caller.
+ // - Other commands, such as CreateDevice, may execute before ExitCommand, but they will
+ // fail gracefully due to pLock->pManager == 0. Future commands can't be enqued
+ // after pManager is null.
+ // - Once ExitCommand executes, ThreadCommand::Run loop will exit and release the last
+ // reference to the thread object.
+ PushExitCommand(false);
+
+ // make sure CFRunLoopRunInMode is woken up
+ CFRunLoopSourceSignal(CommandQueueSource);
+ CFRunLoopWakeUp(RunLoop);
+}
+
+} // namespace OSX
+
+
+//-------------------------------------------------------------------------------------
+// ***** Creation
+
+// Creates a new DeviceManager and initializes OVR.
+DeviceManager* DeviceManager::Create()
+{
+
+ if (!System::IsInitialized())
+ {
+ // Use custom message, since Log is not yet installed.
+ OVR_DEBUG_STATEMENT(Log::GetDefaultLog()->
+ LogMessage(Log_Debug, "DeviceManager::Create failed - OVR::System not initialized"); );
+ return 0;
+ }
+
+ Ptr<OSX::DeviceManager> manager = *new OSX::DeviceManager;
+
+ if (manager)
+ {
+ if (manager->Initialize(0))
+ {
+ manager->AddFactory(&LatencyTestDeviceFactory::Instance);
+ manager->AddFactory(&SensorDeviceFactory::Instance);
+ manager->AddFactory(&OSX::HMDDeviceFactory::Instance);
+
+ manager->AddRef();
+ }
+ else
+ {
+ manager.Clear();
+ }
+ }
+
+ return manager.GetPtr();
+}
+
+} // namespace OVR
diff --git a/LibOVR/Src/OVR_OSX_DeviceManager.h b/LibOVR/Src/OVR_OSX_DeviceManager.h
new file mode 100644
index 0000000..02ba661
--- /dev/null
+++ b/LibOVR/Src/OVR_OSX_DeviceManager.h
@@ -0,0 +1,119 @@
+/************************************************************************************
+
+Filename : OVR_OSX_DeviceManager.h
+Content : OSX specific DeviceManager header.
+Created : March 14, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_OSX_DeviceManager_h
+#define OVR_OSX_DeviceManager_h
+
+#include "OVR_DeviceImpl.h"
+
+#include "Kernel/OVR_Timer.h"
+
+#include <IOKit/hid/IOHIDManager.h>
+#include <CoreGraphics/CGDirectDisplay.h>
+#include <CoreGraphics/CGDisplayConfiguration.h>
+
+
+namespace OVR { namespace OSX {
+
+class DeviceManagerThread;
+
+//-------------------------------------------------------------------------------------
+// ***** OSX DeviceManager
+
+class DeviceManager : public DeviceManagerImpl
+{
+public:
+ DeviceManager();
+ ~DeviceManager();
+
+ // Initialize/Shutdown manager thread.
+ virtual bool Initialize(DeviceBase* parent);
+ virtual void Shutdown();
+
+ virtual ThreadCommandQueue* GetThreadQueue();
+ virtual ThreadId GetThreadId() const;
+
+ virtual DeviceEnumerator<> EnumerateDevicesEx(const DeviceEnumerationArgs& args);
+
+ virtual bool GetDeviceInfo(DeviceInfo* info) const;
+
+protected:
+ static void displayReconfigurationCallBack (CGDirectDisplayID display,
+ CGDisplayChangeSummaryFlags flags,
+ void *userInfo);
+
+public: // data
+ Ptr<DeviceManagerThread> pThread;
+};
+
+//-------------------------------------------------------------------------------------
+// ***** Device Manager Background Thread
+
+class DeviceManagerThread : public Thread, public ThreadCommandQueue
+{
+ friend class DeviceManager;
+ enum { ThreadStackSize = 32 * 1024 };
+public:
+ DeviceManagerThread();
+ ~DeviceManagerThread();
+
+ virtual int Run();
+
+ // ThreadCommandQueue notifications for CommandEvent handling.
+ virtual void OnPushNonEmpty_Locked()
+ {
+ CFRunLoopSourceSignal(CommandQueueSource);
+ CFRunLoopWakeUp(RunLoop);
+ }
+
+ virtual void OnPopEmpty_Locked() {}
+
+
+ // Notifier used for different updates (EVENT or regular timing or messages).
+ class Notifier
+ {
+ public:
+
+ // Called when timing ticks are updated. // Returns the largest number of microseconds
+ // this function can wait till next call.
+ virtual UInt64 OnTicks(UInt64 ticksMks)
+ { OVR_UNUSED1(ticksMks); return Timer::MksPerSecond * 1000; }
+ };
+
+ // Add notifier that will be called at regular intervals.
+ bool AddTicksNotifier(Notifier* notify);
+ bool RemoveTicksNotifier(Notifier* notify);
+
+ CFRunLoopRef GetRunLoop()
+ { return RunLoop; }
+
+ void Shutdown();
+private:
+ CFRunLoopRef RunLoop;
+
+ CFRunLoopSourceRef CommandQueueSource;
+
+ static void staticCommandQueueSourceCallback(void* pContext);
+ void commandQueueSourceCallback();
+
+ Event StartupEvent;
+
+ // Ticks notifiers. Used for time-dependent events such as keep-alive.
+ Array<Notifier*> TicksNotifiers;
+};
+
+}} // namespace OSX::OVR
+
+#endif // OVR_OSX_DeviceManager_h
diff --git a/LibOVR/Src/OVR_OSX_HIDDevice.cpp b/LibOVR/Src/OVR_OSX_HIDDevice.cpp
new file mode 100644
index 0000000..40b63c9
--- /dev/null
+++ b/LibOVR/Src/OVR_OSX_HIDDevice.cpp
@@ -0,0 +1,899 @@
+/************************************************************************************
+Filename : OVR_OSX_HIDDevice.cpp
+Content : OSX HID device implementation.
+Created : February 26, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#include "OVR_OSX_HIDDevice.h"
+
+#include <IOKit/usb/IOUSBLib.h>
+
+namespace OVR { namespace OSX {
+
+static const UInt32 MAX_QUEUED_INPUT_REPORTS = 5;
+
+//-------------------------------------------------------------------------------------
+// **** OSX::DeviceManager
+
+HIDDeviceManager::HIDDeviceManager(DeviceManager* manager)
+ : DevManager(manager)
+{
+ HIDManager = NULL;
+}
+
+HIDDeviceManager::~HIDDeviceManager()
+{
+}
+
+CFRunLoopRef HIDDeviceManager::getRunLoop()
+{
+ if (DevManager != NULL)
+ {
+ return DevManager->pThread->GetRunLoop();
+ }
+
+ return CFRunLoopGetCurrent();
+}
+
+bool HIDDeviceManager::initializeManager()
+{
+ if (HIDManager != NULL)
+ {
+ return true;
+ }
+
+ HIDManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
+
+ if (!HIDManager)
+ {
+ return false;
+ }
+
+ // Create a Matching Dictionary
+ CFMutableDictionaryRef matchDict =
+ CFDictionaryCreateMutable(kCFAllocatorDefault,
+ 2,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+
+ // Specify a device manufacturer in the Matching Dictionary
+ UInt32 vendorId = Oculus_VendorId;
+ CFNumberRef vendorIdRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &vendorId);
+ CFDictionarySetValue(matchDict,
+ CFSTR(kIOHIDVendorIDKey),
+ vendorIdRef);
+ // Register the Matching Dictionary to the HID Manager
+ IOHIDManagerSetDeviceMatching(HIDManager, matchDict);
+ CFRelease(vendorIdRef);
+ CFRelease(matchDict);
+
+ // Register a callback for USB device detection with the HID Manager
+ IOHIDManagerRegisterDeviceMatchingCallback(HIDManager, &staticDeviceMatchingCallback, this);
+
+ IOHIDManagerScheduleWithRunLoop(HIDManager, getRunLoop(), kCFRunLoopDefaultMode);
+
+ return true;
+}
+
+bool HIDDeviceManager::Initialize()
+{
+ return initializeManager();
+}
+
+void HIDDeviceManager::Shutdown()
+{
+ OVR_ASSERT_LOG(HIDManager, ("Should have called 'Initialize' before 'Shutdown'."));
+ CFRelease(HIDManager);
+
+ LogText("OVR::OSX::HIDDeviceManager - shutting down.\n");
+}
+
+bool HIDDeviceManager::getIntProperty(IOHIDDeviceRef device, CFStringRef propertyName, SInt32* pResult)
+{
+
+ CFTypeRef ref = IOHIDDeviceGetProperty(device, propertyName);
+
+ if (!ref)
+ {
+ return false;
+ }
+
+ if (CFGetTypeID(ref) != CFNumberGetTypeID())
+ {
+ return false;
+ }
+
+ CFNumberGetValue((CFNumberRef) ref, kCFNumberSInt32Type, pResult);
+
+ return true;
+}
+
+bool HIDDeviceManager::initVendorProductVersion(IOHIDDeviceRef device, HIDDeviceDesc* pDevDesc)
+{
+
+ if (!getVendorId(device, &(pDevDesc->VendorId)))
+ {
+ return false;
+ }
+
+ if (!getProductId(device, &(pDevDesc->ProductId)))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool HIDDeviceManager::initUsage(IOHIDDeviceRef device, HIDDeviceDesc* pDevDesc)
+{
+
+ SInt32 result;
+
+ if (!getIntProperty(device, CFSTR(kIOHIDPrimaryUsagePageKey), &result))
+ {
+ return false;
+ }
+
+ pDevDesc->UsagePage = result;
+
+
+ if (!getIntProperty(device, CFSTR(kIOHIDPrimaryUsageKey), &result))
+ {
+ return false;
+ }
+
+ pDevDesc->Usage = result;
+
+ return true;
+}
+
+bool HIDDeviceManager::initSerialNumber(IOHIDDeviceRef device, HIDDeviceDesc* pDevDesc)
+{
+ return getSerialNumberString(device, &(pDevDesc->SerialNumber));
+}
+
+bool HIDDeviceManager::initStrings(IOHIDDeviceRef device, HIDDeviceDesc* pDevDesc)
+{
+
+ // Regardless of whether they fail we'll try and get the remaining.
+ getStringProperty(device, CFSTR(kIOHIDManufacturerKey), &(pDevDesc->Manufacturer));
+ getStringProperty(device, CFSTR(kIOHIDProductKey), &(pDevDesc->Product));
+
+ return true;
+}
+
+bool HIDDeviceManager::getStringProperty(IOHIDDeviceRef device,
+ CFStringRef propertyName,
+ String* pResult)
+{
+
+ CFStringRef str = (CFStringRef) IOHIDDeviceGetProperty(device, propertyName);
+
+ if (!str)
+ {
+ return false;
+ }
+
+ CFIndex length = CFStringGetLength(str);
+ CFRange range = CFRangeMake(0, length);
+
+ // Test the conversion first to get required buffer size.
+ CFIndex bufferLength;
+ CFIndex numberOfChars = CFStringGetBytes(str,
+ range,
+ kCFStringEncodingUTF8,
+ (char) '?',
+ FALSE,
+ NULL,
+ 0,
+ &bufferLength);
+
+ if (numberOfChars == 0)
+ {
+ return false;
+ }
+
+ // Now allocate buffer.
+ char* buffer = new char[bufferLength+1];
+
+ numberOfChars = CFStringGetBytes(str,
+ range,
+ kCFStringEncodingUTF8,
+ (char) '?',
+ FALSE,
+ (UInt8*) buffer,
+ bufferLength,
+ NULL);
+ OVR_ASSERT_LOG(numberOfChars != 0, ("CFStringGetBytes failed."));
+
+ buffer[bufferLength] = '\0';
+ *pResult = String(buffer);
+
+ return true;
+}
+
+bool HIDDeviceManager::getVendorId(IOHIDDeviceRef device, UInt16* pResult)
+{
+ SInt32 result;
+
+ if (!getIntProperty(device, CFSTR(kIOHIDVendorIDKey), &result))
+ {
+ return false;
+ }
+
+ *pResult = result;
+
+ return true;
+}
+
+bool HIDDeviceManager::getProductId(IOHIDDeviceRef device, UInt16* pResult)
+{
+ SInt32 result;
+
+ if (!getIntProperty(device, CFSTR(kIOHIDProductIDKey), &result))
+ {
+ return false;
+ }
+
+ *pResult = result;
+
+ return true;
+}
+
+bool HIDDeviceManager::getLocationId(IOHIDDeviceRef device, SInt32* pResult)
+{
+ SInt32 result;
+
+ if (!getIntProperty(device, CFSTR(kIOHIDLocationIDKey), &result))
+ {
+ return false;
+ }
+
+ *pResult = result;
+
+ return true;
+}
+
+bool HIDDeviceManager::getSerialNumberString(IOHIDDeviceRef device, String* pResult)
+{
+
+ if (!getStringProperty(device, CFSTR(kIOHIDSerialNumberKey), pResult))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool HIDDeviceManager::getPath(IOHIDDeviceRef device, String* pPath)
+{
+
+ String transport;
+ if (!getStringProperty(device, CFSTR(kIOHIDTransportKey), &transport))
+ {
+ return false;
+ }
+
+ UInt16 vendorId;
+ if (!getVendorId(device, &vendorId))
+ {
+ return false;
+ }
+
+ UInt16 productId;
+ if (!getProductId(device, &productId))
+ {
+ return false;
+ }
+
+ String serialNumber;
+ if (!getSerialNumberString(device, &serialNumber))
+ {
+ return false;
+ }
+
+
+ StringBuffer buffer;
+ buffer.AppendFormat("%s:vid=%04hx:pid=%04hx:ser=%s",
+ transport.ToCStr(),
+ vendorId,
+ productId,
+ serialNumber.ToCStr());
+
+ *pPath = String(buffer);
+
+ return true;
+}
+
+bool HIDDeviceManager::Enumerate(HIDEnumerateVisitor* enumVisitor)
+{
+ if (!initializeManager())
+ {
+ return false;
+ }
+
+
+ CFSetRef deviceSet = IOHIDManagerCopyDevices(HIDManager);
+ if (!deviceSet)
+ return false;
+
+ CFIndex deviceCount = CFSetGetCount(deviceSet);
+
+ // Allocate a block of memory and read the set into it.
+ IOHIDDeviceRef* devices = (IOHIDDeviceRef*) OVR_ALLOC(sizeof(IOHIDDeviceRef) * deviceCount);
+ CFSetGetValues(deviceSet, (const void **) devices);
+
+
+ // Iterate over devices.
+ for (CFIndex deviceIndex = 0; deviceIndex < deviceCount; deviceIndex++)
+ {
+ IOHIDDeviceRef hidDev = devices[deviceIndex];
+
+ if (!hidDev)
+ {
+ continue;
+ }
+
+ HIDDeviceDesc devDesc;
+
+ if (getPath(hidDev, &(devDesc.Path)) &&
+ initVendorProductVersion(hidDev, &devDesc) &&
+ enumVisitor->MatchVendorProduct(devDesc.VendorId, devDesc.ProductId) &&
+ initUsage(hidDev, &devDesc))
+ {
+ initStrings(hidDev, &devDesc);
+ initSerialNumber(hidDev, &devDesc);
+
+ // Look for the device to check if it is already opened.
+ Ptr<DeviceCreateDesc> existingDevice = DevManager->FindHIDDevice(devDesc);
+ // if device exists and it is opened then most likely the CreateHIDFile
+ // will fail; therefore, we just set Enumerated to 'true' and continue.
+ if (existingDevice && existingDevice->pDevice)
+ {
+ existingDevice->Enumerated = true;
+ continue;
+ }
+
+ // Construct minimal device that the visitor callback can get feature reports from.
+ OSX::HIDDevice device(this, hidDev);
+
+ enumVisitor->Visit(device, devDesc);
+ }
+ }
+
+ OVR_FREE(devices);
+ CFRelease(deviceSet);
+
+ return true;
+}
+
+OVR::HIDDevice* HIDDeviceManager::Open(const String& path)
+{
+
+ Ptr<OSX::HIDDevice> device = *new OSX::HIDDevice(this);
+
+ if (!device->HIDInitialize(path))
+ {
+ return NULL;
+ }
+
+ device->AddRef();
+
+ return device;
+}
+
+bool HIDDeviceManager::getFullDesc(IOHIDDeviceRef device, HIDDeviceDesc* desc)
+{
+
+ if (!initVendorProductVersion(device, desc))
+ {
+ return false;
+ }
+
+ if (!initUsage(device, desc))
+ {
+ return false;
+ }
+
+ if (!initSerialNumber(device, desc))
+ {
+ return false;
+ }
+
+ initStrings(device, desc);
+
+ return true;
+}
+
+// New USB device specified in the matching dictionary has been added (callback function)
+void HIDDeviceManager::staticDeviceMatchingCallback(void *inContext,
+ IOReturn inResult,
+ void *inSender,
+ IOHIDDeviceRef inIOHIDDeviceRef)
+{
+ HIDDeviceManager* hidMgr = static_cast<HIDDeviceManager*>(inContext);
+ HIDDeviceDesc hidDevDesc;
+ hidMgr->getPath(inIOHIDDeviceRef, &hidDevDesc.Path);
+ hidMgr->getFullDesc(inIOHIDDeviceRef, &hidDevDesc);
+
+ hidMgr->DevManager->DetectHIDDevice(hidDevDesc);
+}
+
+//-------------------------------------------------------------------------------------
+// **** OSX::HIDDevice
+
+HIDDevice::HIDDevice(HIDDeviceManager* manager)
+ : HIDManager(manager), InMinimalMode(false)
+{
+ Device = NULL;
+ RepluggedNotificationPort = 0;
+}
+
+// This is a minimal constructor used during enumeration for us to pass
+// a HIDDevice to the visit function (so that it can query feature reports).
+HIDDevice::HIDDevice(HIDDeviceManager* manager, IOHIDDeviceRef device)
+: HIDManager(manager), Device(device), InMinimalMode(true)
+{
+ RepluggedNotificationPort = 0;
+}
+
+HIDDevice::~HIDDevice()
+{
+ if (!InMinimalMode)
+ {
+ HIDShutdown();
+ }
+}
+
+bool HIDDevice::HIDInitialize(const String& path)
+{
+
+ DevDesc.Path = path;
+
+ if (!openDevice())
+ {
+ LogText("OVR::OSX::HIDDevice - Failed to open HIDDevice: %s", path.ToCStr());
+ return false;
+ }
+
+ // Setup notification for when a device is unplugged and plugged back in.
+ if (!setupDevicePluggedInNotification())
+ {
+ LogText("OVR::OSX::HIDDevice - Failed to setup notification for when device plugged back in.");
+ closeDevice(false);
+ return false;
+ }
+
+ HIDManager->DevManager->pThread->AddTicksNotifier(this);
+
+
+ LogText("OVR::OSX::HIDDevice - Opened '%s'\n"
+ " Manufacturer:'%s' Product:'%s' Serial#:'%s'\n",
+ DevDesc.Path.ToCStr(),
+ DevDesc.Manufacturer.ToCStr(), DevDesc.Product.ToCStr(),
+ DevDesc.SerialNumber.ToCStr());
+
+ return true;
+}
+
+bool HIDDevice::initInfo()
+{
+ // Device must have been successfully opened.
+ OVR_ASSERT(Device);
+
+
+ // Get report lengths.
+ SInt32 bufferLength;
+ bool getResult = HIDManager->getIntProperty(Device, CFSTR(kIOHIDMaxInputReportSizeKey), &bufferLength);
+ OVR_ASSERT(getResult);
+ InputReportBufferLength = (UInt16) bufferLength;
+
+ getResult = HIDManager->getIntProperty(Device, CFSTR(kIOHIDMaxOutputReportSizeKey), &bufferLength);
+ OVR_ASSERT(getResult);
+ OutputReportBufferLength = (UInt16) bufferLength;
+
+ getResult = HIDManager->getIntProperty(Device, CFSTR(kIOHIDMaxFeatureReportSizeKey), &bufferLength);
+ OVR_ASSERT(getResult);
+ FeatureReportBufferLength = (UInt16) bufferLength;
+
+
+ if (ReadBufferSize < InputReportBufferLength)
+ {
+ OVR_ASSERT_LOG(false, ("Input report buffer length is bigger than read buffer."));
+ return false;
+ }
+
+ // Get device desc.
+ if (!HIDManager->getFullDesc(Device, &DevDesc))
+ {
+ OVR_ASSERT_LOG(false, ("Failed to get device desc while initializing device."));
+ return false;
+ }
+
+ return true;
+}
+
+void HIDDevice::staticDeviceAddedCallback(void* pContext, io_iterator_t iterator)
+{
+ HIDDevice* pDevice = (HIDDevice*) pContext;
+ pDevice->deviceAddedCallback(iterator);
+}
+
+void HIDDevice::deviceAddedCallback(io_iterator_t iterator)
+{
+
+ if (Device == NULL)
+ {
+ if (openDevice())
+ {
+ LogText("OVR::OSX::HIDDevice - Reopened device : %s", DevDesc.Path.ToCStr());
+
+ Ptr<DeviceCreateDesc> existingHIDDev = HIDManager->DevManager->FindHIDDevice(DevDesc);
+ if (existingHIDDev && existingHIDDev->pDevice)
+ {
+ HIDManager->DevManager->CallOnDeviceAdded(existingHIDDev);
+ }
+ }
+ }
+
+ // Reset callback.
+ while (IOIteratorNext(iterator))
+ ;
+}
+
+bool HIDDevice::openDevice()
+{
+
+ // Have to iterate through devices again to generate paths.
+ CFSetRef deviceSet = IOHIDManagerCopyDevices(HIDManager->HIDManager);
+ CFIndex deviceCount = CFSetGetCount(deviceSet);
+
+ // Allocate a block of memory and read the set into it.
+ IOHIDDeviceRef* devices = (IOHIDDeviceRef*) OVR_ALLOC(sizeof(IOHIDDeviceRef) * deviceCount);
+ CFSetGetValues(deviceSet, (const void **) devices);
+
+
+ // Iterate over devices.
+ IOHIDDeviceRef device = NULL;
+
+ for (CFIndex deviceIndex = 0; deviceIndex < deviceCount; deviceIndex++)
+ {
+ IOHIDDeviceRef tmpDevice = devices[deviceIndex];
+
+ if (!tmpDevice)
+ {
+ continue;
+ }
+
+ String path;
+ if (!HIDManager->getPath(tmpDevice, &path))
+ {
+ continue;
+ }
+
+ if (path == DevDesc.Path)
+ {
+ device = tmpDevice;
+ break;
+ }
+ }
+
+
+ OVR_FREE(devices);
+
+ if (!device)
+ {
+ CFRelease(deviceSet);
+ return false;
+ }
+
+ // Attempt to open device.
+ if (IOHIDDeviceOpen(device, kIOHIDOptionsTypeSeizeDevice)
+ != kIOReturnSuccess)
+ {
+ CFRelease(deviceSet);
+ return false;
+ }
+
+ // Retain the device before we release the set.
+ CFRetain(device);
+ CFRelease(deviceSet);
+
+
+ Device = device;
+
+
+ if (!initInfo())
+ {
+ IOHIDDeviceClose(Device, kIOHIDOptionsTypeSeizeDevice);
+ CFRelease(Device);
+ Device = NULL;
+ return false;
+ }
+
+
+ // Setup the Run Loop and callbacks.
+ IOHIDDeviceScheduleWithRunLoop(Device,
+ HIDManager->getRunLoop(),
+ kCFRunLoopDefaultMode);
+
+ IOHIDDeviceRegisterInputReportCallback(Device,
+ ReadBuffer,
+ ReadBufferSize,
+ staticHIDReportCallback,
+ this);
+
+ IOHIDDeviceRegisterRemovalCallback(Device,
+ staticDeviceRemovedCallback,
+ this);
+
+ return true;
+}
+
+void HIDDevice::HIDShutdown()
+{
+
+ HIDManager->DevManager->pThread->RemoveTicksNotifier(this);
+
+ if (Device != NULL) // Device may already have been closed if unplugged.
+ {
+ closeDevice(false);
+ }
+
+ IOObjectRelease(RepluggedNotification);
+ if (RepluggedNotificationPort)
+ IONotificationPortDestroy(RepluggedNotificationPort);
+
+ LogText("OVR::OSX::HIDDevice - HIDShutdown '%s'\n", DevDesc.Path.ToCStr());
+}
+
+bool HIDDevice::setupDevicePluggedInNotification()
+{
+
+ // Setup notification when devices are plugged in.
+ RepluggedNotificationPort = IONotificationPortCreate(kIOMasterPortDefault);
+
+ CFRunLoopSourceRef notificationRunLoopSource =
+ IONotificationPortGetRunLoopSource(RepluggedNotificationPort);
+
+ CFRunLoopAddSource(HIDManager->getRunLoop(),
+ notificationRunLoopSource,
+ kCFRunLoopDefaultMode);
+
+ CFMutableDictionaryRef matchingDict = IOServiceMatching(kIOUSBDeviceClassName);
+
+ // Have to specify vendorId and productId. Doesn't seem to accept additional
+ // things like serial number.
+ SInt32 vendorId = DevDesc.VendorId;
+ CFNumberRef numberRef = CFNumberCreate(kCFAllocatorDefault,
+ kCFNumberSInt32Type,
+ &vendorId);
+ CFDictionarySetValue(matchingDict, CFSTR(kUSBVendorID), numberRef);
+ CFRelease(numberRef);
+
+ SInt32 deviceProductId = DevDesc.ProductId;
+ numberRef = CFNumberCreate(kCFAllocatorDefault,
+ kCFNumberSInt32Type,
+ &deviceProductId);
+ CFDictionarySetValue(matchingDict, CFSTR(kUSBProductID), numberRef);
+ CFRelease(numberRef);
+
+ kern_return_t result =
+ IOServiceAddMatchingNotification(RepluggedNotificationPort,
+ kIOMatchedNotification,
+ matchingDict,
+ staticDeviceAddedCallback,
+ this,
+ &RepluggedNotification);
+
+ if (result != KERN_SUCCESS)
+ {
+ CFRelease(RepluggedNotificationPort);
+ RepluggedNotificationPort = 0;
+ return false;
+ }
+
+ // Iterate through to arm.
+ while (IOIteratorNext(RepluggedNotification))
+ {
+ }
+
+ return true;
+}
+
+void HIDDevice::closeDevice(bool wasUnplugged)
+{
+ OVR_ASSERT(Device != NULL);
+
+ if (!wasUnplugged)
+ {
+ // Clear the registered callbacks.
+ IOHIDDeviceRegisterInputReportCallback(Device,
+ ReadBuffer,
+ InputReportBufferLength,
+ NULL,
+ this);
+
+ IOHIDDeviceRegisterRemovalCallback(Device, NULL, this);
+
+ IOHIDDeviceUnscheduleFromRunLoop(Device,
+ HIDManager->getRunLoop(),
+ kCFRunLoopDefaultMode);
+ IOHIDDeviceClose(Device, kIOHIDOptionsTypeNone);
+ }
+
+ CFRelease(Device);
+ Device = NULL;
+
+ LogText("OVR::OSX::HIDDevice - HID Device Closed '%s'\n", DevDesc.Path.ToCStr());
+}
+
+void HIDDevice::staticHIDReportCallback(void* pContext,
+ IOReturn result,
+ void* pSender,
+ IOHIDReportType reportType,
+ uint32_t reportId,
+ uint8_t* pReport,
+ CFIndex reportLength)
+{
+ HIDDevice* pDevice = (HIDDevice*) pContext;
+ return pDevice->hidReportCallback(pReport, (UInt32)reportLength);
+}
+
+void HIDDevice::hidReportCallback(UByte* pData, UInt32 length)
+{
+
+ // We got data.
+ if (Handler)
+ {
+ Handler->OnInputReport(pData, length);
+ }
+}
+
+void HIDDevice::staticDeviceRemovedCallback(void* pContext, IOReturn result, void* pSender)
+{
+ HIDDevice* pDevice = (HIDDevice*) pContext;
+ pDevice->deviceRemovedCallback();
+}
+
+void HIDDevice::deviceRemovedCallback()
+{
+ Ptr<HIDDevice> _this(this); // prevent from release
+
+ Ptr<DeviceCreateDesc> existingHIDDev = HIDManager->DevManager->FindHIDDevice(DevDesc);
+ if (existingHIDDev && existingHIDDev->pDevice)
+ {
+ HIDManager->DevManager->CallOnDeviceRemoved(existingHIDDev);
+ }
+ closeDevice(true);
+}
+
+CFStringRef HIDDevice::generateRunLoopModeString(IOHIDDeviceRef device)
+{
+ const UInt32 safeBuffSize = 256;
+ char nameBuff[safeBuffSize];
+ OVR_sprintf(nameBuff, safeBuffSize, "%016lX", device);
+
+ return CFStringCreateWithCString(NULL, nameBuff, kCFStringEncodingASCII);
+}
+
+bool HIDDevice::SetFeatureReport(UByte* data, UInt32 length)
+{
+
+ if (!Device)
+ return false;
+
+ UByte reportID = data[0];
+
+ if (reportID == 0)
+ {
+ // Not using reports so remove from data packet.
+ data++;
+ length--;
+ }
+
+ IOReturn result = IOHIDDeviceSetReport( Device,
+ kIOHIDReportTypeFeature,
+ reportID,
+ data,
+ length);
+
+ return (result == kIOReturnSuccess);
+}
+
+bool HIDDevice::GetFeatureReport(UByte* data, UInt32 length)
+{
+ if (!Device)
+ return false;
+
+ CFIndex bufferLength = length;
+
+ // Report id is in first byte of the buffer.
+ IOReturn result = IOHIDDeviceGetReport(Device, kIOHIDReportTypeFeature, data[0], data, &bufferLength);
+
+ return (result == kIOReturnSuccess);
+}
+
+UInt64 HIDDevice::OnTicks(UInt64 ticksMks)
+{
+
+ if (Handler)
+ {
+ return Handler->OnTicks(ticksMks);
+ }
+
+ return DeviceManagerThread::Notifier::OnTicks(ticksMks);
+}
+
+HIDDeviceManager* HIDDeviceManager::CreateInternal(OSX::DeviceManager* devManager)
+{
+
+ if (!System::IsInitialized())
+ {
+ // Use custom message, since Log is not yet installed.
+ OVR_DEBUG_STATEMENT(Log::GetDefaultLog()->
+ LogMessage(Log_Debug, "HIDDeviceManager::Create failed - OVR::System not initialized"); );
+ return 0;
+ }
+
+ Ptr<OSX::HIDDeviceManager> manager = *new OSX::HIDDeviceManager(devManager);
+
+ if (manager)
+ {
+ if (manager->Initialize())
+ {
+ manager->AddRef();
+ }
+ else
+ {
+ manager.Clear();
+ }
+ }
+
+ return manager.GetPtr();
+}
+
+} // namespace OSX
+
+//-------------------------------------------------------------------------------------
+// ***** Creation
+
+// Creates a new HIDDeviceManager and initializes OVR.
+HIDDeviceManager* HIDDeviceManager::Create()
+{
+ OVR_ASSERT_LOG(false, ("Standalone mode not implemented yet."));
+
+ if (!System::IsInitialized())
+ {
+ // Use custom message, since Log is not yet installed.
+ OVR_DEBUG_STATEMENT(Log::GetDefaultLog()->
+ LogMessage(Log_Debug, "HIDDeviceManager::Create failed - OVR::System not initialized"); );
+ return 0;
+ }
+
+ Ptr<OSX::HIDDeviceManager> manager = *new OSX::HIDDeviceManager(NULL);
+
+ if (manager)
+ {
+ if (manager->Initialize())
+ {
+ manager->AddRef();
+ }
+ else
+ {
+ manager.Clear();
+ }
+ }
+
+ return manager.GetPtr();
+}
+
+} // namespace OVR
diff --git a/LibOVR/Src/OVR_OSX_HIDDevice.h b/LibOVR/Src/OVR_OSX_HIDDevice.h
new file mode 100644
index 0000000..b9b3fc5
--- /dev/null
+++ b/LibOVR/Src/OVR_OSX_HIDDevice.h
@@ -0,0 +1,149 @@
+/************************************************************************************
+Filename : OVR_OSX_HIDDevice.h
+Content : OSX HID device implementation.
+Created : February 26, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_OSX_HIDDevice_h
+#define OVR_OSX_HIDDevice_h
+
+#include "OVR_HIDDevice.h"
+
+#include "OVR_OSX_DeviceManager.h"
+
+#include <IOKit/IOKitLib.h>
+
+namespace OVR { namespace OSX {
+
+class HIDDeviceManager;
+
+//-------------------------------------------------------------------------------------
+// ***** OSX HIDDevice
+
+class HIDDevice : public OVR::HIDDevice, public DeviceManagerThread::Notifier
+{
+private:
+ friend class HIDDeviceManager;
+
+public:
+ HIDDevice(HIDDeviceManager* manager);
+
+ // This is a minimal constructor used during enumeration for us to pass
+ // a HIDDevice to the visit function (so that it can query feature reports).
+ HIDDevice(HIDDeviceManager* manager, IOHIDDeviceRef device);
+
+ virtual ~HIDDevice();
+
+ bool HIDInitialize(const String& path);
+ void HIDShutdown();
+
+ virtual bool SetFeatureReport(UByte* data, UInt32 length);
+ virtual bool GetFeatureReport(UByte* data, UInt32 length);
+
+ bool Write(UByte* data, UInt32 length);
+
+ bool Read(UByte* pData, UInt32 length, UInt32 timeoutMilliS);
+ bool ReadBlocking(UByte* pData, UInt32 length);
+
+
+ // DeviceManagerThread::Notifier
+ UInt64 OnTicks(UInt64 ticksMks);
+
+private:
+ bool initInfo();
+ bool openDevice();
+ void closeDevice(bool wasUnplugged);
+ bool setupDevicePluggedInNotification();
+ CFStringRef generateRunLoopModeString(IOHIDDeviceRef device);
+
+ static void staticHIDReportCallback(void* pContext,
+ IOReturn result,
+ void* pSender,
+ IOHIDReportType reportType,
+ uint32_t reportId,
+ uint8_t* pReport,
+ CFIndex reportLength);
+ void hidReportCallback(UByte* pData, UInt32 length);
+
+ static void staticDeviceRemovedCallback(void* pContext,
+ IOReturn result,
+ void* pSender);
+ void deviceRemovedCallback();
+
+ static void staticDeviceAddedCallback(void* pContext,
+ io_iterator_t iterator);
+ void deviceAddedCallback(io_iterator_t iterator);
+
+ bool InMinimalMode;
+ HIDDeviceManager* HIDManager;
+ IOHIDDeviceRef Device;
+ HIDDeviceDesc DevDesc;
+
+ enum { ReadBufferSize = 96 };
+ UByte ReadBuffer[ReadBufferSize];
+
+ UInt16 InputReportBufferLength;
+ UInt16 OutputReportBufferLength;
+ UInt16 FeatureReportBufferLength;
+
+ IONotificationPortRef RepluggedNotificationPort;
+ io_iterator_t RepluggedNotification;
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** OSX HIDDeviceManager
+
+class HIDDeviceManager : public OVR::HIDDeviceManager
+{
+ friend class HIDDevice;
+
+public:
+ HIDDeviceManager(OSX::DeviceManager* Manager);
+ virtual ~HIDDeviceManager();
+
+ virtual bool Initialize();
+ virtual void Shutdown();
+
+ virtual bool Enumerate(HIDEnumerateVisitor* enumVisitor);
+ virtual OVR::HIDDevice* Open(const String& path);
+
+ static HIDDeviceManager* CreateInternal(DeviceManager* manager);
+
+private:
+ CFRunLoopRef getRunLoop();
+ bool initializeManager();
+ bool initVendorProductVersion(IOHIDDeviceRef device, HIDDeviceDesc* pDevDesc);
+ bool initUsage(IOHIDDeviceRef device, HIDDeviceDesc* pDevDesc);
+ bool initStrings(IOHIDDeviceRef device, HIDDeviceDesc* pDevDesc);
+ bool initSerialNumber(IOHIDDeviceRef device, HIDDeviceDesc* pDevDesc);
+ bool getVendorId(IOHIDDeviceRef device, UInt16* pResult);
+ bool getProductId(IOHIDDeviceRef device, UInt16* pResult);
+ bool getLocationId(IOHIDDeviceRef device, SInt32* pResult);
+ bool getSerialNumberString(IOHIDDeviceRef device, String* pResult);
+ bool getPath(IOHIDDeviceRef device, String* pPath);
+ bool getIntProperty(IOHIDDeviceRef device, CFStringRef key, int32_t* pResult);
+ bool getStringProperty(IOHIDDeviceRef device, CFStringRef propertyName, String* pResult);
+ bool getFullDesc(IOHIDDeviceRef device, HIDDeviceDesc* desc);
+
+ static void staticDeviceMatchingCallback(void *inContext,
+ IOReturn inResult,
+ void *inSender,
+ IOHIDDeviceRef inIOHIDDeviceRef);
+
+ DeviceManager* DevManager;
+
+ IOHIDManagerRef HIDManager;
+};
+
+}} // namespace OVR::OSX
+
+#endif // OVR_OSX_HIDDevice_h
diff --git a/LibOVR/Src/OVR_OSX_HMDDevice.cpp b/LibOVR/Src/OVR_OSX_HMDDevice.cpp
new file mode 100644
index 0000000..df9003d
--- /dev/null
+++ b/LibOVR/Src/OVR_OSX_HMDDevice.cpp
@@ -0,0 +1,326 @@
+/************************************************************************************
+
+Filename : OVR_OSX_HMDDevice.cpp
+Content : OSX Interface to HMD - detects HMD display
+Created : September 21, 2012
+Authors : Michael Antonov
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#include "OVR_OSX_HMDDevice.h"
+#include <CoreGraphics/CGDirectDisplay.h>
+#include <CoreGraphics/CGDisplayConfiguration.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <CoreFoundation/CFString.h>
+#include <IOKit/graphics/IOGraphicsLib.h>
+
+namespace OVR { namespace OSX {
+
+//-------------------------------------------------------------------------------------
+
+HMDDeviceCreateDesc::HMDDeviceCreateDesc(DeviceFactory* factory,
+ UInt32 vend, UInt32 prod, const String& displayDeviceName, long dispId)
+ : DeviceCreateDesc(factory, Device_HMD),
+ DisplayDeviceName(displayDeviceName),
+ DesktopX(0), DesktopY(0), Contents(0),
+ HResolution(0), VResolution(0), HScreenSize(0), VScreenSize(0),
+ DisplayId(dispId)
+{
+ /* //??????????
+ char idstring[9];
+ idstring[0] = 'A'-1+((vend>>10) & 31);
+ idstring[1] = 'A'-1+((vend>>5) & 31);
+ idstring[2] = 'A'-1+((vend>>0) & 31);
+ snprintf(idstring+3, 5, "%04d", prod);
+ DeviceId = idstring;*/
+ DeviceId = DisplayDeviceName;
+}
+
+HMDDeviceCreateDesc::HMDDeviceCreateDesc(const HMDDeviceCreateDesc& other)
+ : DeviceCreateDesc(other.pFactory, Device_HMD),
+ DeviceId(other.DeviceId), DisplayDeviceName(other.DisplayDeviceName),
+ DesktopX(other.DesktopX), DesktopY(other.DesktopY), Contents(other.Contents),
+ HResolution(other.HResolution), VResolution(other.VResolution),
+ HScreenSize(other.HScreenSize), VScreenSize(other.VScreenSize),
+ DisplayId(other.DisplayId)
+{
+}
+
+HMDDeviceCreateDesc::MatchResult HMDDeviceCreateDesc::MatchDevice(const DeviceCreateDesc& other,
+ DeviceCreateDesc** pcandidate) const
+{
+ if ((other.Type != Device_HMD) || (other.pFactory != pFactory))
+ return Match_None;
+
+ // There are several reasons we can come in here:
+ // a) Matching this HMD Monitor created desc to OTHER HMD Monitor desc
+ // - Require exact device DeviceId/DeviceName match
+ // b) Matching SensorDisplayInfo created desc to OTHER HMD Monitor desc
+ // - This DeviceId is empty; becomes candidate
+ // c) Matching this HMD Monitor created desc to SensorDisplayInfo desc
+ // - This other.DeviceId is empty; becomes candidate
+
+ const HMDDeviceCreateDesc& s2 = (const HMDDeviceCreateDesc&) other;
+
+ if ((DeviceId == s2.DeviceId) &&
+ (DisplayId == s2.DisplayId))
+ {
+ // Non-null DeviceId may match while size is different if screen size was overwritten
+ // by SensorDisplayInfo in prior iteration.
+ if (!DeviceId.IsEmpty() ||
+ ((HScreenSize == s2.HScreenSize) &&
+ (VScreenSize == s2.VScreenSize)) )
+ {
+ *pcandidate = 0;
+ return Match_Found;
+ }
+ }
+
+
+ // DisplayInfo takes precedence, although we try to match it first.
+ if ((HResolution == s2.HResolution) &&
+ (VResolution == s2.VResolution) &&
+ (HScreenSize == s2.HScreenSize) &&
+ (VScreenSize == s2.VScreenSize))
+ {
+ if (DeviceId.IsEmpty() && !s2.DeviceId.IsEmpty())
+ {
+ *pcandidate = const_cast<DeviceCreateDesc*>((const DeviceCreateDesc*)this);
+ return Match_Candidate;
+ }
+
+ *pcandidate = 0;
+ return Match_Found;
+ }
+
+ // SensorDisplayInfo may override resolution settings, so store as candidiate.
+ if (s2.DeviceId.IsEmpty() && s2.DisplayId == 0)
+ {
+ *pcandidate = const_cast<DeviceCreateDesc*>((const DeviceCreateDesc*)this);
+ return Match_Candidate;
+ }
+ // OTHER HMD Monitor desc may initialize DeviceName/Id
+ else if (DeviceId.IsEmpty() && DisplayId == 0)
+ {
+ *pcandidate = const_cast<DeviceCreateDesc*>((const DeviceCreateDesc*)this);
+ return Match_Candidate;
+ }
+
+ return Match_None;
+}
+
+
+bool HMDDeviceCreateDesc::UpdateMatchedCandidate(const DeviceCreateDesc& other, bool* newDeviceFlag)
+{
+ // This candidate was the the "best fit" to apply sensor DisplayInfo to.
+ OVR_ASSERT(other.Type == Device_HMD);
+
+ const HMDDeviceCreateDesc& s2 = (const HMDDeviceCreateDesc&) other;
+
+ // Force screen size on resolution from SensorDisplayInfo.
+ // We do this because USB detection is more reliable as compared to HDMI EDID,
+ // which may be corrupted by splitter reporting wrong monitor
+ if (s2.DeviceId.IsEmpty() && s2.DisplayId == 0)
+ {
+ // disconnected HMD: replace old descriptor by the 'fake' one.
+ HScreenSize = s2.HScreenSize;
+ VScreenSize = s2.VScreenSize;
+ Contents |= Contents_Screen;
+
+ if (s2.Contents & HMDDeviceCreateDesc::Contents_Distortion)
+ {
+ memcpy(DistortionK, s2.DistortionK, sizeof(float)*4);
+ Contents |= Contents_Distortion;
+ }
+ DeviceId = s2.DeviceId;
+ DisplayId = s2.DisplayId;
+ DisplayDeviceName = s2.DisplayDeviceName;
+ if (newDeviceFlag) *newDeviceFlag = true;
+ }
+ else if (DeviceId.IsEmpty())
+ {
+ // This branch is executed when 'fake' HMD descriptor is being replaced by
+ // the real one.
+ DeviceId = s2.DeviceId;
+ DisplayId = s2.DisplayId;
+ DisplayDeviceName = s2.DisplayDeviceName;
+ if (newDeviceFlag) *newDeviceFlag = true;
+ }
+ else
+ {
+ if (newDeviceFlag) *newDeviceFlag = false;
+ }
+
+ return true;
+}
+
+
+//-------------------------------------------------------------------------------------
+
+
+//-------------------------------------------------------------------------------------
+// ***** HMDDeviceFactory
+
+HMDDeviceFactory HMDDeviceFactory::Instance;
+
+void HMDDeviceFactory::EnumerateDevices(EnumerateVisitor& visitor)
+{
+ CGDirectDisplayID Displays[32];
+ uint32_t NDisplays = 0;
+ CGGetOnlineDisplayList(32, Displays, &NDisplays);
+
+ for (int i = 0; i < NDisplays; i++)
+ {
+ io_service_t port = CGDisplayIOServicePort(Displays[i]);
+ CFDictionaryRef DispInfo = IODisplayCreateInfoDictionary(port, kIODisplayMatchingInfo);
+
+ uint32_t vendor = CGDisplayVendorNumber(Displays[i]);
+ uint32_t product = CGDisplayModelNumber(Displays[i]);
+ unsigned mwidth = (unsigned)CGDisplayPixelsWide(Displays[i]);
+ unsigned mheight = (unsigned)CGDisplayPixelsHigh(Displays[i]);
+ CGRect desktop = CGDisplayBounds(Displays[i]);
+
+ if (vendor == 16082 && product == 1)
+ {
+ char idstring[9];
+ idstring[0] = 'A'-1+((vendor>>10) & 31);
+ idstring[1] = 'A'-1+((vendor>>5) & 31);
+ idstring[2] = 'A'-1+((vendor>>0) & 31);
+ snprintf(idstring+3, 5, "%04d", product);
+
+ HMDDeviceCreateDesc hmdCreateDesc(this, vendor, product, idstring, Displays[i]);
+
+ if (hmdCreateDesc.Is7Inch())
+ {
+ // Physical dimension of SLA screen.
+ hmdCreateDesc.SetScreenParameters(desktop.origin.x, desktop.origin.y, mwidth, mheight, 0.14976f, 0.0936f);
+ }
+ else
+ {
+ hmdCreateDesc.SetScreenParameters(desktop.origin.x, desktop.origin.y, mwidth, mheight, 0.12096f, 0.0756f);
+ }
+ OVR_DEBUG_LOG_TEXT(("DeviceManager - HMD Found %x:%x\n", vendor, product));
+
+ // Notify caller about detected device. This will call EnumerateAddDevice
+ // if the this is the first time device was detected.
+ visitor.Visit(hmdCreateDesc);
+ }
+ CFRelease(DispInfo);
+ }
+}
+
+DeviceBase* HMDDeviceCreateDesc::NewDeviceInstance()
+{
+ return new HMDDevice(this);
+}
+
+bool HMDDeviceCreateDesc::Is7Inch() const
+{
+ return (strstr(DeviceId.ToCStr(), "OVR0001") != 0) || (Contents & Contents_7Inch);
+}
+
+bool HMDDeviceCreateDesc::GetDeviceInfo(DeviceInfo* info) const
+{
+ if ((info->InfoClassType != Device_HMD) &&
+ (info->InfoClassType != Device_None))
+ return false;
+
+ bool is7Inch = Is7Inch();
+
+ OVR_strcpy(info->ProductName, DeviceInfo::MaxNameLength,
+ is7Inch ? "Oculus Rift DK1" : "Oculus Rift DK1-Prototype");
+ OVR_strcpy(info->Manufacturer, DeviceInfo::MaxNameLength, "Oculus VR");
+ info->Type = Device_HMD;
+ info->Version = 0;
+
+ // Display detection.
+ if (info->InfoClassType == Device_HMD)
+ {
+ HMDInfo* hmdInfo = static_cast<HMDInfo*>(info);
+
+ hmdInfo->DesktopX = DesktopX;
+ hmdInfo->DesktopY = DesktopY;
+ hmdInfo->HResolution = HResolution;
+ hmdInfo->VResolution = VResolution;
+ hmdInfo->HScreenSize = HScreenSize;
+ hmdInfo->VScreenSize = VScreenSize;
+ hmdInfo->VScreenCenter = VScreenSize * 0.5f;
+ hmdInfo->InterpupillaryDistance = 0.064f; // Default IPD; should be configurable.
+ hmdInfo->LensSeparationDistance = 0.0635f;
+
+ if (Contents & Contents_Distortion)
+ {
+ memcpy(hmdInfo->DistortionK, DistortionK, sizeof(float)*4);
+ }
+ else
+ {
+ if (is7Inch)
+ {
+ // 7" screen.
+ hmdInfo->DistortionK[0] = 1.0f;
+ hmdInfo->DistortionK[1] = 0.22f;
+ hmdInfo->DistortionK[2] = 0.24f;
+ hmdInfo->EyeToScreenDistance = 0.041f;
+
+ hmdInfo->ChromaAbCorrection[0] = 0.996f;
+ hmdInfo->ChromaAbCorrection[1] = -0.004f;
+ hmdInfo->ChromaAbCorrection[2] = 1.014f;
+ hmdInfo->ChromaAbCorrection[3] = 0.0f;
+ }
+ else
+ {
+ hmdInfo->DistortionK[0] = 1.0f;
+ hmdInfo->DistortionK[1] = 0.18f;
+ hmdInfo->DistortionK[2] = 0.115f;
+ hmdInfo->EyeToScreenDistance = 0.0387f;
+ }
+ }
+
+ OVR_strcpy(hmdInfo->DisplayDeviceName, sizeof(hmdInfo->DisplayDeviceName),
+ DisplayDeviceName.ToCStr());
+ hmdInfo->DisplayId = DisplayId;
+ }
+
+ return true;
+}
+
+//-------------------------------------------------------------------------------------
+// ***** HMDDevice
+
+HMDDevice::HMDDevice(HMDDeviceCreateDesc* createDesc)
+ : OVR::DeviceImpl<OVR::HMDDevice>(createDesc, 0)
+{
+}
+HMDDevice::~HMDDevice()
+{
+}
+
+bool HMDDevice::Initialize(DeviceBase* parent)
+{
+ pParent = parent;
+ return true;
+}
+void HMDDevice::Shutdown()
+{
+ pParent.Clear();
+}
+
+OVR::SensorDevice* HMDDevice::GetSensor()
+{
+ // Just return first sensor found since we have no way to match it yet.
+ OVR::SensorDevice* sensor = GetManager()->EnumerateDevices<SensorDevice>().CreateDevice();
+ if (sensor)
+ sensor->SetCoordinateFrame(SensorDevice::Coord_HMD);
+ return sensor;
+}
+
+
+}} // namespace OVR::OSX
+
+
diff --git a/LibOVR/Src/OVR_OSX_HMDDevice.h b/LibOVR/Src/OVR_OSX_HMDDevice.h
new file mode 100644
index 0000000..37c34ed
--- /dev/null
+++ b/LibOVR/Src/OVR_OSX_HMDDevice.h
@@ -0,0 +1,136 @@
+/************************************************************************************
+
+Filename : OVR_OSX_HMDDevice.h
+Content : OSX HMDDevice implementation
+Created : September 21, 2012
+Authors : Michael Antonov
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_OSX_HMDDevice_h
+#define OVR_OSX_HMDDevice_h
+
+#include "OVR_DeviceImpl.h"
+#include <Kernel/OVR_String.h>
+
+namespace OVR { namespace OSX {
+
+class HMDDevice;
+
+
+//-------------------------------------------------------------------------------------
+
+// HMDDeviceFactory enumerates attached Oculus HMD devices.
+//
+// This is currently done by matching monitor device strings.
+
+class HMDDeviceFactory : public DeviceFactory
+{
+public:
+ static HMDDeviceFactory Instance;
+
+ // Enumerates devices, creating and destroying relevant objects in manager.
+ virtual void EnumerateDevices(EnumerateVisitor& visitor);
+
+protected:
+ DeviceManager* getManager() const { return (DeviceManager*) pManager; }
+};
+
+
+class HMDDeviceCreateDesc : public DeviceCreateDesc
+{
+ friend class HMDDevice;
+
+protected:
+ enum
+ {
+ Contents_Screen = 1,
+ Contents_Distortion = 2,
+ Contents_7Inch = 4,
+ };
+
+public:
+
+ HMDDeviceCreateDesc(DeviceFactory* factory,
+ UInt32 vendor, UInt32 product, const String& displayDeviceName, long dispId);
+ HMDDeviceCreateDesc(const HMDDeviceCreateDesc& other);
+
+ virtual DeviceCreateDesc* Clone() const
+ {
+ return new HMDDeviceCreateDesc(*this);
+ }
+
+ virtual DeviceBase* NewDeviceInstance();
+
+ virtual MatchResult MatchDevice(const DeviceCreateDesc& other,
+ DeviceCreateDesc**) const;
+
+ virtual bool UpdateMatchedCandidate(const DeviceCreateDesc&, bool* newDeviceFlag = NULL);
+
+ virtual bool GetDeviceInfo(DeviceInfo* info) const;
+
+ void SetScreenParameters(int x, int y, unsigned hres, unsigned vres, float hsize, float vsize)
+ {
+ DesktopX = x;
+ DesktopY = y;
+ HResolution = hres;
+ VResolution = vres;
+ HScreenSize = hsize;
+ VScreenSize = vsize;
+ Contents |= Contents_Screen;
+ }
+
+ void SetDistortion(const float* dks)
+ {
+ for (int i = 0; i < 4; i++)
+ DistortionK[i] = dks[i];
+ Contents |= Contents_Distortion;
+ }
+
+ void Set7Inch() { Contents |= Contents_7Inch; }
+
+ bool Is7Inch() const;
+
+protected:
+ String DeviceId;
+ String DisplayDeviceName;
+ int DesktopX, DesktopY;
+ unsigned Contents;
+ unsigned HResolution, VResolution;
+ float HScreenSize, VScreenSize;
+ long DisplayId;
+ float DistortionK[4];
+};
+
+
+//-------------------------------------------------------------------------------------
+
+// HMDDevice represents an Oculus HMD device unit. An instance of this class
+// is typically created from the DeviceManager.
+// After HMD device is created, we its sensor data can be obtained by
+// first creating a Sensor object and then wrappig it in SensorFusion.
+
+class HMDDevice : public DeviceImpl<OVR::HMDDevice>
+{
+public:
+ HMDDevice(HMDDeviceCreateDesc* createDesc);
+ ~HMDDevice();
+
+ virtual bool Initialize(DeviceBase* parent);
+ virtual void Shutdown();
+
+ // Query associated sensor.
+ virtual OVR::SensorDevice* GetSensor();
+};
+
+
+}} // namespace OVR::OSX
+
+#endif // OVR_OSX_HMDDevice_h
+
diff --git a/LibOVR/Src/OVR_OSX_SensorDevice.cpp b/LibOVR/Src/OVR_OSX_SensorDevice.cpp
new file mode 100644
index 0000000..da0726c
--- /dev/null
+++ b/LibOVR/Src/OVR_OSX_SensorDevice.cpp
@@ -0,0 +1,45 @@
+/************************************************************************************
+
+Filename : OVR_OSX_SensorDevice.cpp
+Content : OSX SensorDevice implementation
+Created : March 14, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#include "OVR_OSX_HMDDevice.h"
+#include "OVR_SensorImpl.h"
+#include "OVR_DeviceImpl.h"
+
+namespace OVR { namespace OSX {
+
+} // namespace OSX
+
+//-------------------------------------------------------------------------------------
+void SensorDeviceImpl::EnumerateHMDFromSensorDisplayInfo( const SensorDisplayInfoImpl& displayInfo,
+ DeviceFactory::EnumerateVisitor& visitor)
+{
+
+ OSX::HMDDeviceCreateDesc hmdCreateDesc(&OSX::HMDDeviceFactory::Instance, 1, 1, "", 0);
+
+ hmdCreateDesc.SetScreenParameters( 0, 0,
+ displayInfo.HResolution, displayInfo.VResolution,
+ displayInfo.HScreenSize, displayInfo.VScreenSize);
+
+ if ((displayInfo.DistortionType & SensorDisplayInfoImpl::Mask_BaseFmt) == SensorDisplayInfoImpl::Base_Distortion)
+ hmdCreateDesc.SetDistortion(displayInfo.DistortionK);
+ if (displayInfo.HScreenSize > 0.14f)
+ hmdCreateDesc.Set7Inch();
+
+ visitor.Visit(hmdCreateDesc);
+}
+
+} // namespace OVR
+
+
diff --git a/LibOVR/Src/OVR_SensorFilter.cpp b/LibOVR/Src/OVR_SensorFilter.cpp
new file mode 100644
index 0000000..6bd71b5
--- /dev/null
+++ b/LibOVR/Src/OVR_SensorFilter.cpp
@@ -0,0 +1,201 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_SensorFilter.cpp
+Content : Basic filtering of sensor data
+Created : March 7, 2013
+Authors : Steve LaValle, Anna Yershova
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#include "OVR_SensorFilter.h"
+
+namespace OVR {
+
+Vector3f SensorFilter::Total() const
+{
+ Vector3f total = Vector3f(0.0f, 0.0f, 0.0f);
+ for (int i = 0; i < Size; i++)
+ total += Elements[i];
+ return total;
+}
+
+Vector3f SensorFilter::Mean() const
+{
+ Vector3f total = Vector3f(0.0f, 0.0f, 0.0f);
+ for (int i = 0; i < Size; i++)
+ total += Elements[i];
+ return total / (float) Size;
+}
+
+Vector3f SensorFilter::Median() const
+{
+ int half_window = (int) Size / 2;
+ float sortx[MaxFilterSize];
+ float resultx = 0.0f;
+
+ float sorty[MaxFilterSize];
+ float resulty = 0.0f;
+
+ float sortz[MaxFilterSize];
+ float resultz = 0.0f;
+
+ for (int i = 0; i < Size; i++)
+ {
+ sortx[i] = Elements[i].x;
+ sorty[i] = Elements[i].y;
+ sortz[i] = Elements[i].z;
+ }
+ for (int j = 0; j <= half_window; j++)
+ {
+ int minx = j;
+ int miny = j;
+ int minz = j;
+ for (int k = j + 1; k < Size; k++)
+ {
+ if (sortx[k] < sortx[minx]) minx = k;
+ if (sorty[k] < sorty[miny]) miny = k;
+ if (sortz[k] < sortz[minz]) minz = k;
+ }
+ const float tempx = sortx[j];
+ const float tempy = sorty[j];
+ const float tempz = sortz[j];
+ sortx[j] = sortx[minx];
+ sortx[minx] = tempx;
+
+ sorty[j] = sorty[miny];
+ sorty[miny] = tempy;
+
+ sortz[j] = sortz[minz];
+ sortz[minz] = tempz;
+ }
+ resultx = sortx[half_window];
+ resulty = sorty[half_window];
+ resultz = sortz[half_window];
+
+ return Vector3f(resultx, resulty, resultz);
+}
+
+// Only the diagonal of the covariance matrix.
+Vector3f SensorFilter::Variance() const
+{
+ Vector3f mean = Mean();
+ Vector3f total = Vector3f(0.0f, 0.0f, 0.0f);
+ for (int i = 0; i < Size; i++)
+ {
+ total.x += (Elements[i].x - mean.x) * (Elements[i].x - mean.x);
+ total.y += (Elements[i].y - mean.y) * (Elements[i].y - mean.y);
+ total.z += (Elements[i].z - mean.z) * (Elements[i].z - mean.z);
+ }
+ return total / (float) Size;
+}
+
+// Should be a 3x3 matrix returned, but OVR_math.h doesn't have one
+Matrix4f SensorFilter::Covariance() const
+{
+ Vector3f mean = Mean();
+ Matrix4f total = Matrix4f(0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0);
+ for (int i = 0; i < Size; i++)
+ {
+ total.M[0][0] += (Elements[i].x - mean.x) * (Elements[i].x - mean.x);
+ total.M[1][0] += (Elements[i].y - mean.y) * (Elements[i].x - mean.x);
+ total.M[2][0] += (Elements[i].z - mean.z) * (Elements[i].x - mean.x);
+ total.M[1][1] += (Elements[i].y - mean.y) * (Elements[i].y - mean.y);
+ total.M[2][1] += (Elements[i].z - mean.z) * (Elements[i].y - mean.y);
+ total.M[2][2] += (Elements[i].z - mean.z) * (Elements[i].z - mean.z);
+ }
+ total.M[0][1] = total.M[1][0];
+ total.M[0][2] = total.M[2][0];
+ total.M[1][2] = total.M[2][1];
+ for (int i = 0; i < 3; i++)
+ for (int j = 0; j < 3; j++)
+ total.M[i][j] *= 1.0f / Size;
+ return total;
+}
+
+Vector3f SensorFilter::PearsonCoefficient() const
+{
+ Matrix4f cov = Covariance();
+ Vector3f pearson = Vector3f();
+ pearson.x = cov.M[0][1]/(sqrt(cov.M[0][0])*sqrt(cov.M[1][1]));
+ pearson.y = cov.M[1][2]/(sqrt(cov.M[1][1])*sqrt(cov.M[2][2]));
+ pearson.z = cov.M[2][0]/(sqrt(cov.M[2][2])*sqrt(cov.M[0][0]));
+
+ return pearson;
+}
+
+
+Vector3f SensorFilter::SavitzkyGolaySmooth8() const
+{
+ OVR_ASSERT(Size >= 8);
+ return GetPrev(0)*0.41667f +
+ GetPrev(1)*0.33333f +
+ GetPrev(2)*0.25f +
+ GetPrev(3)*0.16667f +
+ GetPrev(4)*0.08333f -
+ GetPrev(6)*0.08333f -
+ GetPrev(7)*0.16667f;
+}
+
+
+Vector3f SensorFilter::SavitzkyGolayDerivative4() const
+{
+ OVR_ASSERT(Size >= 4);
+ return GetPrev(0)*0.3f +
+ GetPrev(1)*0.1f -
+ GetPrev(2)*0.1f -
+ GetPrev(3)*0.3f;
+}
+
+Vector3f SensorFilter::SavitzkyGolayDerivative5() const
+{
+ OVR_ASSERT(Size >= 5);
+ return GetPrev(0)*0.2f +
+ GetPrev(1)*0.1f -
+ GetPrev(3)*0.1f -
+ GetPrev(4)*0.2f;
+}
+
+Vector3f SensorFilter::SavitzkyGolayDerivative12() const
+{
+ OVR_ASSERT(Size >= 12);
+ return GetPrev(0)*0.03846f +
+ GetPrev(1)*0.03147f +
+ GetPrev(2)*0.02448f +
+ GetPrev(3)*0.01748f +
+ GetPrev(4)*0.01049f +
+ GetPrev(5)*0.0035f -
+ GetPrev(6)*0.0035f -
+ GetPrev(7)*0.01049f -
+ GetPrev(8)*0.01748f -
+ GetPrev(9)*0.02448f -
+ GetPrev(10)*0.03147f -
+ GetPrev(11)*0.03846f;
+}
+
+Vector3f SensorFilter::SavitzkyGolayDerivativeN(int n) const
+{
+ OVR_ASSERT(Size >= n);
+ int m = (n-1)/2;
+ Vector3f result = Vector3f();
+ for (int k = 1; k <= m; k++)
+ {
+ int ind1 = m - k;
+ int ind2 = n - m + k - 1;
+ result += (GetPrev(ind1) - GetPrev(ind2)) * (float) k;
+ }
+ float coef = 3.0f/(m*(m+1.0f)*(2.0f*m+1.0f));
+ result = result*coef;
+ return result;
+}
+
+
+
+
+} //namespace OVR \ No newline at end of file
diff --git a/LibOVR/Src/OVR_SensorFilter.h b/LibOVR/Src/OVR_SensorFilter.h
new file mode 100644
index 0000000..18665b1
--- /dev/null
+++ b/LibOVR/Src/OVR_SensorFilter.h
@@ -0,0 +1,99 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_SensorFilter.h
+Content : Basic filtering of sensor data
+Created : March 7, 2013
+Authors : Steve LaValle, Anna Yershova
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_SensorFilter_h
+#define OVR_SensorFilter_h
+
+#include "Kernel/OVR_Math.h"
+
+
+namespace OVR {
+
+// This class maintains a sliding window of sensor data taken over time and implements
+// various simple filters, most of which are linear functions of the data history.
+class SensorFilter
+{
+ enum
+ {
+ MaxFilterSize = 100,
+ DefaultFilterSize = 20
+ };
+
+private:
+ int LastIdx; // The index of the last element that was added to the array
+ int Size; // The window size (number of elements)
+ Vector3f Elements[MaxFilterSize];
+
+public:
+ // Create a new filter with default size
+ SensorFilter()
+ {
+ LastIdx = -1;
+ Size = DefaultFilterSize;
+ };
+
+ // Create a new filter with size i
+ SensorFilter(int i)
+ {
+ OVR_ASSERT(i <= MaxFilterSize);
+ LastIdx = -1;
+ Size = i;
+ };
+
+
+ // Create a new element to the filter
+ void AddElement (const Vector3f &e)
+ {
+ if (LastIdx == Size - 1)
+ LastIdx = 0;
+ else
+ LastIdx++;
+
+ Elements[LastIdx] = e;
+ };
+
+ // Get element i. 0 is the most recent, 1 is one step ago, 2 is two steps ago, ...
+ Vector3f GetPrev(int i) const
+ {
+ OVR_ASSERT(i >= 0); //
+ int idx = (LastIdx - i);
+ if (idx < 0) // Fix the wraparound case
+ idx += Size;
+ OVR_ASSERT(idx >= 0); // Multiple wraparounds not allowed
+ return Elements[idx];
+ };
+
+ // Simple statistics
+ Vector3f Total() const;
+ Vector3f Mean() const;
+ Vector3f Median() const;
+ Vector3f Variance() const; // The diagonal of covariance matrix
+ Matrix4f Covariance() const;
+ Vector3f PearsonCoefficient() const;
+
+ // A popular family of smoothing filters and smoothed derivatives
+ Vector3f SavitzkyGolaySmooth8() const;
+ Vector3f SavitzkyGolayDerivative4() const;
+ Vector3f SavitzkyGolayDerivative5() const;
+ Vector3f SavitzkyGolayDerivative12() const;
+ Vector3f SavitzkyGolayDerivativeN(int n) const;
+
+ ~SensorFilter() {};
+};
+
+} //namespace OVR
+
+#endif // OVR_SensorFilter_h
diff --git a/LibOVR/Src/OVR_SensorFusion.cpp b/LibOVR/Src/OVR_SensorFusion.cpp
new file mode 100644
index 0000000..78dd128
--- /dev/null
+++ b/LibOVR/Src/OVR_SensorFusion.cpp
@@ -0,0 +1,378 @@
+/************************************************************************************
+
+Filename : OVR_SensorFusion.cpp
+Content : Methods that determine head orientation from sensor data over time
+Created : October 9, 2012
+Authors : Michael Antonov, Steve LaValle
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#include "OVR_SensorFusion.h"
+#include "Kernel/OVR_Log.h"
+#include "Kernel/OVR_System.h"
+
+namespace OVR {
+
+//-------------------------------------------------------------------------------------
+// ***** Sensor Fusion
+
+SensorFusion::SensorFusion(SensorDevice* sensor)
+ : Handler(getThis()), pDelegate(0),
+ Gain(0.05f), YawMult(1), EnableGravity(true), Stage(0), DeltaT(0.001f),
+ EnablePrediction(false), PredictionDT(0.03f),
+ FRawMag(10), FAccW(20), FAngV(20),
+ TiltCondCount(0), TiltErrorAngle(0),
+ TiltErrorAxis(0,1,0),
+ MagCondCount(0), MagReady(false), MagCalibrated(false), MagReferenced(false),
+ MagRefQ(0, 0, 0, 1), MagRefM(0), MagRefYaw(0), YawErrorAngle(0), MagRefDistance(0.15f),
+ YawErrorCount(0), YawCorrectionActivated(false), YawCorrectionInProgress(false),
+ EnableYawCorrection(false)
+{
+ if (sensor)
+ AttachToSensor(sensor);
+ MagCalibrationMatrix.SetIdentity();
+}
+
+SensorFusion::~SensorFusion()
+{
+}
+
+
+bool SensorFusion::AttachToSensor(SensorDevice* sensor)
+{
+
+ if (sensor != NULL)
+ {
+ MessageHandler* pCurrentHandler = sensor->GetMessageHandler();
+
+ if (pCurrentHandler == &Handler)
+ {
+ Reset();
+ return true;
+ }
+
+ if (pCurrentHandler != NULL)
+ {
+ OVR_DEBUG_LOG(
+ ("SensorFusion::AttachToSensor failed - sensor %p already has handler", sensor));
+ return false;
+ }
+ }
+
+ if (Handler.IsHandlerInstalled())
+ {
+ Handler.RemoveHandlerFromDevices();
+ }
+
+ if (sensor != NULL)
+ {
+ sensor->SetMessageHandler(&Handler);
+ }
+
+ Reset();
+ return true;
+}
+
+
+
+
+void SensorFusion::handleMessage(const MessageBodyFrame& msg)
+{
+ if (msg.Type != Message_BodyFrame)
+ return;
+
+ // Put the sensor readings into convenient local variables
+ Vector3f angVel = msg.RotationRate;
+ Vector3f rawAccel = msg.Acceleration;
+ Vector3f mag = msg.MagneticField;
+
+ // Set variables accessible through the class API
+ DeltaT = msg.TimeDelta;
+ AngV = msg.RotationRate;
+ AngV.y *= YawMult; // Warning: If YawMult != 1, then AngV is not true angular velocity
+ A = rawAccel;
+
+ // Allow external access to uncalibrated magnetometer values
+ RawMag = mag;
+
+ // Apply the calibration parameters to raw mag
+ if (HasMagCalibration())
+ {
+ mag.x += MagCalibrationMatrix.M[0][3];
+ mag.y += MagCalibrationMatrix.M[1][3];
+ mag.z += MagCalibrationMatrix.M[2][3];
+ }
+
+ // Provide external access to calibrated mag values
+ // (if the mag is not calibrated, then the raw value is returned)
+ CalMag = mag;
+
+ float angVelLength = angVel.Length();
+ float accLength = rawAccel.Length();
+
+
+ // Acceleration in the world frame (Q is current HMD orientation)
+ Vector3f accWorld = Q.Rotate(rawAccel);
+
+ // Keep track of time
+ Stage++;
+ float currentTime = Stage * DeltaT; // Assumes uniform time spacing
+
+ // Insert current sensor data into filter history
+ FRawMag.AddElement(RawMag);
+ FAccW.AddElement(accWorld);
+ FAngV.AddElement(angVel);
+
+ // Update orientation Q based on gyro outputs. This technique is
+ // based on direct properties of the angular velocity vector:
+ // Its direction is the current rotation axis, and its magnitude
+ // is the rotation rate (rad/sec) about that axis. Our sensor
+ // sampling rate is so fast that we need not worry about integral
+ // approximation error (not yet, anyway).
+ if (angVelLength > 0.0f)
+ {
+ Vector3f rotAxis = angVel / angVelLength;
+ float halfRotAngle = angVelLength * DeltaT * 0.5f;
+ float sinHRA = sin(halfRotAngle);
+ Quatf deltaQ(rotAxis.x*sinHRA, rotAxis.y*sinHRA, rotAxis.z*sinHRA, cos(halfRotAngle));
+
+ Q = Q * deltaQ;
+ }
+
+ // The quaternion magnitude may slowly drift due to numerical error,
+ // so it is periodically normalized.
+ if (Stage % 5000 == 0)
+ Q.Normalize();
+
+ // Maintain the uncorrected orientation for later use by predictive filtering
+ QUncorrected = Q;
+
+ // Perform tilt correction using the accelerometer data. This enables
+ // drift errors in pitch and roll to be corrected. Note that yaw cannot be corrected
+ // because the rotation axis is parallel to the gravity vector.
+ if (EnableGravity)
+ {
+ // Correcting for tilt error by using accelerometer data
+ const float gravityEpsilon = 0.4f;
+ const float angVelEpsilon = 0.1f; // Relatively slow rotation
+ const int tiltPeriod = 50; // Req'd time steps of stability
+ const float maxTiltError = 0.05f;
+ const float minTiltError = 0.01f;
+
+ // This condition estimates whether the only measured acceleration is due to gravity
+ // (the Rift is not linearly accelerating). It is often wrong, but tends to average
+ // out well over time.
+ if ((fabs(accLength - 9.81f) < gravityEpsilon) &&
+ (angVelLength < angVelEpsilon))
+ TiltCondCount++;
+ else
+ TiltCondCount = 0;
+
+ // After stable measurements have been taken over a sufficiently long period,
+ // estimate the amount of tilt error and calculate the tilt axis for later correction.
+ if (TiltCondCount >= tiltPeriod)
+ { // Update TiltErrorEstimate
+ TiltCondCount = 0;
+ // Use an average value to reduce noice (could alternatively use an LPF)
+ Vector3f accWMean = FAccW.Mean();
+ // Project the acceleration vector into the XZ plane
+ Vector3f xzAcc = Vector3f(accWMean.x, 0.0f, accWMean.z);
+ // The unit normal of xzAcc will be the rotation axis for tilt correction
+ Vector3f tiltAxis = Vector3f(xzAcc.z, 0.0f, -xzAcc.x).Normalized();
+ Vector3f yUp = Vector3f(0.0f, 1.0f, 0.0f);
+ // This is the amount of rotation
+ float tiltAngle = yUp.Angle(accWMean);
+ // Record values if the tilt error is intolerable
+ if (tiltAngle > maxTiltError)
+ {
+ TiltErrorAngle = tiltAngle;
+ TiltErrorAxis = tiltAxis;
+ }
+ }
+
+ // This part performs the actual tilt correction as needed
+ if (TiltErrorAngle > minTiltError)
+ {
+ if ((TiltErrorAngle > 0.4f)&&(Stage < 8000))
+ { // Tilt completely to correct orientation
+ Q = Quatf(TiltErrorAxis, -TiltErrorAngle) * Q;
+ TiltErrorAngle = 0.0f;
+ }
+ else
+ {
+ //LogText("Performing tilt correction - Angle: %f Axis: %f %f %f\n",
+ // TiltErrorAngle,TiltErrorAxis.x,TiltErrorAxis.y,TiltErrorAxis.z);
+ //float deltaTiltAngle = -Gain*TiltErrorAngle*0.005f;
+ // This uses agressive correction steps while your head is moving fast
+ float deltaTiltAngle = -Gain*TiltErrorAngle*0.005f*(5.0f*angVelLength+1.0f);
+ // Incrementally "untilt" by a small step size
+ Q = Quatf(TiltErrorAxis, deltaTiltAngle) * Q;
+ TiltErrorAngle += deltaTiltAngle;
+ }
+ }
+ }
+
+ // Yaw drift correction based on magnetometer data. This corrects the part of the drift
+ // that the accelerometer cannot handle.
+ // This will only work if the magnetometer has been enabled, calibrated, and a reference
+ // point has been set.
+ const float maxAngVelLength = 3.0f;
+ const int magWindow = 5;
+ const float yawErrorMax = 0.1f;
+ const float yawErrorMin = 0.01f;
+ const int yawErrorCountLimit = 50;
+ const float yawRotationStep = 0.00002f;
+
+ if (angVelLength < maxAngVelLength)
+ MagCondCount++;
+ else
+ MagCondCount = 0;
+
+ YawCorrectionInProgress = false;
+ if (EnableYawCorrection && MagReady && (currentTime > 2.0f) && (MagCondCount >= magWindow) &&
+ (Q.Distance(MagRefQ) < MagRefDistance))
+ {
+ // Use rotational invariance to bring reference mag value into global frame
+ Vector3f grefmag = MagRefQ.Rotate(GetCalibratedMagValue(MagRefM));
+ // Bring current (averaged) mag reading into global frame
+ Vector3f gmag = Q.Rotate(GetCalibratedMagValue(FRawMag.Mean()));
+ // Calculate the reference yaw in the global frame
+ float gryaw = atan2(grefmag.x,grefmag.z);
+ // Calculate the current yaw in the global frame
+ float gyaw = atan2(gmag.x,gmag.z);
+ //LogText("Yaw error estimate: %f\n",YawErrorAngle);
+ // The difference between reference and current yaws is the perceived error
+ YawErrorAngle = AngleDifference(gyaw,gryaw);
+ // If the perceived error is large, keep count
+ if ((fabs(YawErrorAngle) > yawErrorMax) && (!YawCorrectionActivated))
+ YawErrorCount++;
+ // After enough iterations of high perceived error, start the correction process
+ if (YawErrorCount > yawErrorCountLimit)
+ YawCorrectionActivated = true;
+ // If the perceived error becomes small, turn off the yaw correction
+ if ((fabs(YawErrorAngle) < yawErrorMin) && YawCorrectionActivated)
+ {
+ YawCorrectionActivated = false;
+ YawErrorCount = 0;
+ }
+ // Perform the actual yaw correction, due to previously detected, large yaw error
+ if (YawCorrectionActivated)
+ {
+ YawCorrectionInProgress = true;
+ int sign = (YawErrorAngle > 0.0f) ? 1 : -1;
+ // Incrementally "unyaw" by a small step size
+ Q = Quatf(Vector3f(0.0f,1.0f,0.0f), -yawRotationStep * sign) * Q;
+ }
+ }
+}
+
+
+ // This is a simple predictive filter based only on extrapolating the smoothed, current angular velocity.
+ // Note that both QP (the predicted future orientation) and Q (the current orientation) are both maintained.
+Quatf SensorFusion::GetPredictedOrientation()
+{
+ Lock::Locker lockScope(Handler.GetHandlerLock());
+ Quatf qP = QUncorrected;
+ if (EnablePrediction) {
+#if 1
+ Vector3f angVelF = FAngV.SavitzkyGolaySmooth8();
+ float angVelFL = angVelF.Length();
+
+ if (angVelFL > 0.001f)
+ {
+ Vector3f rotAxisP = angVelF / angVelFL;
+ float halfRotAngleP = angVelFL * PredictionDT * 0.5f;
+ float sinaHRAP = sin(halfRotAngleP);
+ Quatf deltaQP(rotAxisP.x*sinaHRAP, rotAxisP.y*sinaHRAP,
+ rotAxisP.z*sinaHRAP, cos(halfRotAngleP));
+ qP = QUncorrected * deltaQP;
+ }
+#else
+ Quatd qpd = Quatd(Q.x,Q.y,Q.z,Q.w);
+ int predictionStages = (int)(PredictionDT / DeltaT);
+ Vector3f aa = FAngV.SavitzkyGolayDerivative12();
+ Vector3d aad = Vector3d(aa.x,aa.y,aa.z);
+ Vector3f angVelF = FAngV.SavitzkyGolaySmooth8();
+ Vector3d avkd = Vector3d(angVelF.x,angVelF.y,angVelF.z);
+ for (int i = 0; i < predictionStages; i++)
+ {
+ double angVelLengthd = avkd.Length();
+ Vector3d rotAxisd = avkd / angVelLengthd;
+ double halfRotAngled = angVelLengthd * DeltaT * 0.5;
+ double sinHRAd = sin(halfRotAngled);
+ Quatd deltaQd = Quatd(rotAxisd.x*sinHRAd, rotAxisd.y*sinHRAd, rotAxisd.z*sinHRAd,
+ cos(halfRotAngled));
+ qpd = qpd * deltaQd;
+ // Update vel
+ avkd += aad;
+ }
+ qP = Quatf((float)qpd.x,(float)qpd.y,(float)qpd.z,(float)qpd.w);
+#endif
+ }
+ return qP;
+}
+
+
+Vector3f SensorFusion::GetCalibratedMagValue(const Vector3f& rawMag) const
+{
+ Vector3f mag = rawMag;
+ OVR_ASSERT(HasMagCalibration());
+ mag.x += MagCalibrationMatrix.M[0][3];
+ mag.y += MagCalibrationMatrix.M[1][3];
+ mag.z += MagCalibrationMatrix.M[2][3];
+ return mag;
+}
+
+
+void SensorFusion::SetMagReference(const Quatf& q, const Vector3f& rawMag)
+{
+ MagRefQ = q;
+ MagRefM = rawMag;
+
+ float pitch, roll, yaw;
+ Q.GetEulerAngles<Axis_X, Axis_Z, Axis_Y>(&pitch, &roll, &yaw);
+ MagRefYaw = yaw;
+ MagReferenced = true;
+ if (MagCalibrated)
+ MagReady = true;
+}
+
+
+float SensorFusion::AngleDifference(float theta1, float theta2)
+{
+ float x = theta1 - theta2;
+ if (x > Math<float>::Pi)
+ return x - Math<float>::TwoPi;
+ if (x < -Math<float>::Pi)
+ return x + Math<float>::TwoPi;
+ return x;
+}
+
+
+SensorFusion::BodyFrameHandler::~BodyFrameHandler()
+{
+ RemoveHandlerFromDevices();
+}
+
+void SensorFusion::BodyFrameHandler::OnMessage(const Message& msg)
+{
+ if (msg.Type == Message_BodyFrame)
+ pFusion->handleMessage(static_cast<const MessageBodyFrame&>(msg));
+ if (pFusion->pDelegate)
+ pFusion->pDelegate->OnMessage(msg);
+}
+
+bool SensorFusion::BodyFrameHandler::SupportsMessageType(MessageType type) const
+{
+ return (type == Message_BodyFrame);
+}
+
+
+} // namespace OVR
+
diff --git a/LibOVR/Src/OVR_SensorFusion.h b/LibOVR/Src/OVR_SensorFusion.h
new file mode 100644
index 0000000..8b88ea2
--- /dev/null
+++ b/LibOVR/Src/OVR_SensorFusion.h
@@ -0,0 +1,268 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_SensorFusion.h
+Content : Methods that determine head orientation from sensor data over time
+Created : October 9, 2012
+Authors : Michael Antonov, Steve LaValle
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_SensorFusion_h
+#define OVR_SensorFusion_h
+
+#include "OVR_Device.h"
+#include "OVR_SensorFilter.h"
+
+namespace OVR {
+
+//-------------------------------------------------------------------------------------
+// ***** SensorFusion
+
+// SensorFusion class accumulates Sensor notification messages to keep track of
+// orientation, which involves integrating the gyro and doing correction with gravity.
+// Orientation is reported as a quaternion, from which users can obtain either the
+// rotation matrix or Euler angles.
+//
+// The class can operate in two ways:
+// - By user manually passing MessageBodyFrame messages to the OnMessage() function.
+// - By attaching SensorFusion to a SensorDevice, in which case it will
+// automatically handle notifications from that device.
+
+class SensorFusion : public NewOverrideBase
+{
+public:
+ SensorFusion(SensorDevice* sensor = 0);
+ ~SensorFusion();
+
+ // Attaches this SensorFusion to a sensor device, from which it will receive
+ // notification messages. If a sensor is attached, manual message notification
+ // is not necessary. Calling this function also resets SensorFusion state.
+ bool AttachToSensor(SensorDevice* sensor);
+
+ // Returns true if this Sensor fusion object is attached to a sensor.
+ bool IsAttachedToSensor() const { return Handler.IsHandlerInstalled(); }
+
+ void SetGravityEnabled(bool enableGravity) { EnableGravity = enableGravity; }
+
+ bool IsGravityEnabled() const { return EnableGravity;}
+
+ void SetYawCorrectionEnabled(bool enableYawCorrection) { EnableYawCorrection = enableYawCorrection; }
+
+ // Yaw correction is set up to work
+ bool IsYawCorrectionEnabled() const { return EnableYawCorrection;}
+
+ // Yaw correction is currently working (forcing a corrective yaw rotation)
+ bool IsYawCorrectionInProgress() const { return YawCorrectionInProgress;}
+
+ // Store the calibration matrix for the magnetometer
+ void SetMagCalibration(const Matrix4f& m)
+ {
+ MagCalibrationMatrix = m;
+ MagCalibrated = true;
+ if (MagReferenced)
+ MagReady = true;
+ }
+
+ // True only if the mag has calibration values stored
+ bool HasMagCalibration() const { return MagCalibrated;}
+
+ // Force the mag into the uncalibrated state
+ void ClearMagCalibration()
+ {
+ MagCalibrated = false;
+ MagReady = false;
+ }
+
+ // Set the magnetometer's reference orientation for use in yaw correction
+ // The supplied mag is an uncalibrated value
+ void SetMagReference(const Quatf& q, const Vector3f& rawMag);
+ // Default to current HMD orientation
+ void SetMagReference() { SetMagReference(Q, RawMag); }
+
+ bool HasMagReference() const { return MagReferenced; }
+
+ void ClearMagReference()
+ {
+ MagReferenced = false;
+ MagReady = false;
+ }
+
+ bool IsMagReady() const { return MagReady; }
+
+ void SetMagRefDistance(const float d) { MagRefDistance = d; }
+
+ // Notifies SensorFusion object about a new BodyFrame message from a sensor.
+ // Should be called by user if not attaching to a sensor.
+ void OnMessage(const MessageBodyFrame& msg)
+ {
+ OVR_ASSERT(!IsAttachedToSensor());
+ handleMessage(msg);
+ }
+
+ // Obtain the current accumulated orientation.
+ Quatf GetOrientation() const
+ {
+ Lock::Locker lockScope(Handler.GetHandlerLock());
+ return Q;
+ }
+
+ // Use a predictive filter to estimate the future orientation
+ Quatf GetPredictedOrientation();
+
+ // Obtain the last absolute acceleration reading, in m/s^2.
+ Vector3f GetAcceleration() const
+ {
+ Lock::Locker lockScope(Handler.GetHandlerLock());
+ return A;
+ }
+
+ // Obtain the last angular velocity reading, in rad/s.
+ Vector3f GetAngularVelocity() const
+ {
+ Lock::Locker lockScope(Handler.GetHandlerLock());
+ return AngV;
+ }
+ // Obtain the last magnetometer reading, in Gauss
+ Vector3f GetMagnetometer() const
+ {
+ Lock::Locker lockScope(Handler.GetHandlerLock());
+ return RawMag;
+ }
+ // Obtain the filtered magnetometer reading, in Gauss
+ Vector3f GetFilteredMagnetometer() const
+ {
+ Lock::Locker lockScope(Handler.GetHandlerLock());
+ return FRawMag.Mean();
+ }
+ // Obtain the calibrated magnetometer reading (direction and field strength)
+ Vector3f GetCalibratedMagnetometer() const
+ {
+ OVR_ASSERT(MagCalibrated);
+ Lock::Locker lockScope(Handler.GetHandlerLock());
+ return CalMag;
+ }
+
+ Vector3f GetCalibratedMagValue(const Vector3f& rawMag) const;
+
+ float GetMagRefYaw() const
+ {
+ return MagRefYaw;
+ }
+
+ float GetYawErrorAngle() const
+ {
+ return YawErrorAngle;
+ }
+ // For later
+ //Vector3f GetGravity() const;
+
+ // Resets the current orientation
+ void Reset()
+ {
+ Lock::Locker lockScope(Handler.GetHandlerLock());
+ Q = Quatf();
+ QUncorrected = Quatf();
+
+ Stage = 0;
+ }
+
+ // Configuration
+
+ // Gain used to correct gyro with accel. Default value is appropriate for typical use.
+ float GetAccelGain() const { return Gain; }
+ void SetAccelGain(float ag) { Gain = ag; }
+
+ // Multiplier for yaw rotation (turning); setting this higher than 1 (the default) can allow the game
+ // to be played without auxillary rotation controls, possibly making it more immersive. Whether this is more
+ // or less likely to cause motion sickness is unknown.
+ float GetYawMultiplier() const { return YawMult; }
+ void SetYawMultiplier(float y) { YawMult = y; }
+
+ void SetDelegateMessageHandler(MessageHandler* handler)
+ { pDelegate = handler; }
+
+ // Prediction functions.
+ // Prediction delta specifes how much prediction should be applied in seconds; it should in
+ // general be under the average rendering latency. Call GetPredictedOrientation() to get
+ // predicted orientation.
+ float GetPredictionDelta() const { return PredictionDT; }
+ void SetPrediction(float dt, bool enable = true) { PredictionDT = dt; EnablePrediction = enable; }
+ void SetPredictionEnabled(bool enable = true) { EnablePrediction = enable; }
+ bool IsPredictionEnabled() { return EnablePrediction; }
+
+ // Methods for magnetometer calibration
+ static float AngleDifference(float theta1, float theta2);
+ static Vector3f CalculateSphereCenter(Vector3f p1, Vector3f p2,
+ Vector3f p3, Vector3f p4);
+
+
+private:
+ SensorFusion* getThis() { return this; }
+
+ // Internal handler for messages; bypasses error checking.
+ void handleMessage(const MessageBodyFrame& msg);
+
+ class BodyFrameHandler : public MessageHandler
+ {
+ SensorFusion* pFusion;
+ public:
+ BodyFrameHandler(SensorFusion* fusion) : pFusion(fusion) { }
+ ~BodyFrameHandler();
+
+ virtual void OnMessage(const Message& msg);
+ virtual bool SupportsMessageType(MessageType type) const;
+ };
+
+ Quatf Q;
+ Quatf QUncorrected;
+ Vector3f A;
+ Vector3f AngV;
+ Vector3f CalMag;
+ Vector3f RawMag;
+ unsigned int Stage;
+ float DeltaT;
+ BodyFrameHandler Handler;
+ MessageHandler* pDelegate;
+ float Gain;
+ float YawMult;
+ volatile bool EnableGravity;
+
+ bool EnablePrediction;
+ float PredictionDT;
+
+ SensorFilter FRawMag;
+ SensorFilter FAccW;
+ SensorFilter FAngV;
+
+ int TiltCondCount;
+ float TiltErrorAngle;
+ Vector3f TiltErrorAxis;
+
+ bool EnableYawCorrection;
+ Matrix4f MagCalibrationMatrix;
+ bool MagCalibrated;
+ int MagCondCount;
+ bool MagReferenced;
+ float MagRefDistance;
+ bool MagReady;
+ Quatf MagRefQ;
+ Vector3f MagRefM;
+ float MagRefYaw;
+ float YawErrorAngle;
+ int YawErrorCount;
+ bool YawCorrectionInProgress;
+ bool YawCorrectionActivated;
+
+};
+
+
+} // namespace OVR
+
+#endif
diff --git a/LibOVR/Src/OVR_SensorImpl.cpp b/LibOVR/Src/OVR_SensorImpl.cpp
new file mode 100644
index 0000000..fb322df
--- /dev/null
+++ b/LibOVR/Src/OVR_SensorImpl.cpp
@@ -0,0 +1,882 @@
+/************************************************************************************
+
+Filename : OVR_SensorImpl.cpp
+Content : Oculus Sensor device implementation.
+Created : March 7, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#include "OVR_SensorImpl.h"
+
+// HMDDeviceDesc can be created/updated through Sensor carrying DisplayInfo.
+
+#include "Kernel/OVR_Timer.h"
+
+namespace OVR {
+
+//-------------------------------------------------------------------------------------
+// ***** Oculus Sensor-specific packet data structures
+
+enum {
+ Sensor_VendorId = Oculus_VendorId,
+ Sensor_ProductId = 0x0001,
+
+ // ST's VID used originally; should be removed in the future
+ Sensor_OldVendorId = 0x0483,
+ Sensor_OldProductId = 0x5750,
+
+ Sensor_DefaultReportRate = 500, // Hz
+ Sensor_MaxReportRate = 1000 // Hz
+};
+
+// Reported data is little-endian now
+static UInt16 DecodeUInt16(const UByte* buffer)
+{
+ return (UInt16(buffer[1]) << 8) | UInt16(buffer[0]);
+}
+
+static SInt16 DecodeSInt16(const UByte* buffer)
+{
+ return (SInt16(buffer[1]) << 8) | SInt16(buffer[0]);
+}
+
+static UInt32 DecodeUInt32(const UByte* buffer)
+{
+ return (buffer[0]) | UInt32(buffer[1] << 8) | UInt32(buffer[2] << 16) | UInt32(buffer[3] << 24);
+}
+
+static float DecodeFloat(const UByte* buffer)
+{
+ union {
+ UInt32 U;
+ float F;
+ };
+
+ U = DecodeUInt32(buffer);
+ return F;
+}
+
+
+static void UnpackSensor(const UByte* buffer, SInt32* x, SInt32* y, SInt32* z)
+{
+ // Sign extending trick
+ // from http://graphics.stanford.edu/~seander/bithacks.html#FixedSignExtend
+ struct {SInt32 x:21;} s;
+
+ *x = s.x = (buffer[0] << 13) | (buffer[1] << 5) | ((buffer[2] & 0xF8) >> 3);
+ *y = s.x = ((buffer[2] & 0x07) << 18) | (buffer[3] << 10) | (buffer[4] << 2) |
+ ((buffer[5] & 0xC0) >> 6);
+ *z = s.x = ((buffer[5] & 0x3F) << 15) | (buffer[6] << 7) | (buffer[7] >> 1);
+}
+
+// Messages we care for
+enum TrackerMessageType
+{
+ TrackerMessage_None = 0,
+ TrackerMessage_Sensors = 1,
+ TrackerMessage_Unknown = 0x100,
+ TrackerMessage_SizeError = 0x101,
+};
+
+struct TrackerSample
+{
+ SInt32 AccelX, AccelY, AccelZ;
+ SInt32 GyroX, GyroY, GyroZ;
+};
+
+
+struct TrackerSensors
+{
+ UByte SampleCount;
+ UInt16 Timestamp;
+ UInt16 LastCommandID;
+ SInt16 Temperature;
+
+ TrackerSample Samples[3];
+
+ SInt16 MagX, MagY, MagZ;
+
+ TrackerMessageType Decode(const UByte* buffer, int size)
+ {
+ if (size < 62)
+ return TrackerMessage_SizeError;
+
+ SampleCount = buffer[1];
+ Timestamp = DecodeUInt16(buffer + 2);
+ LastCommandID = DecodeUInt16(buffer + 4);
+ Temperature = DecodeSInt16(buffer + 6);
+
+ //if (SampleCount > 2)
+ // OVR_DEBUG_LOG_TEXT(("TackerSensor::Decode SampleCount=%d\n", SampleCount));
+
+ // Only unpack as many samples as there actually are
+ UByte iterationCount = (SampleCount > 2) ? 3 : SampleCount;
+
+ for (UByte i = 0; i < iterationCount; i++)
+ {
+ UnpackSensor(buffer + 8 + 16 * i, &Samples[i].AccelX, &Samples[i].AccelY, &Samples[i].AccelZ);
+ UnpackSensor(buffer + 16 + 16 * i, &Samples[i].GyroX, &Samples[i].GyroY, &Samples[i].GyroZ);
+ }
+
+ MagX = DecodeSInt16(buffer + 56);
+ MagY = DecodeSInt16(buffer + 58);
+ MagZ = DecodeSInt16(buffer + 60);
+
+ return TrackerMessage_Sensors;
+ }
+};
+
+struct TrackerMessage
+{
+ TrackerMessageType Type;
+ TrackerSensors Sensors;
+};
+
+bool DecodeTrackerMessage(TrackerMessage* message, UByte* buffer, int size)
+{
+ memset(message, 0, sizeof(TrackerMessage));
+
+ if (size < 4)
+ {
+ message->Type = TrackerMessage_SizeError;
+ return false;
+ }
+
+ switch (buffer[0])
+ {
+ case TrackerMessage_Sensors:
+ message->Type = message->Sensors.Decode(buffer, size);
+ break;
+
+ default:
+ message->Type = TrackerMessage_Unknown;
+ break;
+ }
+
+ return (message->Type < TrackerMessage_Unknown) && (message->Type != TrackerMessage_None);
+}
+
+
+// ***** SensorRangeImpl Implementation
+
+// Sensor HW only accepts specific maximum range values, used to maximize
+// the 16-bit sensor outputs. Use these ramps to specify and report appropriate values.
+static const UInt16 AccelRangeRamp[] = { 2, 4, 8, 16 };
+static const UInt16 GyroRangeRamp[] = { 250, 500, 1000, 2000 };
+static const UInt16 MagRangeRamp[] = { 880, 1300, 1900, 2500 };
+
+static UInt16 SelectSensorRampValue(const UInt16* ramp, unsigned count,
+ float val, float factor, const char* label)
+{
+ UInt16 threshold = (UInt16)(val * factor);
+
+ for (unsigned i = 0; i<count; i++)
+ {
+ if (ramp[i] >= threshold)
+ return ramp[i];
+ }
+ OVR_DEBUG_LOG(("SensorDevice::SetRange - %s clamped to %0.4f",
+ label, float(ramp[count-1]) / factor));
+ OVR_UNUSED2(factor, label);
+ return ramp[count-1];
+}
+
+// SensorScaleImpl provides buffer packing logic for the Sensor Range
+// record that can be applied to DK1 sensor through Get/SetFeature. We expose this
+// through SensorRange class, which has different units.
+struct SensorRangeImpl
+{
+ enum { PacketSize = 8 };
+ UByte Buffer[PacketSize];
+
+ UInt16 CommandId;
+ UInt16 AccelScale;
+ UInt16 GyroScale;
+ UInt16 MagScale;
+
+ SensorRangeImpl(const SensorRange& r, UInt16 commandId = 0)
+ {
+ SetSensorRange(r, commandId);
+ }
+
+ void SetSensorRange(const SensorRange& r, UInt16 commandId = 0)
+ {
+ CommandId = commandId;
+ AccelScale = SelectSensorRampValue(AccelRangeRamp, sizeof(AccelRangeRamp)/sizeof(AccelRangeRamp[0]),
+ r.MaxAcceleration, (1.0f / 9.81f), "MaxAcceleration");
+ GyroScale = SelectSensorRampValue(GyroRangeRamp, sizeof(GyroRangeRamp)/sizeof(GyroRangeRamp[0]),
+ r.MaxRotationRate, Math<float>::RadToDegreeFactor, "MaxRotationRate");
+ MagScale = SelectSensorRampValue(MagRangeRamp, sizeof(MagRangeRamp)/sizeof(MagRangeRamp[0]),
+ r.MaxMagneticField, 1000.0f, "MaxMagneticField");
+ Pack();
+ }
+
+ void GetSensorRange(SensorRange* r)
+ {
+ r->MaxAcceleration = AccelScale * 9.81f;
+ r->MaxRotationRate = DegreeToRad((float)GyroScale);
+ r->MaxMagneticField= MagScale * 0.001f;
+ }
+
+ static SensorRange GetMaxSensorRange()
+ {
+ return SensorRange(AccelRangeRamp[sizeof(AccelRangeRamp)/sizeof(AccelRangeRamp[0]) - 1] * 9.81f,
+ GyroRangeRamp[sizeof(GyroRangeRamp)/sizeof(GyroRangeRamp[0]) - 1] *
+ Math<float>::DegreeToRadFactor,
+ MagRangeRamp[sizeof(MagRangeRamp)/sizeof(MagRangeRamp[0]) - 1] * 0.001f);
+ }
+
+ void Pack()
+ {
+ Buffer[0] = 4;
+ Buffer[1] = UByte(CommandId & 0xFF);
+ Buffer[2] = UByte(CommandId >> 8);
+ Buffer[3] = UByte(AccelScale);
+ Buffer[4] = UByte(GyroScale & 0xFF);
+ Buffer[5] = UByte(GyroScale >> 8);
+ Buffer[6] = UByte(MagScale & 0xFF);
+ Buffer[7] = UByte(MagScale >> 8);
+ }
+
+ void Unpack()
+ {
+ CommandId = Buffer[1] | (UInt16(Buffer[2]) << 8);
+ AccelScale= Buffer[3];
+ GyroScale = Buffer[4] | (UInt16(Buffer[5]) << 8);
+ MagScale = Buffer[6] | (UInt16(Buffer[7]) << 8);
+ }
+};
+
+
+// Sensor configuration command, ReportId == 2.
+
+struct SensorConfigImpl
+{
+ enum { PacketSize = 7 };
+ UByte Buffer[PacketSize];
+
+ // Flag values for Flags.
+ enum {
+ Flag_RawMode = 0x01,
+ Flag_CallibrationTest = 0x02, // Internal test mode
+ Flag_UseCallibration = 0x04,
+ Flag_AutoCallibration = 0x08,
+ Flag_MotionKeepAlive = 0x10,
+ Flag_CommandKeepAlive = 0x20,
+ Flag_SensorCoordinates = 0x40
+ };
+
+ UInt16 CommandId;
+ UByte Flags;
+ UInt16 PacketInterval;
+ UInt16 KeepAliveIntervalMs;
+
+ SensorConfigImpl() : CommandId(0), Flags(0), PacketInterval(0), KeepAliveIntervalMs(0)
+ {
+ memset(Buffer, 0, PacketSize);
+ Buffer[0] = 2;
+ }
+
+ void SetSensorCoordinates(bool sensorCoordinates)
+ { Flags = (Flags & ~Flag_SensorCoordinates) | (sensorCoordinates ? Flag_SensorCoordinates : 0); }
+ bool IsUsingSensorCoordinates() const
+ { return (Flags & Flag_SensorCoordinates) != 0; }
+
+ void Pack()
+ {
+ Buffer[0] = 2;
+ Buffer[1] = UByte(CommandId & 0xFF);
+ Buffer[2] = UByte(CommandId >> 8);
+ Buffer[3] = Flags;
+ Buffer[4] = UByte(PacketInterval);
+ Buffer[5] = UByte(KeepAliveIntervalMs & 0xFF);
+ Buffer[6] = UByte(KeepAliveIntervalMs >> 8);
+ }
+
+ void Unpack()
+ {
+ CommandId = Buffer[1] | (UInt16(Buffer[2]) << 8);
+ Flags = Buffer[3];
+ PacketInterval = Buffer[4];
+ KeepAliveIntervalMs= Buffer[5] | (UInt16(Buffer[6]) << 8);
+ }
+
+};
+
+
+// SensorKeepAlive - feature report that needs to be sent at regular intervals for sensor
+// to receive commands.
+struct SensorKeepAliveImpl
+{
+ enum { PacketSize = 5 };
+ UByte Buffer[PacketSize];
+
+ UInt16 CommandId;
+ UInt16 KeepAliveIntervalMs;
+
+ SensorKeepAliveImpl(UInt16 interval = 0, UInt16 commandId = 0)
+ : CommandId(commandId), KeepAliveIntervalMs(interval)
+ {
+ Pack();
+ }
+
+ void Pack()
+ {
+ Buffer[0] = 8;
+ Buffer[1] = UByte(CommandId & 0xFF);
+ Buffer[2] = UByte(CommandId >> 8);
+ Buffer[3] = UByte(KeepAliveIntervalMs & 0xFF);
+ Buffer[4] = UByte(KeepAliveIntervalMs >> 8);
+ }
+
+ void Unpack()
+ {
+ CommandId = Buffer[1] | (UInt16(Buffer[2]) << 8);
+ KeepAliveIntervalMs= Buffer[3] | (UInt16(Buffer[4]) << 8);
+ }
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** SensorDisplayInfoImpl
+SensorDisplayInfoImpl::SensorDisplayInfoImpl()
+ : CommandId(0), DistortionType(Base_None)
+{
+ memset(Buffer, 0, PacketSize);
+ Buffer[0] = 9;
+}
+
+void SensorDisplayInfoImpl::Unpack()
+{
+ CommandId = Buffer[1] | (UInt16(Buffer[2]) << 8);
+ DistortionType = Buffer[3];
+ HResolution = DecodeUInt16(Buffer+4);
+ VResolution = DecodeUInt16(Buffer+6);
+ HScreenSize = DecodeUInt32(Buffer+8) * (1/1000000.f);
+ VScreenSize = DecodeUInt32(Buffer+12) * (1/1000000.f);
+ VCenter = DecodeUInt32(Buffer+16) * (1/1000000.f);
+ LensSeparation = DecodeUInt32(Buffer+20) * (1/1000000.f);
+ EyeToScreenDistance[0] = DecodeUInt32(Buffer+24) * (1/1000000.f);
+ EyeToScreenDistance[1] = DecodeUInt32(Buffer+28) * (1/1000000.f);
+ DistortionK[0] = DecodeFloat(Buffer+32);
+ DistortionK[1] = DecodeFloat(Buffer+36);
+ DistortionK[2] = DecodeFloat(Buffer+40);
+ DistortionK[3] = DecodeFloat(Buffer+44);
+ DistortionK[4] = DecodeFloat(Buffer+48);
+ DistortionK[5] = DecodeFloat(Buffer+52);
+}
+
+
+//-------------------------------------------------------------------------------------
+// ***** SensorDeviceFactory
+
+SensorDeviceFactory SensorDeviceFactory::Instance;
+
+void SensorDeviceFactory::EnumerateDevices(EnumerateVisitor& visitor)
+{
+
+ class SensorEnumerator : public HIDEnumerateVisitor
+ {
+ // Assign not supported; suppress MSVC warning.
+ void operator = (const SensorEnumerator&) { }
+
+ DeviceFactory* pFactory;
+ EnumerateVisitor& ExternalVisitor;
+ public:
+ SensorEnumerator(DeviceFactory* factory, EnumerateVisitor& externalVisitor)
+ : pFactory(factory), ExternalVisitor(externalVisitor) { }
+
+ virtual bool MatchVendorProduct(UInt16 vendorId, UInt16 productId)
+ {
+ return pFactory->MatchVendorProduct(vendorId, productId);
+ }
+
+ virtual void Visit(HIDDevice& device, const HIDDeviceDesc& desc)
+ {
+ SensorDeviceCreateDesc createDesc(pFactory, desc);
+ ExternalVisitor.Visit(createDesc);
+
+ // Check if the sensor returns DisplayInfo. If so, try to use it to override potentially
+ // mismatching monitor information (in case wrong EDID is reported by splitter),
+ // or to create a new "virtualized" HMD Device.
+
+ SensorDisplayInfoImpl displayInfo;
+
+ if (device.GetFeatureReport(displayInfo.Buffer, SensorDisplayInfoImpl::PacketSize))
+ {
+ displayInfo.Unpack();
+
+ // If we got display info, try to match / create HMDDevice as well
+ // so that sensor settings give preference.
+ if (displayInfo.DistortionType & SensorDisplayInfoImpl::Mask_BaseFmt)
+ {
+ SensorDeviceImpl::EnumerateHMDFromSensorDisplayInfo(displayInfo, ExternalVisitor);
+ }
+ }
+ }
+ };
+
+ //double start = Timer::GetProfileSeconds();
+
+ SensorEnumerator sensorEnumerator(this, visitor);
+ GetManagerImpl()->GetHIDDeviceManager()->Enumerate(&sensorEnumerator);
+
+ //double totalSeconds = Timer::GetProfileSeconds() - start;
+}
+
+bool SensorDeviceFactory::MatchVendorProduct(UInt16 vendorId, UInt16 productId) const
+{
+ return ((vendorId == Sensor_VendorId) && (productId == Sensor_ProductId)) ||
+ ((vendorId == Sensor_OldVendorId) && (productId == Sensor_OldProductId));
+}
+
+bool SensorDeviceFactory::DetectHIDDevice(DeviceManager* pdevMgr, const HIDDeviceDesc& desc)
+{
+ if (MatchVendorProduct(desc.VendorId, desc.ProductId))
+ {
+ SensorDeviceCreateDesc createDesc(this, desc);
+ return pdevMgr->AddDevice_NeedsLock(createDesc).GetPtr() != NULL;
+ }
+ return false;
+}
+
+//-------------------------------------------------------------------------------------
+// ***** SensorDeviceCreateDesc
+
+DeviceBase* SensorDeviceCreateDesc::NewDeviceInstance()
+{
+ return new SensorDeviceImpl(this);
+}
+
+bool SensorDeviceCreateDesc::GetDeviceInfo(DeviceInfo* info) const
+{
+ if ((info->InfoClassType != Device_Sensor) &&
+ (info->InfoClassType != Device_None))
+ return false;
+
+ OVR_strcpy(info->ProductName, DeviceInfo::MaxNameLength, HIDDesc.Product.ToCStr());
+ OVR_strcpy(info->Manufacturer, DeviceInfo::MaxNameLength, HIDDesc.Manufacturer.ToCStr());
+ info->Type = Device_Sensor;
+ info->Version = 0;
+
+ if (info->InfoClassType == Device_Sensor)
+ {
+ SensorInfo* sinfo = (SensorInfo*)info;
+ sinfo->VendorId = HIDDesc.VendorId;
+ sinfo->ProductId = HIDDesc.ProductId;
+ sinfo->MaxRanges = SensorRangeImpl::GetMaxSensorRange();
+ OVR_strcpy(sinfo->SerialNumber, sizeof(sinfo->SerialNumber),HIDDesc.SerialNumber.ToCStr());
+ }
+ return true;
+}
+
+
+//-------------------------------------------------------------------------------------
+// ***** SensorDevice
+
+SensorDeviceImpl::SensorDeviceImpl(SensorDeviceCreateDesc* createDesc)
+ : OVR::HIDDeviceImpl<OVR::SensorDevice>(createDesc, 0),
+ Coordinates(SensorDevice::Coord_Sensor),
+ HWCoordinates(SensorDevice::Coord_HMD), // HW reports HMD coordinates by default.
+ NextKeepAliveTicks(0),
+ MaxValidRange(SensorRangeImpl::GetMaxSensorRange())
+{
+ SequenceValid = false;
+ LastSampleCount= 0;
+ LastTimestamp = 0;
+
+ OldCommandId = 0;
+}
+
+SensorDeviceImpl::~SensorDeviceImpl()
+{
+ // Check that Shutdown() was called.
+ OVR_ASSERT(!pCreateDesc->pDevice);
+}
+
+// Internal creation APIs.
+bool SensorDeviceImpl::Initialize(DeviceBase* parent)
+{
+ if (HIDDeviceImpl<OVR::SensorDevice>::Initialize(parent))
+ {
+ openDevice();
+
+ LogText("OVR::SensorDevice initialized.\n");
+
+ return true;
+ }
+
+ return false;
+}
+
+void SensorDeviceImpl::openDevice()
+{
+
+ // Read the currently configured range from sensor.
+ SensorRangeImpl sr(SensorRange(), 0);
+
+ if (GetInternalDevice()->GetFeatureReport(sr.Buffer, SensorRangeImpl::PacketSize))
+ {
+ sr.Unpack();
+ sr.GetSensorRange(&CurrentRange);
+ }
+
+
+ // If the sensor has "DisplayInfo" data, use HMD coordinate frame by default.
+ SensorDisplayInfoImpl displayInfo;
+ if (GetInternalDevice()->GetFeatureReport(displayInfo.Buffer, SensorDisplayInfoImpl::PacketSize))
+ {
+ displayInfo.Unpack();
+ Coordinates = (displayInfo.DistortionType & SensorDisplayInfoImpl::Mask_BaseFmt) ?
+ Coord_HMD : Coord_Sensor;
+ }
+
+ // Read/Apply sensor config.
+ setCoordinateFrame(Coordinates);
+ setReportRate(Sensor_DefaultReportRate);
+
+ // Set Keep-alive at 10 seconds.
+ SensorKeepAliveImpl skeepAlive(10 * 1000);
+ GetInternalDevice()->SetFeatureReport(skeepAlive.Buffer, SensorKeepAliveImpl::PacketSize);
+}
+
+void SensorDeviceImpl::closeDeviceOnError()
+{
+ LogText("OVR::SensorDevice - Lost connection to '%s'\n", getHIDDesc()->Path.ToCStr());
+ NextKeepAliveTicks = 0;
+}
+
+void SensorDeviceImpl::Shutdown()
+{
+ HIDDeviceImpl<OVR::SensorDevice>::Shutdown();
+
+ LogText("OVR::SensorDevice - Closed '%s'\n", getHIDDesc()->Path.ToCStr());
+}
+
+
+void SensorDeviceImpl::OnInputReport(UByte* pData, UInt32 length)
+{
+
+ bool processed = false;
+ if (!processed)
+ {
+
+ TrackerMessage message;
+ if (DecodeTrackerMessage(&message, pData, length))
+ {
+ processed = true;
+ onTrackerMessage(&message);
+ }
+ }
+}
+
+UInt64 SensorDeviceImpl::OnTicks(UInt64 ticksMks)
+{
+
+ if (ticksMks >= NextKeepAliveTicks)
+ {
+ // Use 3-seconds keep alive by default.
+ UInt64 keepAliveDelta = Timer::MksPerSecond * 3;
+
+ // Set Keep-alive at 10 seconds.
+ SensorKeepAliveImpl skeepAlive(10 * 1000);
+ // OnTicks is called from background thread so we don't need to add this to the command queue.
+ GetInternalDevice()->SetFeatureReport(skeepAlive.Buffer, SensorKeepAliveImpl::PacketSize);
+
+ // Emit keep-alive every few seconds.
+ NextKeepAliveTicks = ticksMks + keepAliveDelta;
+ }
+ return NextKeepAliveTicks - ticksMks;
+}
+
+bool SensorDeviceImpl::SetRange(const SensorRange& range, bool waitFlag)
+{
+ bool result = 0;
+ ThreadCommandQueue * threadQueue = GetManagerImpl()->GetThreadQueue();
+
+ if (!waitFlag)
+ {
+ return threadQueue->PushCall(this, &SensorDeviceImpl::setRange, range);
+ }
+
+ if (!threadQueue->PushCallAndWaitResult(this,
+ &SensorDeviceImpl::setRange,
+ &result,
+ range))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+void SensorDeviceImpl::GetRange(SensorRange* range) const
+{
+ Lock::Locker lockScope(GetLock());
+ *range = CurrentRange;
+}
+
+bool SensorDeviceImpl::setRange(const SensorRange& range)
+{
+ SensorRangeImpl sr(range);
+
+ if (GetInternalDevice()->SetFeatureReport(sr.Buffer, SensorRangeImpl::PacketSize))
+ {
+ Lock::Locker lockScope(GetLock());
+ sr.GetSensorRange(&CurrentRange);
+ return true;
+ }
+
+ return false;
+}
+
+void SensorDeviceImpl::SetCoordinateFrame(CoordinateFrame coordframe)
+{
+ // Push call with wait.
+ GetManagerImpl()->GetThreadQueue()->
+ PushCall(this, &SensorDeviceImpl::setCoordinateFrame, coordframe, true);
+}
+
+SensorDevice::CoordinateFrame SensorDeviceImpl::GetCoordinateFrame() const
+{
+ return Coordinates;
+}
+
+Void SensorDeviceImpl::setCoordinateFrame(CoordinateFrame coordframe)
+{
+
+ Coordinates = coordframe;
+
+ // Read the original coordinate frame, then try to change it.
+ SensorConfigImpl scfg;
+ if (GetInternalDevice()->GetFeatureReport(scfg.Buffer, SensorConfigImpl::PacketSize))
+ {
+ scfg.Unpack();
+ }
+
+ scfg.SetSensorCoordinates(coordframe == Coord_Sensor);
+ scfg.Pack();
+
+ GetInternalDevice()->SetFeatureReport(scfg.Buffer, SensorConfigImpl::PacketSize);
+
+ // Re-read the state, in case of older firmware that doesn't support Sensor coordinates.
+ if (GetInternalDevice()->GetFeatureReport(scfg.Buffer, SensorConfigImpl::PacketSize))
+ {
+ scfg.Unpack();
+ HWCoordinates = scfg.IsUsingSensorCoordinates() ? Coord_Sensor : Coord_HMD;
+ }
+ else
+ {
+ HWCoordinates = Coord_HMD;
+ }
+ return 0;
+}
+
+void SensorDeviceImpl::SetReportRate(unsigned rateHz)
+{
+ // Push call with wait.
+ GetManagerImpl()->GetThreadQueue()->
+ PushCall(this, &SensorDeviceImpl::setReportRate, rateHz, true);
+}
+
+unsigned SensorDeviceImpl::GetReportRate() const
+{
+ // Read the original configuration
+ SensorConfigImpl scfg;
+ if (GetInternalDevice()->GetFeatureReport(scfg.Buffer, SensorConfigImpl::PacketSize))
+ {
+ scfg.Unpack();
+ return Sensor_MaxReportRate / (scfg.PacketInterval + 1);
+ }
+ return 0; // error
+}
+
+Void SensorDeviceImpl::setReportRate(unsigned rateHz)
+{
+ // Read the original configuration
+ SensorConfigImpl scfg;
+ if (GetInternalDevice()->GetFeatureReport(scfg.Buffer, SensorConfigImpl::PacketSize))
+ {
+ scfg.Unpack();
+ }
+
+ if (rateHz > Sensor_MaxReportRate)
+ rateHz = Sensor_MaxReportRate;
+ else if (rateHz == 0)
+ rateHz = Sensor_DefaultReportRate;
+
+ scfg.PacketInterval = UInt16((Sensor_MaxReportRate / rateHz) - 1);
+
+ scfg.Pack();
+
+ GetInternalDevice()->SetFeatureReport(scfg.Buffer, SensorConfigImpl::PacketSize);
+ return 0;
+}
+
+void SensorDeviceImpl::SetMessageHandler(MessageHandler* handler)
+{
+ if (handler)
+ {
+ SequenceValid = false;
+ DeviceBase::SetMessageHandler(handler);
+ }
+ else
+ {
+ DeviceBase::SetMessageHandler(handler);
+ }
+}
+
+// Sensor reports data in the following coordinate system:
+// Accelerometer: 10^-4 m/s^2; X forward, Y right, Z Down.
+// Gyro: 10^-4 rad/s; X positive roll right, Y positive pitch up; Z positive yaw right.
+
+
+// We need to convert it to the following RHS coordinate system:
+// X right, Y Up, Z Back (out of screen)
+//
+Vector3f AccelFromBodyFrameUpdate(const TrackerSensors& update, UByte sampleNumber,
+ bool convertHMDToSensor = false)
+{
+ const TrackerSample& sample = update.Samples[sampleNumber];
+ float ax = (float)sample.AccelX;
+ float ay = (float)sample.AccelY;
+ float az = (float)sample.AccelZ;
+
+ Vector3f val = convertHMDToSensor ? Vector3f(ax, az, -ay) : Vector3f(ax, ay, az);
+ return val * 0.0001f;
+}
+
+
+Vector3f MagFromBodyFrameUpdate(const TrackerSensors& update,
+ bool convertHMDToSensor = false)
+{
+ // Note: Y and Z are swapped in comparison to the Accel.
+ // This accounts for DK1 sensor firmware axis swap, which should be undone in future releases.
+ if (!convertHMDToSensor)
+ {
+ return Vector3f( (float)update.MagX,
+ (float)update.MagZ,
+ (float)update.MagY) * 0.0001f;
+ }
+
+ return Vector3f( (float)update.MagX,
+ (float)update.MagY,
+ -(float)update.MagZ) * 0.0001f;
+}
+
+Vector3f EulerFromBodyFrameUpdate(const TrackerSensors& update, UByte sampleNumber,
+ bool convertHMDToSensor = false)
+{
+ const TrackerSample& sample = update.Samples[sampleNumber];
+ float gx = (float)sample.GyroX;
+ float gy = (float)sample.GyroY;
+ float gz = (float)sample.GyroZ;
+
+ Vector3f val = convertHMDToSensor ? Vector3f(gx, gz, -gy) : Vector3f(gx, gy, gz);
+ return val * 0.0001f;
+}
+
+
+void SensorDeviceImpl::onTrackerMessage(TrackerMessage* message)
+{
+ if (message->Type != TrackerMessage_Sensors)
+ return;
+
+ const float timeUnit = (1.0f / 1000.f);
+ TrackerSensors& s = message->Sensors;
+
+
+ // Call OnMessage() within a lock to avoid conflicts with handlers.
+ Lock::Locker scopeLock(HandlerRef.GetLock());
+
+
+ if (SequenceValid)
+ {
+ unsigned timestampDelta;
+
+ if (s.Timestamp < LastTimestamp)
+ timestampDelta = ((((int)s.Timestamp) + 0x10000) - (int)LastTimestamp);
+ else
+ timestampDelta = (s.Timestamp - LastTimestamp);
+
+ // If we missed a small number of samples, replicate the last sample.
+ if ((timestampDelta > LastSampleCount) && (timestampDelta <= 254))
+ {
+ if (HandlerRef.GetHandler())
+ {
+ MessageBodyFrame sensors(this);
+ sensors.TimeDelta = (timestampDelta - LastSampleCount) * timeUnit;
+ sensors.Acceleration = LastAcceleration;
+ sensors.RotationRate = LastRotationRate;
+ sensors.MagneticField = LastMagneticField;
+ sensors.Temperature = LastTemperature;
+
+ HandlerRef.GetHandler()->OnMessage(sensors);
+ }
+ }
+ }
+ else
+ {
+ LastAcceleration = Vector3f(0);
+ LastRotationRate = Vector3f(0);
+ LastMagneticField= Vector3f(0);
+ LastTemperature = 0;
+ SequenceValid = true;
+ }
+
+ LastSampleCount = s.SampleCount;
+ LastTimestamp = s.Timestamp;
+
+ bool convertHMDToSensor = (Coordinates == Coord_Sensor) && (HWCoordinates == Coord_HMD);
+
+ if (HandlerRef.GetHandler())
+ {
+ MessageBodyFrame sensors(this);
+ UByte iterations = s.SampleCount;
+
+ if (s.SampleCount > 3)
+ {
+ iterations = 3;
+ sensors.TimeDelta = (s.SampleCount - 2) * timeUnit;
+ }
+ else
+ {
+ sensors.TimeDelta = timeUnit;
+ }
+
+ for (UByte i = 0; i < iterations; i++)
+ {
+ sensors.Acceleration = AccelFromBodyFrameUpdate(s, i, convertHMDToSensor);
+ sensors.RotationRate = EulerFromBodyFrameUpdate(s, i, convertHMDToSensor);
+ sensors.MagneticField= MagFromBodyFrameUpdate(s, convertHMDToSensor);
+ sensors.Temperature = s.Temperature * 0.01f;
+ HandlerRef.GetHandler()->OnMessage(sensors);
+ // TimeDelta for the last two sample is always fixed.
+ sensors.TimeDelta = timeUnit;
+ }
+
+ LastAcceleration = sensors.Acceleration;
+ LastRotationRate = sensors.RotationRate;
+ LastMagneticField= sensors.MagneticField;
+ LastTemperature = sensors.Temperature;
+ }
+ else
+ {
+ UByte i = (s.SampleCount > 3) ? 2 : (s.SampleCount - 1);
+ LastAcceleration = AccelFromBodyFrameUpdate(s, i, convertHMDToSensor);
+ LastRotationRate = EulerFromBodyFrameUpdate(s, i, convertHMDToSensor);
+ LastMagneticField = MagFromBodyFrameUpdate(s, convertHMDToSensor);
+ LastTemperature = s.Temperature * 0.01f;
+ }
+}
+
+} // namespace OVR
+
+
diff --git a/LibOVR/Src/OVR_SensorImpl.h b/LibOVR/Src/OVR_SensorImpl.h
new file mode 100644
index 0000000..890d621
--- /dev/null
+++ b/LibOVR/Src/OVR_SensorImpl.h
@@ -0,0 +1,208 @@
+/************************************************************************************
+
+Filename : OVR_SensorImpl.h
+Content : Sensor device specific implementation.
+Created : March 7, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_SensorImpl_h
+#define OVR_SensorImpl_h
+
+#include "OVR_HIDDeviceImpl.h"
+
+namespace OVR {
+
+struct TrackerMessage;
+class ExternalVisitor;
+
+//-------------------------------------------------------------------------------------
+// SensorDeviceFactory enumerates Oculus Sensor devices.
+class SensorDeviceFactory : public DeviceFactory
+{
+public:
+ static SensorDeviceFactory Instance;
+
+ // Enumerates devices, creating and destroying relevant objects in manager.
+ virtual void EnumerateDevices(EnumerateVisitor& visitor);
+
+ virtual bool MatchVendorProduct(UInt16 vendorId, UInt16 productId) const;
+ virtual bool DetectHIDDevice(DeviceManager* pdevMgr, const HIDDeviceDesc& desc);
+protected:
+ DeviceManager* getManager() const { return (DeviceManager*) pManager; }
+};
+
+
+// Describes a single a Oculus Sensor device and supports creating its instance.
+class SensorDeviceCreateDesc : public HIDDeviceCreateDesc
+{
+public:
+ SensorDeviceCreateDesc(DeviceFactory* factory, const HIDDeviceDesc& hidDesc)
+ : HIDDeviceCreateDesc(factory, Device_Sensor, hidDesc) { }
+
+ virtual DeviceCreateDesc* Clone() const
+ {
+ return new SensorDeviceCreateDesc(*this);
+ }
+
+ virtual DeviceBase* NewDeviceInstance();
+
+ virtual MatchResult MatchDevice(const DeviceCreateDesc& other,
+ DeviceCreateDesc**) const
+ {
+ if ((other.Type == Device_Sensor) && (pFactory == other.pFactory))
+ {
+ const SensorDeviceCreateDesc& s2 = (const SensorDeviceCreateDesc&) other;
+ if (MatchHIDDevice(s2.HIDDesc))
+ return Match_Found;
+ }
+ return Match_None;
+ }
+
+ virtual bool MatchHIDDevice(const HIDDeviceDesc& hidDesc) const
+ {
+ // should paths comparison be case insensitive?
+ return ((HIDDesc.Path.CompareNoCase(hidDesc.Path) == 0) &&
+ (HIDDesc.SerialNumber == hidDesc.SerialNumber));
+ }
+
+ virtual bool GetDeviceInfo(DeviceInfo* info) const;
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** OVR::SensorDisplayInfoImpl
+
+// DisplayInfo obtained from sensor; these values are used to report distortion
+// settings and other coefficients.
+// Older SensorDisplayInfo will have all zeros, causing the library to apply hard-coded defaults.
+// Currently, only resolutions and sizes are used.
+struct SensorDisplayInfoImpl
+{
+ enum { PacketSize = 56 };
+ UByte Buffer[PacketSize];
+
+ enum
+ {
+ Mask_BaseFmt = 0x0f,
+ Mask_OptionFmts = 0xf0,
+ Base_None = 0,
+ Base_ScreenOnly = 1,
+ Base_Distortion = 2,
+ };
+
+ UInt16 CommandId;
+ UByte DistortionType;
+ UInt16 HResolution, VResolution;
+ float HScreenSize, VScreenSize;
+ float VCenter;
+ float LensSeparation;
+ float EyeToScreenDistance[2];
+ float DistortionK[6];
+
+ SensorDisplayInfoImpl();
+
+ void Unpack();
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** OVR::SensorDeviceImpl
+
+// Oculus Sensor interface.
+
+class SensorDeviceImpl : public HIDDeviceImpl<OVR::SensorDevice>
+{
+public:
+ SensorDeviceImpl(SensorDeviceCreateDesc* createDesc);
+ ~SensorDeviceImpl();
+
+
+ // DeviceCommaon interface
+ virtual bool Initialize(DeviceBase* parent);
+ virtual void Shutdown();
+
+ virtual void SetMessageHandler(MessageHandler* handler);
+
+ // HIDDevice::Notifier interface.
+ virtual void OnInputReport(UByte* pData, UInt32 length);
+ virtual UInt64 OnTicks(UInt64 ticksMks);
+
+ // HMD-Mounted sensor has a different coordinate frame.
+ virtual void SetCoordinateFrame(CoordinateFrame coordframe);
+ virtual CoordinateFrame GetCoordinateFrame() const;
+
+ // SensorDevice interface
+ virtual bool SetRange(const SensorRange& range, bool waitFlag);
+ virtual void GetRange(SensorRange* range) const;
+
+ // Sets report rate (in Hz) of MessageBodyFrame messages (delivered through MessageHandler::OnMessage call).
+ // Currently supported maximum rate is 1000Hz. If the rate is set to 500 or 333 Hz then OnMessage will be
+ // called twice or thrice at the same 'tick'.
+ // If the rate is < 333 then the OnMessage / MessageBodyFrame will be called three
+ // times for each 'tick': the first call will contain averaged values, the second
+ // and third calls will provide with most recent two recorded samples.
+ virtual void SetReportRate(unsigned rateHz);
+ // Returns currently set report rate, in Hz. If 0 - error occurred.
+ // Note, this value may be different from the one provided for SetReportRate. The return
+ // value will contain the actual rate.
+ virtual unsigned GetReportRate() const;
+
+ // Hack to create HMD device from sensor display info.
+ static void EnumerateHMDFromSensorDisplayInfo(const SensorDisplayInfoImpl& displayInfo,
+ DeviceFactory::EnumerateVisitor& visitor);
+protected:
+
+ void openDevice();
+ void closeDeviceOnError();
+
+ Void setCoordinateFrame(CoordinateFrame coordframe);
+ bool setRange(const SensorRange& range);
+
+ Void setReportRate(unsigned rateHz);
+
+ // Called for decoded messages
+ void onTrackerMessage(TrackerMessage* message);
+
+ // Helpers to reduce casting.
+/*
+ SensorDeviceCreateDesc* getCreateDesc() const
+ { return (SensorDeviceCreateDesc*)pCreateDesc.GetPtr(); }
+
+ HIDDeviceDesc* getHIDDesc() const
+ { return &getCreateDesc()->HIDDesc; }
+*/
+
+ // Set if the sensor is located on the HMD.
+ // Older prototype firmware doesn't support changing HW coordinates,
+ // so we track its state.
+ CoordinateFrame Coordinates;
+ CoordinateFrame HWCoordinates;
+ UInt64 NextKeepAliveTicks;
+
+ bool SequenceValid;
+ SInt16 LastTimestamp;
+ UByte LastSampleCount;
+ float LastTemperature;
+ Vector3f LastAcceleration;
+ Vector3f LastRotationRate;
+ Vector3f LastMagneticField;
+
+ // Current sensor range obtained from device.
+ SensorRange MaxValidRange;
+ SensorRange CurrentRange;
+
+ UInt16 OldCommandId;
+};
+
+
+} // namespace OVR
+
+#endif // OVR_SensorImpl_h
diff --git a/LibOVR/Src/OVR_ThreadCommandQueue.cpp b/LibOVR/Src/OVR_ThreadCommandQueue.cpp
new file mode 100644
index 0000000..d9dbab3
--- /dev/null
+++ b/LibOVR/Src/OVR_ThreadCommandQueue.cpp
@@ -0,0 +1,370 @@
+/************************************************************************************
+
+PublicHeader: None
+Filename : OVR_ThreadCommandQueue.cpp
+Content : Command queue for operations executed on a thread
+Created : October 29, 2012
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#include "OVR_ThreadCommandQueue.h"
+
+namespace OVR {
+
+
+//------------------------------------------------------------------------
+// ***** CircularBuffer
+
+// CircularBuffer is a FIFO buffer implemented in a single block of memory,
+// which allows writing and reading variable-size data chucks. Write fails
+// if buffer is full.
+
+class CircularBuffer
+{
+ enum {
+ AlignSize = 16,
+ AlignMask = AlignSize - 1
+ };
+
+ UByte* pBuffer;
+ UPInt Size;
+ UPInt Tail; // Byte offset of next item to be popped.
+ UPInt Head; // Byte offset of where next push will take place.
+ UPInt End; // When Head < Tail, this is used instead of Size.
+
+ inline UPInt roundUpSize(UPInt size)
+ { return (size + AlignMask) & ~(UPInt)AlignMask; }
+
+public:
+
+ CircularBuffer(UPInt size)
+ : Size(size), Tail(0), Head(0), End(0)
+ {
+ pBuffer = (UByte*)OVR_ALLOC_ALIGNED(roundUpSize(size), AlignSize);
+ }
+ ~CircularBuffer()
+ {
+ // For ThreadCommands, we must consume everything before shutdown.
+ OVR_ASSERT(IsEmpty());
+ OVR_FREE_ALIGNED(pBuffer);
+ }
+
+ bool IsEmpty() const { return (Head == Tail); }
+
+ // Allocates a state block of specified size and advances pointers,
+ // returning 0 if buffer is full.
+ UByte* Write(UPInt size);
+
+ // Returns a pointer to next available data block; 0 if none available.
+ UByte* ReadBegin()
+ { return (Head != Tail) ? (pBuffer + Tail) : 0; }
+ // Consumes data of specified size; this must match size passed to Write.
+ void ReadEnd(UPInt size);
+};
+
+
+// Allocates a state block of specified size and advances pointers,
+// returning 0 if buffer is full.
+UByte* CircularBuffer::Write(UPInt size)
+{
+ UByte* p = 0;
+
+ size = roundUpSize(size);
+ // Since this is circular buffer, always allow at least one item.
+ OVR_ASSERT(size < Size/2);
+
+ if (Head >= Tail)
+ {
+ OVR_ASSERT(End == 0);
+
+ if (size <= (Size - Head))
+ {
+ p = pBuffer + Head;
+ Head += size;
+ }
+ else if (size < Tail)
+ {
+ p = pBuffer;
+ End = Head;
+ Head = size;
+ OVR_ASSERT(Head != Tail);
+ }
+ }
+ else
+ {
+ OVR_ASSERT(End != 0);
+
+ if ((Tail - Head) > size)
+ {
+ p = pBuffer + Head;
+ Head += size;
+ OVR_ASSERT(Head != Tail);
+ }
+ }
+
+ return p;
+}
+
+void CircularBuffer::ReadEnd(UPInt size)
+{
+ OVR_ASSERT(Head != Tail);
+ size = roundUpSize(size);
+
+ Tail += size;
+ if (Tail == End)
+ {
+ Tail = End = 0;
+ }
+ else if (Tail == Head)
+ {
+ OVR_ASSERT(End == 0);
+ Tail = Head = 0;
+ }
+}
+
+
+//-------------------------------------------------------------------------------------
+// ***** ThreadCommand
+
+ThreadCommand::PopBuffer::~PopBuffer()
+{
+ if (Size)
+ Destruct<ThreadCommand>(toCommand());
+}
+
+void ThreadCommand::PopBuffer::InitFromBuffer(void* data)
+{
+ ThreadCommand* cmd = (ThreadCommand*)data;
+ OVR_ASSERT(cmd->Size <= MaxSize);
+
+ if (Size)
+ Destruct<ThreadCommand>(toCommand());
+ Size = cmd->Size;
+ memcpy(Buffer, (void*)cmd, Size);
+}
+
+void ThreadCommand::PopBuffer::Execute()
+{
+ ThreadCommand* command = toCommand();
+ OVR_ASSERT(command);
+ command->Execute();
+ if (NeedsWait())
+ GetEvent()->PulseEvent();
+}
+
+//-------------------------------------------------------------------------------------
+
+class ThreadCommandQueueImpl : public NewOverrideBase
+{
+ typedef ThreadCommand::NotifyEvent NotifyEvent;
+ friend class ThreadCommandQueue;
+
+public:
+
+ ThreadCommandQueueImpl(ThreadCommandQueue* queue)
+ : pQueue(queue), CommandBuffer(2048),
+ ExitEnqueued(false), ExitProcessed(false)
+ {
+ }
+ ~ThreadCommandQueueImpl();
+
+
+ bool PushCommand(const ThreadCommand& command);
+ bool PopCommand(ThreadCommand::PopBuffer* popBuffer);
+
+
+ // ExitCommand is used by notify us that Thread is shutting down.
+ struct ExitCommand : public ThreadCommand
+ {
+ ThreadCommandQueueImpl* pImpl;
+
+ ExitCommand(ThreadCommandQueueImpl* impl, bool wait)
+ : ThreadCommand(sizeof(ExitCommand), wait, true), pImpl(impl) { }
+
+ virtual void Execute() const
+ {
+ Lock::Locker lock(&pImpl->QueueLock);
+ pImpl->ExitProcessed = true;
+ }
+ virtual ThreadCommand* CopyConstruct(void* p) const
+ { return Construct<ExitCommand>(p, *this); }
+ };
+
+
+ NotifyEvent* AllocNotifyEvent_NTS()
+ {
+ NotifyEvent* p = AvailableEvents.GetFirst();
+
+ if (!AvailableEvents.IsNull(p))
+ p->RemoveNode();
+ else
+ p = new NotifyEvent;
+ return p;
+ }
+
+ void FreeNotifyEvent_NTS(NotifyEvent* p)
+ {
+ AvailableEvents.PushBack(p);
+ }
+
+ void FreeNotifyEvents_NTS()
+ {
+ while(!AvailableEvents.IsEmpty())
+ {
+ NotifyEvent* p = AvailableEvents.GetFirst();
+ p->RemoveNode();
+ delete p;
+ }
+ }
+
+ ThreadCommandQueue* pQueue;
+ Lock QueueLock;
+ volatile bool ExitEnqueued;
+ volatile bool ExitProcessed;
+ List<NotifyEvent> AvailableEvents;
+ List<NotifyEvent> BlockedProducers;
+ CircularBuffer CommandBuffer;
+};
+
+
+
+ThreadCommandQueueImpl::~ThreadCommandQueueImpl()
+{
+ Lock::Locker lock(&QueueLock);
+ OVR_ASSERT(BlockedProducers.IsEmpty());
+ FreeNotifyEvents_NTS();
+}
+
+bool ThreadCommandQueueImpl::PushCommand(const ThreadCommand& command)
+{
+ ThreadCommand::NotifyEvent* completeEvent = 0;
+ ThreadCommand::NotifyEvent* queueAvailableEvent = 0;
+
+ // Repeat writing command into buffer until it is available.
+ do {
+
+ { // Lock Scope
+ Lock::Locker lock(&QueueLock);
+
+ if (queueAvailableEvent)
+ {
+ FreeNotifyEvent_NTS(queueAvailableEvent);
+ queueAvailableEvent = 0;
+ }
+
+ // Don't allow any commands after PushExitCommand() is called.
+ if (ExitEnqueued && !command.ExitFlag)
+ return false;
+
+
+ bool bufferWasEmpty = CommandBuffer.IsEmpty();
+ UByte* buffer = CommandBuffer.Write(command.GetSize());
+ if (buffer)
+ {
+ ThreadCommand* c = command.CopyConstruct(buffer);
+ if (c->NeedsWait())
+ completeEvent = c->pEvent = AllocNotifyEvent_NTS();
+ // Signal-waker consumer when we add data to buffer.
+ if (bufferWasEmpty)
+ pQueue->OnPushNonEmpty_Locked();
+ break;
+ }
+
+ queueAvailableEvent = AllocNotifyEvent_NTS();
+ BlockedProducers.PushBack(queueAvailableEvent);
+ } // Lock Scope
+
+ queueAvailableEvent->Wait();
+
+ } while(1);
+
+ // Command was enqueued, wait if necessary.
+ if (completeEvent)
+ {
+ completeEvent->Wait();
+ Lock::Locker lock(&QueueLock);
+ FreeNotifyEvent_NTS(completeEvent);
+ }
+
+ return true;
+}
+
+
+// Pops the next command from the thread queue, if any is available.
+bool ThreadCommandQueueImpl::PopCommand(ThreadCommand::PopBuffer* popBuffer)
+{
+ Lock::Locker lock(&QueueLock);
+
+ UByte* buffer = CommandBuffer.ReadBegin();
+ if (!buffer)
+ {
+ // Notify thread while in lock scope, enabling initialization of wait.
+ pQueue->OnPopEmpty_Locked();
+ return false;
+ }
+
+ popBuffer->InitFromBuffer(buffer);
+ CommandBuffer.ReadEnd(popBuffer->GetSize());
+
+ if (!BlockedProducers.IsEmpty())
+ {
+ ThreadCommand::NotifyEvent* queueAvailableEvent = BlockedProducers.GetFirst();
+ queueAvailableEvent->RemoveNode();
+ queueAvailableEvent->PulseEvent();
+ // Event is freed later by waiter.
+ }
+ return true;
+}
+
+
+//-------------------------------------------------------------------------------------
+
+ThreadCommandQueue::ThreadCommandQueue()
+{
+ pImpl = new ThreadCommandQueueImpl(this);
+}
+ThreadCommandQueue::~ThreadCommandQueue()
+{
+ delete pImpl;
+}
+
+bool ThreadCommandQueue::PushCommand(const ThreadCommand& command)
+{
+ return pImpl->PushCommand(command);
+}
+
+bool ThreadCommandQueue::PopCommand(ThreadCommand::PopBuffer* popBuffer)
+{
+ return pImpl->PopCommand(popBuffer);
+}
+
+void ThreadCommandQueue::PushExitCommand(bool wait)
+{
+ // Exit is processed in two stages:
+ // - First, ExitEnqueued flag is set to block further commands from queuing up.
+ // - Second, the actual exit call is processed on the consumer thread, flushing
+ // any prior commands.
+ // IsExiting() only returns true after exit has flushed.
+ {
+ Lock::Locker lock(&pImpl->QueueLock);
+ if (pImpl->ExitEnqueued)
+ return;
+ pImpl->ExitEnqueued = true;
+ }
+
+ PushCommand(ThreadCommandQueueImpl::ExitCommand(pImpl, wait));
+}
+
+bool ThreadCommandQueue::IsExiting() const
+{
+ return pImpl->ExitProcessed;
+}
+
+
+} // namespace OVR
diff --git a/LibOVR/Src/OVR_ThreadCommandQueue.h b/LibOVR/Src/OVR_ThreadCommandQueue.h
new file mode 100644
index 0000000..98d6228
--- /dev/null
+++ b/LibOVR/Src/OVR_ThreadCommandQueue.h
@@ -0,0 +1,308 @@
+/************************************************************************************
+
+PublicHeader: None
+Filename : OVR_ThreadCommandQueue.h
+Content : Command queue for operations executed on a thread
+Created : October 29, 2012
+Author : Michael Antonov
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#ifndef OVR_ThreadCommandQueue_h
+#define OVR_ThreadCommandQueue_h
+
+#include "Kernel/OVR_Types.h"
+#include "Kernel/OVR_List.h"
+#include "Kernel/OVR_Atomic.h"
+#include "Kernel/OVR_Threads.h"
+
+namespace OVR {
+
+class ThreadCommand;
+class ThreadCommandQueue;
+
+
+//-------------------------------------------------------------------------------------
+// ***** ThreadCommand
+
+// ThreadCommand is a base class implementation for commands stored in ThreadCommandQueue.
+class ThreadCommand
+{
+public:
+
+ // NotifyEvent is used by ThreadCommandQueue::PushCallAndWait to notify the
+ // calling (producer) thread when command is completed or queue slot is available.
+ class NotifyEvent : public ListNode<NotifyEvent>, public NewOverrideBase
+ {
+ Event E;
+ public:
+ NotifyEvent() { }
+
+ void Wait() { E.Wait(); }
+ void PulseEvent() { E.PulseEvent(); }
+ };
+
+ // ThreadCommand::PopBuffer is temporary storage for a command popped off
+ // by ThreadCommandQueue::PopCommand.
+ class PopBuffer
+ {
+ enum { MaxSize = 256 };
+
+ UPInt Size;
+ union {
+ UByte Buffer[MaxSize];
+ UPInt Align;
+ };
+
+ ThreadCommand* toCommand() const { return (ThreadCommand*)Buffer; }
+
+ public:
+ PopBuffer() : Size(0) { }
+ ~PopBuffer();
+
+ void InitFromBuffer(void* data);
+
+ bool HasCommand() const { return Size != 0; }
+ UPInt GetSize() const { return Size; }
+ bool NeedsWait() const { return toCommand()->NeedsWait(); }
+ NotifyEvent* GetEvent() const { return toCommand()->pEvent; }
+
+ // Execute the command and also notifies caller to finish waiting,
+ // if necessary.
+ void Execute();
+ };
+
+ UInt16 Size;
+ bool WaitFlag;
+ bool ExitFlag; // Marks the last exit command.
+ NotifyEvent* pEvent;
+
+ ThreadCommand(UPInt size, bool waitFlag, bool exitFlag = false)
+ : Size((UInt16)size), WaitFlag(waitFlag), ExitFlag(exitFlag), pEvent(0) { }
+ virtual ~ThreadCommand() { }
+
+ bool NeedsWait() const { return WaitFlag; }
+ UPInt GetSize() const { return Size; }
+
+ virtual void Execute() const = 0;
+ // Copy constructor used for serializing this to memory buffer.
+ virtual ThreadCommand* CopyConstruct(void* p) const = 0;
+};
+
+
+//-------------------------------------------------------------------------------------
+
+// CleanType is a template that strips 'const' and '&' modifiers from the argument type;
+// for example, typename CleanType<A&>::Type is equivalent to A.
+template<class T> struct CleanType { typedef T Type; };
+template<class T> struct CleanType<T&> { typedef T Type; };
+template<class T> struct CleanType<const T> { typedef T Type; };
+template<class T> struct CleanType<const T&> { typedef T Type; };
+
+// SelfType is a template that yields the argument type. This helps avoid conflicts with
+// automatic template argument deduction for function calls when identical argument
+// is already defined.
+template<class T> struct SelfType { typedef T Type; };
+
+
+
+//-------------------------------------------------------------------------------------
+// ThreadCommand specializations for member functions with different number of
+// arguments and argument types.
+
+// Used to return nothing from a ThreadCommand, to avoid problems with 'void'.
+struct Void
+{
+ Void() {}
+ Void(int) {}
+};
+
+// ThreadCommand for member function with 0 arguments.
+template<class C, class R>
+class ThreadCommandMF0 : public ThreadCommand
+{
+ typedef R (C::*FnPtr)();
+ C* pClass;
+ FnPtr pFn;
+ R* pRet;
+
+ void executeImpl() const
+ {
+ pRet ? (void)(*pRet = (pClass->*pFn)()) :
+ (void)(pClass->*pFn)();
+ }
+
+public:
+ ThreadCommandMF0(C* pclass, FnPtr fn, R* ret, bool needsWait)
+ : ThreadCommand(sizeof(ThreadCommandMF0), needsWait),
+ pClass(pclass), pFn(fn), pRet(ret) { }
+
+ virtual void Execute() const { executeImpl(); }
+ virtual ThreadCommand* CopyConstruct(void* p) const
+ { return Construct<ThreadCommandMF0>(p, *this); }
+};
+
+
+// ThreadCommand for member function with 1 argument.
+template<class C, class R, class A0>
+class ThreadCommandMF1 : public ThreadCommand
+{
+ typedef R (C::*FnPtr)(A0);
+ C* pClass;
+ FnPtr pFn;
+ R* pRet;
+ typename CleanType<A0>::Type AVal0;
+
+ void executeImpl() const
+ {
+ pRet ? (void)(*pRet = (pClass->*pFn)(AVal0)) :
+ (void)(pClass->*pFn)(AVal0);
+ }
+
+public:
+ ThreadCommandMF1(C* pclass, FnPtr fn, R* ret, A0 a0, bool needsWait)
+ : ThreadCommand(sizeof(ThreadCommandMF1), needsWait),
+ pClass(pclass), pFn(fn), pRet(ret), AVal0(a0) { }
+
+ virtual void Execute() const { executeImpl(); }
+ virtual ThreadCommand* CopyConstruct(void* p) const
+ { return Construct<ThreadCommandMF1>(p, *this); }
+};
+
+// ThreadCommand for member function with 2 arguments.
+template<class C, class R, class A0, class A1>
+class ThreadCommandMF2 : public ThreadCommand
+{
+ typedef R (C::*FnPtr)(A0, A1);
+ C* pClass;
+ FnPtr pFn;
+ R* pRet;
+ typename CleanType<A0>::Type AVal0;
+ typename CleanType<A1>::Type AVal1;
+
+ void executeImpl() const
+ {
+ pRet ? (void)(*pRet = (pClass->*pFn)(AVal0, AVal1)) :
+ (void)(pClass->*pFn)(AVal0, AVal1);
+ }
+
+public:
+ ThreadCommandMF2(C* pclass, FnPtr fn, R* ret, A0 a0, A1 a1, bool needsWait)
+ : ThreadCommand(sizeof(ThreadCommandMF2), needsWait),
+ pClass(pclass), pFn(fn), pRet(ret), AVal0(a0), AVal1(a1) { }
+
+ virtual void Execute() const { executeImpl(); }
+ virtual ThreadCommand* CopyConstruct(void* p) const
+ { return Construct<ThreadCommandMF2>(p, *this); }
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** ThreadCommandQueue
+
+// ThreadCommandQueue is a queue of executable function-call commands intended to be
+// serviced by a single consumer thread. Commands are added to the queue with PushCall
+// and removed with PopCall; they are processed in FIFO order. Multiple producer threads
+// are supported and will be blocked if internal data buffer is full.
+
+class ThreadCommandQueue
+{
+public:
+
+ ThreadCommandQueue();
+ virtual ~ThreadCommandQueue();
+
+
+ // Pops the next command from the thread queue, if any is available.
+ // The command should be executed by calling popBuffer->Execute().
+ // Returns 'false' if no command is available at the time of the call.
+ bool PopCommand(ThreadCommand::PopBuffer* popBuffer);
+
+ // Generic implementaion of PushCommand; enqueues a command for execution.
+ // Returns 'false' if push failed, usually indicating thread shutdown.
+ bool PushCommand(const ThreadCommand& command);
+
+ //
+ void PushExitCommand(bool wait);
+
+ // Returns 'true' once ExitCommand has been processed, so the thread can shut down.
+ bool IsExiting() const;
+
+
+ // These two virtual functions serve as notifications for derived
+ // thread waiting.
+ virtual void OnPushNonEmpty_Locked() { }
+ virtual void OnPopEmpty_Locked() { }
+
+
+ // *** PushCall with no result
+
+ // Enqueue a member function of 'this' class to be called on consumer thread.
+ // By default the function returns immediately; set 'wait' argument to 'true' to
+ // wait for completion.
+ template<class C, class R>
+ bool PushCall(R (C::*fn)(), bool wait = false)
+ { return PushCommand(ThreadCommandMF0<C,R>(static_cast<C*>(this), fn, 0, wait)); }
+ template<class C, class R, class A0>
+ bool PushCall(R (C::*fn)(A0), typename SelfType<A0>::Type a0, bool wait = false)
+ { return PushCommand(ThreadCommandMF1<C,R,A0>(static_cast<C*>(this), fn, 0, a0, wait)); }
+ template<class C, class R, class A0, class A1>
+ bool PushCall(R (C::*fn)(A0, A1),
+ typename SelfType<A0>::Type a0, typename SelfType<A1>::Type a1, bool wait = false)
+ { return PushCommand(ThreadCommandMF2<C,R,A0,A1>(static_cast<C*>(this), fn, 0, a0, a1, wait)); }
+ // Enqueue a specified member function call of class C.
+ // By default the function returns immediately; set 'wait' argument to 'true' to
+ // wait for completion.
+ template<class C, class R>
+ bool PushCall(C* p, R (C::*fn)(), bool wait = false)
+ { return PushCommand(ThreadCommandMF0<C,R>(p, fn, 0, wait)); }
+ template<class C, class R, class A0>
+ bool PushCall(C* p, R (C::*fn)(A0), typename SelfType<A0>::Type a0, bool wait = false)
+ { return PushCommand(ThreadCommandMF1<C,R,A0>(p, fn, 0, a0, wait)); }
+ template<class C, class R, class A0, class A1>
+ bool PushCall(C* p, R (C::*fn)(A0, A1),
+ typename SelfType<A0>::Type a0, typename SelfType<A1>::Type a1, bool wait = false)
+ { return PushCommand(ThreadCommandMF2<C,R,A0,A1>(p, fn, 0, a0, a1, wait)); }
+
+
+ // *** PushCall with Result
+
+ // Enqueue a member function of 'this' class call and wait for call to complete
+ // on consumer thread before returning.
+ template<class C, class R>
+ bool PushCallAndWaitResult(R (C::*fn)(), R* ret)
+ { return PushCommand(ThreadCommandMF0<C,R>(static_cast<C*>(this), fn, ret, true)); }
+ template<class C, class R, class A0>
+ bool PushCallAndWaitResult(R (C::*fn)(A0), R* ret, typename SelfType<A0>::Type a0)
+ { return PushCommand(ThreadCommandMF1<C,R,A0>(static_cast<C*>(this), fn, ret, a0, true)); }
+ template<class C, class R, class A0, class A1>
+ bool PushCallAndWaitResult(R (C::*fn)(A0, A1), R* ret,
+ typename SelfType<A0>::Type a0, typename SelfType<A1>::Type a1)
+ { return PushCommand(ThreadCommandMF2<C,R,A0,A1>(static_cast<C*>(this), fn, ret, a0, a1, true)); }
+ // Enqueue a member function call for class C and wait for the call to complete
+ // on consumer thread before returning.
+ template<class C, class R>
+ bool PushCallAndWaitResult(C* p, R (C::*fn)(), R* ret)
+ { return PushCommand(ThreadCommandMF0<C,R>(p, fn, ret, true)); }
+ template<class C, class R, class A0>
+ bool PushCallAndWaitResult(C* p, R (C::*fn)(A0), R* ret, typename SelfType<A0>::Type a0)
+ { return PushCommand(ThreadCommandMF1<C,R,A0>(p, fn, ret, a0, true)); }
+ template<class C, class R, class A0, class A1>
+ bool PushCallAndWaitResult(C* p, R (C::*fn)(A0, A1), R* ret,
+ typename SelfType<A0>::Type a0, typename SelfType<A1>::Type a1)
+ { return PushCommand(ThreadCommandMF2<C,R,A0,A1>(p, fn, ret, a0, a1, true)); }
+
+private:
+ class ThreadCommandQueueImpl* pImpl;
+};
+
+
+}
+
+#endif // OVR_ThreadCommandQueue_h
diff --git a/LibOVR/Src/OVR_Win32_DeviceManager.cpp b/LibOVR/Src/OVR_Win32_DeviceManager.cpp
new file mode 100644
index 0000000..f7bb9eb
--- /dev/null
+++ b/LibOVR/Src/OVR_Win32_DeviceManager.cpp
@@ -0,0 +1,423 @@
+/************************************************************************************
+
+Filename : OVR_Win32_DeviceManager.cpp
+Content : Win32 implementation of DeviceManager.
+Created : September 21, 2012
+Authors : Michael Antonov
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#include "OVR_Win32_DeviceManager.h"
+
+// Sensor & HMD Factories
+#include "OVR_SensorImpl.h"
+#include "OVR_LatencyTestImpl.h"
+#include "OVR_Win32_HMDDevice.h"
+#include "OVR_Win32_DeviceStatus.h"
+#include "OVR_Win32_HIDDevice.h"
+
+#include "Kernel/OVR_Timer.h"
+#include "Kernel/OVR_Std.h"
+#include "Kernel/OVR_Log.h"
+
+DWORD Debug_WaitedObjectCount = 0;
+
+namespace OVR { namespace Win32 {
+
+
+//-------------------------------------------------------------------------------------
+// **** Win32::DeviceManager
+
+DeviceManager::DeviceManager()
+{
+ HidDeviceManager = *HIDDeviceManager::CreateInternal(this);
+}
+
+DeviceManager::~DeviceManager()
+{
+ // make sure Shutdown was called.
+ OVR_ASSERT(!pThread);
+}
+
+bool DeviceManager::Initialize(DeviceBase*)
+{
+ if (!DeviceManagerImpl::Initialize(0))
+ return false;
+
+ pThread = *new DeviceManagerThread(this);
+ if (!pThread || !pThread->Start())
+ return false;
+
+ pCreateDesc->pDevice = this;
+ LogText("OVR::DeviceManager - initialized.\n");
+ return true;
+}
+
+void DeviceManager::Shutdown()
+{
+ LogText("OVR::DeviceManager - shutting down.\n");
+
+ // Set Manager shutdown marker variable; this prevents
+ // any existing DeviceHandle objects from accessing device.
+ pCreateDesc->pLock->pManager = 0;
+
+ // Push for thread shutdown *WITH NO WAIT*.
+ // This will have the following effect:
+ // - Exit command will get enqueued, which will be executed later on the thread itself.
+ // - Beyond this point, this DeviceManager object may be deleted by our caller.
+ // - Other commands, such as CreateDevice, may execute before ExitCommand, but they will
+ // fail gracefully due to pLock->pManager == 0. Future commands can't be enqued
+ // after pManager is null.
+ // - Once ExitCommand executes, ThreadCommand::Run loop will exit and release the last
+ // reference to the thread object.
+ pThread->PushExitCommand(false);
+ pThread->DetachDeviceManager();
+ pThread.Clear();
+
+ DeviceManagerImpl::Shutdown();
+}
+
+ThreadCommandQueue* DeviceManager::GetThreadQueue()
+{
+ return pThread;
+}
+
+bool DeviceManager::GetDeviceInfo(DeviceInfo* info) const
+{
+ if ((info->InfoClassType != Device_Manager) &&
+ (info->InfoClassType != Device_None))
+ return false;
+
+ info->Type = Device_Manager;
+ info->Version = 0;
+ OVR_strcpy(info->ProductName, DeviceInfo::MaxNameLength, "DeviceManager");
+ OVR_strcpy(info->Manufacturer,DeviceInfo::MaxNameLength, "Oculus VR, Inc.");
+ return true;
+}
+
+DeviceEnumerator<> DeviceManager::EnumerateDevicesEx(const DeviceEnumerationArgs& args)
+{
+ // TBD: Can this be avoided in the future, once proper device notification is in place?
+ if (GetThreadId() != OVR::GetCurrentThreadId())
+ {
+ pThread->PushCall((DeviceManagerImpl*)this,
+ &DeviceManager::EnumerateAllFactoryDevices, true);
+ }
+ else
+ DeviceManager::EnumerateAllFactoryDevices();
+
+ return DeviceManagerImpl::EnumerateDevicesEx(args);
+}
+
+ThreadId DeviceManager::GetThreadId() const
+{
+ return pThread->GetThreadId();
+}
+
+bool DeviceManager::GetHIDDeviceDesc(const String& path, HIDDeviceDesc* pdevDesc) const
+{
+ if (GetHIDDeviceManager())
+ return static_cast<HIDDeviceManager*>(GetHIDDeviceManager())->GetHIDDeviceDesc(path, pdevDesc);
+ return false;
+}
+
+
+//-------------------------------------------------------------------------------------
+// ***** DeviceManager Thread
+
+DeviceManagerThread::DeviceManagerThread(DeviceManager* pdevMgr)
+ : Thread(ThreadStackSize), hCommandEvent(0), pDeviceMgr(pdevMgr)
+{
+ // Create a non-signaled manual-reset event.
+ hCommandEvent = ::CreateEvent(0, TRUE, FALSE, 0);
+ if (!hCommandEvent)
+ return;
+
+ // Must add event before starting.
+ AddOverlappedEvent(0, hCommandEvent);
+
+ // Create device messages object.
+ pStatusObject = *new DeviceStatus(this);
+}
+
+DeviceManagerThread::~DeviceManagerThread()
+{
+ // Remove overlapped event [0], after thread service exit.
+ if (hCommandEvent)
+ {
+ RemoveOverlappedEvent(0, hCommandEvent);
+ ::CloseHandle(hCommandEvent);
+ hCommandEvent = 0;
+ }
+}
+
+int DeviceManagerThread::Run()
+{
+ ThreadCommand::PopBuffer command;
+
+ SetThreadName("OVR::DeviceManagerThread");
+ LogText("OVR::DeviceManagerThread - running (ThreadId=0x%X).\n", GetThreadId());
+
+ if (!pStatusObject->Initialize())
+ {
+ LogText("OVR::DeviceManagerThread - failed to initialize MessageObject.\n");
+ }
+
+ while(!IsExiting())
+ {
+ // PopCommand will reset event on empty queue.
+ if (PopCommand(&command))
+ {
+ command.Execute();
+ }
+ else
+ {
+ DWORD eventIndex = 0;
+ do {
+ UPInt numberOfWaitHandles = WaitHandles.GetSize();
+ Debug_WaitedObjectCount = (DWORD)numberOfWaitHandles;
+
+ DWORD waitMs = INFINITE;
+
+ // If devices have time-dependent logic registered, get the longest wait
+ // allowed based on current ticks.
+ if (!TicksNotifiers.IsEmpty())
+ {
+ UInt64 ticksMks = Timer::GetTicks();
+ DWORD waitAllowed;
+
+ for (UPInt j = 0; j < TicksNotifiers.GetSize(); j++)
+ {
+ waitAllowed = (DWORD)(TicksNotifiers[j]->OnTicks(ticksMks) / Timer::MksPerMs);
+ if (waitAllowed < waitMs)
+ waitMs = waitAllowed;
+ }
+ }
+
+ // Wait for event signals or window messages.
+ eventIndex = MsgWaitForMultipleObjects((DWORD)numberOfWaitHandles, &WaitHandles[0], FALSE, waitMs, QS_ALLINPUT);
+
+ if (eventIndex != WAIT_FAILED)
+ {
+ if (eventIndex == WAIT_TIMEOUT)
+ continue;
+
+ // TBD: Does this ever apply?
+ OVR_ASSERT(eventIndex < WAIT_ABANDONED_0);
+
+ if (eventIndex == WAIT_OBJECT_0)
+ {
+ // Handle [0] services commands.
+ break;
+ }
+ else if (eventIndex == WAIT_OBJECT_0 + numberOfWaitHandles)
+ {
+ // Handle Windows messages.
+ pStatusObject->ProcessMessages();
+ }
+ else
+ {
+ // Notify waiting device that its event is signaled.
+ unsigned i = eventIndex - WAIT_OBJECT_0;
+ OVR_ASSERT(i < numberOfWaitHandles);
+ if (WaitNotifiers[i])
+ WaitNotifiers[i]->OnOverlappedEvent(WaitHandles[i]);
+ }
+ }
+
+ } while(eventIndex != WAIT_FAILED);
+
+ }
+ }
+
+ pStatusObject->ShutDown();
+
+ LogText("OVR::DeviceManagerThread - exiting (ThreadId=0x%X).\n", GetThreadId());
+ return 0;
+}
+
+bool DeviceManagerThread::AddOverlappedEvent(Notifier* notify, HANDLE hevent)
+{
+ WaitNotifiers.PushBack(notify);
+ WaitHandles.PushBack(hevent);
+
+ OVR_ASSERT(WaitNotifiers.GetSize() <= MAXIMUM_WAIT_OBJECTS);
+ return true;
+}
+
+bool DeviceManagerThread::RemoveOverlappedEvent(Notifier* notify, HANDLE hevent)
+{
+ // [0] is reserved for thread commands with notify of null, but we still
+ // can use this function to remove it.
+ for (UPInt i = 0; i < WaitNotifiers.GetSize(); i++)
+ {
+ if ((WaitNotifiers[i] == notify) && (WaitHandles[i] == hevent))
+ {
+ WaitNotifiers.RemoveAt(i);
+ WaitHandles.RemoveAt(i);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool DeviceManagerThread::AddTicksNotifier(Notifier* notify)
+{
+ TicksNotifiers.PushBack(notify);
+ return true;
+}
+
+bool DeviceManagerThread::RemoveTicksNotifier(Notifier* notify)
+{
+ for (UPInt i = 0; i < TicksNotifiers.GetSize(); i++)
+ {
+ if (TicksNotifiers[i] == notify)
+ {
+ TicksNotifiers.RemoveAt(i);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool DeviceManagerThread::AddMessageNotifier(Notifier* notify)
+{
+ MessageNotifiers.PushBack(notify);
+ return true;
+}
+
+bool DeviceManagerThread::RemoveMessageNotifier(Notifier* notify)
+{
+ for (UPInt i = 0; i < MessageNotifiers.GetSize(); i++)
+ {
+ if (MessageNotifiers[i] == notify)
+ {
+ MessageNotifiers.RemoveAt(i);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool DeviceManagerThread::OnMessage(MessageType type, const String& devicePath)
+{
+ Notifier::DeviceMessageType notifierMessageType = Notifier::DeviceMessage_DeviceAdded;
+ if (type == DeviceAdded)
+ {
+ }
+ else if (type == DeviceRemoved)
+ {
+ notifierMessageType = Notifier::DeviceMessage_DeviceRemoved;
+ }
+ else
+ {
+ OVR_ASSERT(false);
+ }
+
+ bool error = false;
+ bool deviceFound = false;
+ for (UPInt i = 0; i < MessageNotifiers.GetSize(); i++)
+ {
+ if (MessageNotifiers[i] &&
+ MessageNotifiers[i]->OnDeviceMessage(notifierMessageType, devicePath, &error))
+ {
+ // The notifier belonged to a device with the specified device name so we're done.
+ deviceFound = true;
+ break;
+ }
+ }
+ if (type == DeviceAdded && !deviceFound)
+ {
+ Lock::Locker devMgrLock(&DevMgrLock);
+ // a new device was connected. Go through all device factories and
+ // try to detect the device using HIDDeviceDesc.
+ HIDDeviceDesc devDesc;
+ if (pDeviceMgr->GetHIDDeviceDesc(devicePath, &devDesc))
+ {
+ Lock::Locker deviceLock(pDeviceMgr->GetLock());
+ DeviceFactory* factory = pDeviceMgr->Factories.GetFirst();
+ while(!pDeviceMgr->Factories.IsNull(factory))
+ {
+ if (factory->DetectHIDDevice(pDeviceMgr, devDesc))
+ {
+ deviceFound = true;
+ break;
+ }
+ factory = factory->pNext;
+ }
+ }
+ }
+
+ if (!deviceFound && strstr(devicePath.ToCStr(), "#OVR00"))
+ {
+ Ptr<DeviceManager> pmgr;
+ {
+ Lock::Locker devMgrLock(&DevMgrLock);
+ pmgr = pDeviceMgr;
+ }
+ // HMD plugged/unplugged
+ // This is not a final solution to enumerate HMD devices and get
+ // a first available handle. This won't work with multiple rifts.
+ // @TODO (!AB)
+ pmgr->EnumerateDevices<HMDDevice>();
+ }
+
+ return !error;
+}
+
+void DeviceManagerThread::DetachDeviceManager()
+{
+ Lock::Locker devMgrLock(&DevMgrLock);
+ pDeviceMgr = NULL;
+}
+
+} // namespace Win32
+
+
+//-------------------------------------------------------------------------------------
+// ***** Creation
+
+
+// Creates a new DeviceManager and initializes OVR.
+DeviceManager* DeviceManager::Create()
+{
+
+ if (!System::IsInitialized())
+ {
+ // Use custom message, since Log is not yet installed.
+ OVR_DEBUG_STATEMENT(Log::GetDefaultLog()->
+ LogMessage(Log_Debug, "DeviceManager::Create failed - OVR::System not initialized"); );
+ return 0;
+ }
+
+ Ptr<Win32::DeviceManager> manager = *new Win32::DeviceManager;
+
+ if (manager)
+ {
+ if (manager->Initialize(0))
+ {
+ manager->AddFactory(&SensorDeviceFactory::Instance);
+ manager->AddFactory(&LatencyTestDeviceFactory::Instance);
+ manager->AddFactory(&Win32::HMDDeviceFactory::Instance);
+
+ manager->AddRef();
+ }
+ else
+ {
+ manager.Clear();
+ }
+
+ }
+
+ return manager.GetPtr();
+}
+
+
+} // namespace OVR
+
diff --git a/LibOVR/Src/OVR_Win32_DeviceManager.h b/LibOVR/Src/OVR_Win32_DeviceManager.h
new file mode 100644
index 0000000..3b86c67
--- /dev/null
+++ b/LibOVR/Src/OVR_Win32_DeviceManager.h
@@ -0,0 +1,146 @@
+/************************************************************************************
+
+Filename : OVR_Win32_DeviceManager.h
+Content : Win32-specific DeviceManager header.
+Created : September 21, 2012
+Authors : Michael Antonov
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_Win32_DeviceManager_h
+#define OVR_Win32_DeviceManager_h
+
+#include "OVR_DeviceImpl.h"
+#include "OVR_Win32_DeviceStatus.h"
+
+#include "Kernel/OVR_Timer.h"
+
+
+namespace OVR { namespace Win32 {
+
+class DeviceManagerThread;
+
+//-------------------------------------------------------------------------------------
+// ***** Win32 DeviceManager
+
+class DeviceManager : public DeviceManagerImpl
+{
+public:
+ DeviceManager();
+ ~DeviceManager();
+
+ // Initialize/Shutdowncreate and shutdown manger thread.
+ virtual bool Initialize(DeviceBase* parent);
+ virtual void Shutdown();
+
+ virtual ThreadCommandQueue* GetThreadQueue();
+ virtual ThreadId GetThreadId() const;
+
+ virtual DeviceEnumerator<> EnumerateDevicesEx(const DeviceEnumerationArgs& args);
+
+ virtual bool GetDeviceInfo(DeviceInfo* info) const;
+
+ // Fills HIDDeviceDesc by using the path.
+ // Returns 'true' if successful, 'false' otherwise.
+ bool GetHIDDeviceDesc(const String& path, HIDDeviceDesc* pdevDesc) const;
+
+ Ptr<DeviceManagerThread> pThread;
+};
+
+//-------------------------------------------------------------------------------------
+// ***** Device Manager Background Thread
+
+class DeviceManagerThread : public Thread, public ThreadCommandQueue, public DeviceStatus::Notifier
+{
+ friend class DeviceManager;
+ enum { ThreadStackSize = 32 * 1024 };
+public:
+ DeviceManagerThread(DeviceManager* pdevMgr);
+ ~DeviceManagerThread();
+
+ virtual int Run();
+
+ // ThreadCommandQueue notifications for CommandEvent handling.
+ virtual void OnPushNonEmpty_Locked() { ::SetEvent(hCommandEvent); }
+ virtual void OnPopEmpty_Locked() { ::ResetEvent(hCommandEvent); }
+
+
+ // Notifier used for different updates (EVENT or regular timing or messages).
+ class Notifier
+ {
+ public:
+ // Called when overlapped I/O handle is signaled.
+ virtual void OnOverlappedEvent(HANDLE hevent) { OVR_UNUSED1(hevent); }
+
+ // Called when timing ticks are updated.
+ // Returns the largest number of microseconds this function can
+ // wait till next call.
+ virtual UInt64 OnTicks(UInt64 ticksMks)
+ { OVR_UNUSED1(ticksMks); return Timer::MksPerSecond * 1000; }
+
+ enum DeviceMessageType
+ {
+ DeviceMessage_DeviceAdded = 0,
+ DeviceMessage_DeviceRemoved = 1,
+ };
+
+ // Called to notify device object.
+ virtual bool OnDeviceMessage(DeviceMessageType messageType,
+ const String& devicePath,
+ bool* error)
+ { OVR_UNUSED3(messageType, devicePath, error); return false; }
+ };
+
+
+ // Adds device's OVERLAPPED structure for I/O.
+ // After it's added, Overlapped object will be signaled if a message arrives.
+ bool AddOverlappedEvent(Notifier* notify, HANDLE hevent);
+ bool RemoveOverlappedEvent(Notifier* notify, HANDLE hevent);
+
+ // Add notifier that will be called at regular intervals.
+ bool AddTicksNotifier(Notifier* notify);
+ bool RemoveTicksNotifier(Notifier* notify);
+
+ bool AddMessageNotifier(Notifier* notify);
+ bool RemoveMessageNotifier(Notifier* notify);
+
+ // DeviceStatus::Notifier interface.
+ bool OnMessage(MessageType type, const String& devicePath);
+
+ void DetachDeviceManager();
+
+private:
+ bool threadInitialized() { return hCommandEvent != 0; }
+
+ // Event used to wake us up thread commands are enqueued.
+ HANDLE hCommandEvent;
+
+ // Event notifications for devices whose OVERLAPPED I/O we service.
+ // This list is modified through AddDeviceOverlappedEvent.
+ // WaitHandles[0] always == hCommandEvent, with null device.
+ Array<HANDLE> WaitHandles;
+ Array<Notifier*> WaitNotifiers;
+
+ // Ticks notifiers - used for time-dependent events such as keep-alive.
+ Array<Notifier*> TicksNotifiers;
+
+ // Message notifiers.
+ Array<Notifier*> MessageNotifiers;
+
+ // Object that manages notifications originating from Windows messages.
+ Ptr<DeviceStatus> pStatusObject;
+
+ Lock DevMgrLock;
+ // pDeviceMgr should be accessed under DevMgrLock
+ DeviceManager* pDeviceMgr; // back ptr, no addref.
+};
+
+}} // namespace Win32::OVR
+
+#endif // OVR_Win32_DeviceManager_h
diff --git a/LibOVR/Src/OVR_Win32_DeviceStatus.cpp b/LibOVR/Src/OVR_Win32_DeviceStatus.cpp
new file mode 100644
index 0000000..80ec4f9
--- /dev/null
+++ b/LibOVR/Src/OVR_Win32_DeviceStatus.cpp
@@ -0,0 +1,350 @@
+/************************************************************************************
+
+Filename : OVR_Win32_DeviceStatus.cpp
+Content : Win32 implementation of DeviceStatus.
+Created : January 24, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#include "OVR_Win32_DeviceStatus.h"
+
+#include "OVR_Win32_HIDDevice.h"
+
+#include "Kernel/OVR_Log.h"
+
+#include <dbt.h>
+
+namespace OVR { namespace Win32 {
+
+static TCHAR windowClassName[] = TEXT("LibOVR_DeviceStatus_WindowClass");
+
+//-------------------------------------------------------------------------------------
+DeviceStatus::DeviceStatus(Notifier* const pClient)
+ : pNotificationClient(pClient), LastTimerId(0)
+{
+}
+
+bool DeviceStatus::Initialize()
+{
+
+ WNDCLASS wndClass;
+ wndClass.style = CS_HREDRAW | CS_VREDRAW;
+ wndClass.lpfnWndProc = WindowsMessageCallback;
+ wndClass.cbClsExtra = 0;
+ wndClass.cbWndExtra = 0;
+ wndClass.hInstance = 0;
+ wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
+ wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wndClass.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
+ wndClass.lpszMenuName = NULL;
+ wndClass.lpszClassName = windowClassName;
+
+ if (!RegisterClass(&wndClass))
+ {
+ OVR_ASSERT_LOG(false, ("Failed to register window class."));
+ return false;
+ }
+
+ // We're going to create a 'message-only' window. This will be hidden, can't be enumerated etc.
+ // To do this we supply 'HWND_MESSAGE' as the hWndParent.
+ // http://msdn.microsoft.com/en-us/library/ms632599%28VS.85%29.aspx#message_only
+ hMessageWindow = CreateWindow( windowClassName,
+ windowClassName,
+ WS_OVERLAPPEDWINDOW,
+ CW_USEDEFAULT,
+ CW_USEDEFAULT,
+ CW_USEDEFAULT,
+ CW_USEDEFAULT,
+ HWND_MESSAGE,
+ NULL,
+ 0,
+ this); // Pass this object via the CREATESTRUCT mechanism
+ // so that we can attach it to the window user data.
+
+ if (hMessageWindow == NULL)
+ {
+ OVR_ASSERT_LOG(false, ("Failed to create window."));
+ return false;
+ }
+
+ // According to MS, topmost windows receive WM_DEVICECHANGE faster.
+ ::SetWindowPos(hMessageWindow, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
+ UpdateWindow(hMessageWindow);
+
+
+ // Register notification for additional HID messages.
+ HIDDeviceManager* hidDeviceManager = new HIDDeviceManager(NULL);
+ HidGuid = hidDeviceManager->GetHIDGuid();
+ hidDeviceManager->Release();
+
+ DEV_BROADCAST_DEVICEINTERFACE notificationFilter;
+
+ ZeroMemory(&notificationFilter, sizeof(notificationFilter));
+ notificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
+ notificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
+ //notificationFilter.dbcc_classguid = hidguid;
+
+ // We need DEVICE_NOTIFY_ALL_INTERFACE_CLASSES to detect
+ // HDMI plug/unplug events.
+ hDeviceNotify = RegisterDeviceNotification(
+ hMessageWindow,
+ &notificationFilter,
+ DEVICE_NOTIFY_ALL_INTERFACE_CLASSES|DEVICE_NOTIFY_WINDOW_HANDLE);
+
+ if (hDeviceNotify == NULL)
+ {
+ OVR_ASSERT_LOG(false, ("Failed to register for device notifications."));
+ return false;
+ }
+
+ return true;
+}
+
+void DeviceStatus::ShutDown()
+{
+ OVR_ASSERT(hMessageWindow);
+
+ if (!UnregisterDeviceNotification(hDeviceNotify))
+ {
+ OVR_ASSERT_LOG(false, ("Failed to unregister device notification."));
+ }
+
+ PostMessage(hMessageWindow, WM_CLOSE, 0, 0);
+
+ while (hMessageWindow != NULL)
+ {
+ ProcessMessages();
+ Sleep(1);
+ }
+
+ if (!UnregisterClass(windowClassName, NULL))
+ {
+ OVR_ASSERT_LOG(false, ("Failed to unregister window class."));
+ }
+}
+
+DeviceStatus::~DeviceStatus()
+{
+ OVR_ASSERT_LOG(hMessageWindow == NULL, ("Need to call 'ShutDown' from DeviceManagerThread."));
+}
+
+void DeviceStatus::ProcessMessages()
+{
+ OVR_ASSERT_LOG(hMessageWindow != NULL, ("Need to call 'Initialize' before first use."));
+
+ MSG msg;
+
+ // Note WM_DEVICECHANGED messages are dispatched but not retrieved by PeekMessage.
+ // I think this is because they are pending, non-queued messages.
+ while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+}
+
+bool DeviceStatus::MessageCallback(WORD messageType, const String& devicePath)
+{
+ bool rv = true;
+ if (messageType == DBT_DEVICEARRIVAL)
+ {
+ rv = pNotificationClient->OnMessage(Notifier::DeviceAdded, devicePath);
+ }
+ else if (messageType == DBT_DEVICEREMOVECOMPLETE)
+ {
+ pNotificationClient->OnMessage(Notifier::DeviceRemoved, devicePath);
+ }
+ else
+ {
+ OVR_ASSERT(0);
+ }
+ return rv;
+}
+
+void DeviceStatus::CleanupRecoveryTimer(UPInt index)
+{
+ ::KillTimer(hMessageWindow, RecoveryTimers[index].TimerId);
+ RecoveryTimers.RemoveAt(index);
+}
+
+DeviceStatus::RecoveryTimerDesc*
+DeviceStatus::FindRecoveryTimer(UINT_PTR timerId, UPInt* pindex)
+{
+ for (UPInt i = 0, n = RecoveryTimers.GetSize(); i < n; ++i)
+ {
+ RecoveryTimerDesc* pdesc = &RecoveryTimers[i];
+ if (pdesc->TimerId == timerId)
+ {
+ *pindex = i;
+ return pdesc;
+ }
+ }
+ return NULL;
+}
+
+void DeviceStatus::FindAndCleanupRecoveryTimer(const String& devicePath)
+{
+ for (UPInt i = 0, n = RecoveryTimers.GetSize(); i < n; ++i)
+ {
+ RecoveryTimerDesc* pdesc = &RecoveryTimers[i];
+ if (pdesc->DevicePath.CompareNoCase(devicePath))
+ {
+ CleanupRecoveryTimer(i);
+ break;
+ }
+ }
+}
+
+LRESULT CALLBACK DeviceStatus::WindowsMessageCallback( HWND hwnd,
+ UINT message,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ switch (message)
+ {
+ case WM_CREATE:
+ {
+ // Setup window user data with device status object pointer.
+ LPCREATESTRUCT create_struct = reinterpret_cast<LPCREATESTRUCT>(lParam);
+ void *lpCreateParam = create_struct->lpCreateParams;
+ DeviceStatus *pDeviceStatus = reinterpret_cast<DeviceStatus*>(lpCreateParam);
+
+ SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pDeviceStatus));
+ }
+ return 0; // Return 0 for successfully handled WM_CREATE.
+
+ case WM_DEVICECHANGE:
+ {
+ WORD loword = LOWORD(wParam);
+
+ if (loword != DBT_DEVICEARRIVAL &&
+ loword != DBT_DEVICEREMOVECOMPLETE)
+ {
+ // Ignore messages other than device arrive and remove complete
+ // (we're not handling intermediate ones).
+ return TRUE; // Grant WM_DEVICECHANGE request.
+ }
+
+ DEV_BROADCAST_DEVICEINTERFACE* hdr;
+ hdr = (DEV_BROADCAST_DEVICEINTERFACE*) lParam;
+
+ if (hdr->dbcc_devicetype != DBT_DEVTYP_DEVICEINTERFACE)
+ {
+ // Ignore non interface device messages.
+ return TRUE; // Grant WM_DEVICECHANGE request.
+ }
+
+ LONG_PTR userData = GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ OVR_ASSERT(userData != NULL);
+
+ // Call callback on device messages object with the device path.
+ DeviceStatus* pDeviceStatus = (DeviceStatus*) userData;
+ String devicePath(hdr->dbcc_name);
+
+ // check if HID device caused the event...
+ if (pDeviceStatus->HidGuid == hdr->dbcc_classguid)
+ {
+ // check if recovery timer is already running; stop it and
+ // remove it, if so.
+ pDeviceStatus->FindAndCleanupRecoveryTimer(devicePath);
+
+ if (!pDeviceStatus->MessageCallback(loword, devicePath))
+ {
+ // hmmm.... unsuccessful
+ if (loword == DBT_DEVICEARRIVAL)
+ {
+ // Windows sometimes may return errors ERROR_SHARING_VIOLATION and
+ // ERROR_FILE_NOT_FOUND when trying to open an USB device via
+ // CreateFile. Need to start a recovery timer that will try to
+ // re-open the device again.
+ OVR_DEBUG_LOG(("Adding failed, recovering through a timer..."));
+ UINT_PTR tid = ::SetTimer(hwnd, ++pDeviceStatus->LastTimerId,
+ USBRecoveryTimeInterval, NULL);
+ RecoveryTimerDesc rtDesc;
+ rtDesc.TimerId = tid;
+ rtDesc.DevicePath = devicePath;
+ rtDesc.NumAttempts= 0;
+ pDeviceStatus->RecoveryTimers.PushBack(rtDesc);
+ // wrap around the timer counter, avoid timerId == 0...
+ if (pDeviceStatus->LastTimerId + 1 == 0)
+ pDeviceStatus->LastTimerId = 0;
+ }
+ }
+ }
+ // Check if Oculus HDMI device was plugged/unplugged, preliminary
+ // filtering. (is there any way to get GUID? !AB)
+ //else if (strstr(devicePath.ToCStr(), "DISPLAY#"))
+ else if (strstr(devicePath.ToCStr(), "#OVR00"))
+ {
+ pDeviceStatus->MessageCallback(loword, devicePath);
+ }
+ }
+ return TRUE; // Grant WM_DEVICECHANGE request.
+
+ case WM_TIMER:
+ {
+ if (wParam != 0)
+ {
+ LONG_PTR userData = GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ OVR_ASSERT(userData != NULL);
+
+ // Call callback on device messages object with the device path.
+ DeviceStatus* pDeviceStatus = (DeviceStatus*) userData;
+
+ // Check if we have recovery timer running (actually, we must be!)
+ UPInt rtIndex;
+ RecoveryTimerDesc* prtDesc = pDeviceStatus->FindRecoveryTimer(wParam, &rtIndex);
+ if (prtDesc)
+ {
+ if (pDeviceStatus->MessageCallback(DBT_DEVICEARRIVAL, prtDesc->DevicePath))
+ {
+ OVR_DEBUG_LOG(("Recovered, adding is successful, cleaning up the timer..."));
+ // now it is successful, kill the timer and cleanup
+ pDeviceStatus->CleanupRecoveryTimer(rtIndex);
+ }
+ else
+ {
+ if (++prtDesc->NumAttempts >= MaxUSBRecoveryAttempts)
+ {
+ OVR_DEBUG_LOG(("Failed to recover USB after %d attempts, path = '%s', aborting...",
+ prtDesc->NumAttempts, prtDesc->DevicePath.ToCStr()));
+ pDeviceStatus->CleanupRecoveryTimer(rtIndex);
+ }
+ else
+ {
+ OVR_DEBUG_LOG(("Failed to recover USB, %d attempts, path = '%s'",
+ prtDesc->NumAttempts, prtDesc->DevicePath.ToCStr()));
+ }
+ }
+ }
+ }
+ }
+ return 0;
+
+ case WM_CLOSE:
+ {
+ LONG_PTR userData = GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ OVR_ASSERT(userData != NULL);
+ DeviceStatus* pDeviceStatus = (DeviceStatus*) userData;
+ pDeviceStatus->hMessageWindow = NULL;
+
+ DestroyWindow(hwnd);
+ }
+ return 0; // We processed the WM_CLOSE message.
+
+ case WM_DESTROY:
+ PostQuitMessage(0);
+ return 0; // We processed the WM_DESTROY message.
+ }
+
+ return DefWindowProc(hwnd, message, wParam, lParam);
+}
+
+}} // namespace OVR::Win32
diff --git a/LibOVR/Src/OVR_Win32_DeviceStatus.h b/LibOVR/Src/OVR_Win32_DeviceStatus.h
new file mode 100644
index 0000000..820e3a5
--- /dev/null
+++ b/LibOVR/Src/OVR_Win32_DeviceStatus.h
@@ -0,0 +1,101 @@
+/************************************************************************************
+
+Filename : OVR_Win32_DeviceStatus.h
+Content : Win32-specific DeviceStatus header.
+Created : January 24, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_Win32_DeviceStatus_h
+#define OVR_Win32_DeviceStatus_h
+
+#include <windows.h>
+#include "Kernel/OVR_String.h"
+#include "Kernel/OVR_RefCount.h"
+#include "Kernel/OVR_Array.h"
+
+namespace OVR { namespace Win32 {
+
+//-------------------------------------------------------------------------------------
+// ***** DeviceStatus
+//
+// DeviceStatus abstracts the handling of windows messages of interest for
+// example the WM_DEVICECHANGED message which occurs when a device is plugged/unplugged.
+// The device manager thread creates an instance of this class and passes its pointer
+// in the constructor. That thread is also responsible for periodically calling 'ProcessMessages'
+// to process queued windows messages. The client is notified via the 'OnMessage' method
+// declared in the 'DeviceMessages::Notifier' interface.
+class DeviceStatus : public RefCountBase<DeviceStatus>
+{
+public:
+
+ // Notifier used for device messages.
+ class Notifier
+ {
+ public:
+ enum MessageType
+ {
+ DeviceAdded = 0,
+ DeviceRemoved = 1,
+ };
+
+ virtual bool OnMessage(MessageType type, const String& devicePath)
+ { OVR_UNUSED2(type, devicePath); return true; }
+ };
+
+ DeviceStatus(Notifier* const pClient);
+ ~DeviceStatus();
+
+ void operator = (const DeviceStatus&); // No assignment implementation.
+
+ bool Initialize();
+ void ShutDown();
+
+ void ProcessMessages();
+
+private:
+ enum
+ {
+ MaxUSBRecoveryAttempts = 20,
+ USBRecoveryTimeInterval = 500 // ms
+ };
+ struct RecoveryTimerDesc
+ {
+ UINT_PTR TimerId;
+ String DevicePath;
+ unsigned NumAttempts;
+ };
+
+ static LRESULT CALLBACK WindowsMessageCallback( HWND hwnd,
+ UINT message,
+ WPARAM wParam,
+ LPARAM lParam);
+
+ bool MessageCallback(WORD messageType, const String& devicePath);
+
+ void CleanupRecoveryTimer(UPInt index);
+ RecoveryTimerDesc* FindRecoveryTimer(UINT_PTR timerId, UPInt* pindex);
+ void FindAndCleanupRecoveryTimer(const String& devicePath);
+
+private: // data
+ Notifier* const pNotificationClient; // Don't reference count a back-pointer.
+
+ HWND hMessageWindow;
+ HDEVNOTIFY hDeviceNotify;
+
+ UINT_PTR LastTimerId;
+ Array<RecoveryTimerDesc> RecoveryTimers;
+
+ GUID HidGuid;
+};
+
+}} // namespace OVR::Win32
+
+#endif // OVR_Win32_DeviceStatus_h
diff --git a/LibOVR/Src/OVR_Win32_HIDDevice.cpp b/LibOVR/Src/OVR_Win32_HIDDevice.cpp
new file mode 100644
index 0000000..8c04bcf
--- /dev/null
+++ b/LibOVR/Src/OVR_Win32_HIDDevice.cpp
@@ -0,0 +1,637 @@
+/************************************************************************************
+
+Filename : OVR_Win32_HIDDevice.cpp
+Content : Win32 HID device implementation.
+Created : February 22, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#include "OVR_Win32_HIDDevice.h"
+#include "OVR_Win32_DeviceManager.h"
+
+#include "Kernel/OVR_System.h"
+#include "Kernel/OVR_Log.h"
+
+namespace OVR { namespace Win32 {
+
+//-------------------------------------------------------------------------------------
+// HIDDevicePathWrapper is a simple class used to extract HID device file path
+// through SetupDiGetDeviceInterfaceDetail. We use a class since this is a bit messy.
+class HIDDevicePathWrapper
+{
+ SP_INTERFACE_DEVICE_DETAIL_DATA_A* pData;
+public:
+ HIDDevicePathWrapper() : pData(0) { }
+ ~HIDDevicePathWrapper() { if (pData) OVR_FREE(pData); }
+
+ const char* GetPath() const { return pData ? pData->DevicePath : 0; }
+
+ bool InitPathFromInterfaceData(HDEVINFO hdevInfoSet, SP_DEVICE_INTERFACE_DATA* pidata);
+};
+
+bool HIDDevicePathWrapper::InitPathFromInterfaceData(HDEVINFO hdevInfoSet, SP_DEVICE_INTERFACE_DATA* pidata)
+{
+ DWORD detailSize = 0;
+ // SetupDiGetDeviceInterfaceDetailA returns "not enough buffer error code"
+ // doe size request. Just check valid size.
+ SetupDiGetDeviceInterfaceDetailA(hdevInfoSet, pidata, NULL, 0, &detailSize, NULL);
+ if (!detailSize ||
+ ((pData = (SP_INTERFACE_DEVICE_DETAIL_DATA_A*)OVR_ALLOC(detailSize)) == 0))
+ return false;
+ pData->cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA_A);
+
+ if (!SetupDiGetDeviceInterfaceDetailA(hdevInfoSet, pidata, pData, detailSize, NULL, NULL))
+ return false;
+ return true;
+}
+
+
+//-------------------------------------------------------------------------------------
+// **** Win32::DeviceManager
+
+HIDDeviceManager::HIDDeviceManager(DeviceManager* manager)
+ : Manager(manager)
+{
+ hHidLib = ::LoadLibraryA("hid.dll");
+ OVR_ASSERT_LOG(hHidLib, ("Couldn't load Win32 'hid.dll'."));
+
+ OVR_RESOLVE_HIDFUNC(HidD_GetHidGuid);
+ OVR_RESOLVE_HIDFUNC(HidD_SetNumInputBuffers);
+ OVR_RESOLVE_HIDFUNC(HidD_GetFeature);
+ OVR_RESOLVE_HIDFUNC(HidD_SetFeature);
+ OVR_RESOLVE_HIDFUNC(HidD_GetAttributes);
+ OVR_RESOLVE_HIDFUNC(HidD_GetManufacturerString);
+ OVR_RESOLVE_HIDFUNC(HidD_GetProductString);
+ OVR_RESOLVE_HIDFUNC(HidD_GetSerialNumberString);
+ OVR_RESOLVE_HIDFUNC(HidD_GetPreparsedData);
+ OVR_RESOLVE_HIDFUNC(HidD_FreePreparsedData);
+ OVR_RESOLVE_HIDFUNC(HidP_GetCaps);
+
+ if (HidD_GetHidGuid)
+ HidD_GetHidGuid(&HidGuid);
+}
+
+HIDDeviceManager::~HIDDeviceManager()
+{
+ ::FreeLibrary(hHidLib);
+}
+
+bool HIDDeviceManager::Initialize()
+{
+ return true;
+}
+
+void HIDDeviceManager::Shutdown()
+{
+ LogText("OVR::Win32::HIDDeviceManager - shutting down.\n");
+}
+
+bool HIDDeviceManager::Enumerate(HIDEnumerateVisitor* enumVisitor)
+{
+ HDEVINFO hdevInfoSet;
+ SP_DEVICE_INTERFACE_DATA interfaceData;
+ interfaceData.cbSize = sizeof(interfaceData);
+
+ // Get handle to info data set describing all available HIDs.
+ hdevInfoSet = SetupDiGetClassDevsA(&HidGuid, NULL, NULL, DIGCF_INTERFACEDEVICE | DIGCF_PRESENT);
+ if (hdevInfoSet == INVALID_HANDLE_VALUE)
+ return false;
+
+ for(int deviceIndex = 0;
+ SetupDiEnumDeviceInterfaces(hdevInfoSet, NULL, &HidGuid, deviceIndex, &interfaceData);
+ deviceIndex++)
+ {
+ // For each device, we extract its file path and open it to get attributes,
+ // such as vendor and product id. If anything goes wrong, we move onto next device.
+ HIDDevicePathWrapper pathWrapper;
+ if (!pathWrapper.InitPathFromInterfaceData(hdevInfoSet, &interfaceData))
+ continue;
+
+ // Look for the device to check if it is already opened.
+ Ptr<DeviceCreateDesc> existingDevice = Manager->FindDevice(pathWrapper.GetPath());
+ // if device exists and it is opened then most likely the CreateHIDFile
+ // will fail; therefore, we just set Enumerated to 'true' and continue.
+ if (existingDevice && existingDevice->pDevice)
+ {
+ existingDevice->Enumerated = true;
+ continue;
+ }
+
+ // open device in non-exclusive mode for detection...
+ HANDLE hidDev = CreateHIDFile(pathWrapper.GetPath(), false);
+ if (hidDev == INVALID_HANDLE_VALUE)
+ continue;
+
+ HIDDeviceDesc devDesc;
+ devDesc.Path = pathWrapper.GetPath();
+ if (initVendorProductVersion(hidDev, &devDesc) &&
+ enumVisitor->MatchVendorProduct(devDesc.VendorId, devDesc.ProductId) &&
+ initUsage(hidDev, &devDesc))
+ {
+ initStrings(hidDev, &devDesc);
+
+ // Construct minimal device that the visitor callback can get feature reports from.
+ Win32::HIDDevice device(this, hidDev);
+ enumVisitor->Visit(device, devDesc);
+ }
+
+ ::CloseHandle(hidDev);
+ }
+
+ SetupDiDestroyDeviceInfoList(hdevInfoSet);
+ return true;
+}
+
+bool HIDDeviceManager::GetHIDDeviceDesc(const String& path, HIDDeviceDesc* pdevDesc) const
+{
+ // open device in non-exclusive mode for detection...
+ HANDLE hidDev = CreateHIDFile(path, false);
+ if (hidDev == INVALID_HANDLE_VALUE)
+ return false;
+
+ pdevDesc->Path = path;
+ getFullDesc(hidDev, pdevDesc);
+
+ ::CloseHandle(hidDev);
+ return true;
+}
+
+OVR::HIDDevice* HIDDeviceManager::Open(const String& path)
+{
+
+ Ptr<Win32::HIDDevice> device = *new Win32::HIDDevice(this);
+
+ if (device->HIDInitialize(path))
+ {
+ device->AddRef();
+ return device;
+ }
+
+ return NULL;
+}
+
+bool HIDDeviceManager::getFullDesc(HANDLE hidDev, HIDDeviceDesc* desc) const
+{
+
+ if (!initVendorProductVersion(hidDev, desc))
+ {
+ return false;
+ }
+
+ if (!initUsage(hidDev, desc))
+ {
+ return false;
+ }
+
+ initStrings(hidDev, desc);
+
+ return true;
+}
+
+bool HIDDeviceManager::initVendorProductVersion(HANDLE hidDev, HIDDeviceDesc* desc) const
+{
+ HIDD_ATTRIBUTES attr;
+ attr.Size = sizeof(attr);
+ if (!HidD_GetAttributes(hidDev, &attr))
+ return false;
+ desc->VendorId = attr.VendorID;
+ desc->ProductId = attr.ProductID;
+ desc->VersionNumber = attr.VersionNumber;
+ return true;
+}
+
+bool HIDDeviceManager::initUsage(HANDLE hidDev, HIDDeviceDesc* desc) const
+{
+ bool result = false;
+ HIDP_CAPS caps;
+ HIDP_PREPARSED_DATA* preparsedData = 0;
+
+ if (!HidD_GetPreparsedData(hidDev, &preparsedData))
+ return false;
+
+ if (HidP_GetCaps(preparsedData, &caps) == HIDP_STATUS_SUCCESS)
+ {
+ desc->Usage = caps.Usage;
+ desc->UsagePage = caps.UsagePage;
+ result = true;
+ }
+ HidD_FreePreparsedData(preparsedData);
+ return result;
+}
+
+void HIDDeviceManager::initStrings(HANDLE hidDev, HIDDeviceDesc* desc) const
+{
+ // Documentation mentions 126 as being the max for USB.
+ wchar_t strBuffer[196];
+
+ // HidD_Get*String functions return nothing in buffer on failure,
+ // so it's ok to do this without further error checking.
+ strBuffer[0] = 0;
+ HidD_GetManufacturerString(hidDev, strBuffer, sizeof(strBuffer));
+ desc->Manufacturer = strBuffer;
+
+ strBuffer[0] = 0;
+ HidD_GetProductString(hidDev, strBuffer, sizeof(strBuffer));
+ desc->Product = strBuffer;
+
+ strBuffer[0] = 0;
+ HidD_GetSerialNumberString(hidDev, strBuffer, sizeof(strBuffer));
+ desc->SerialNumber = strBuffer;
+}
+
+//-------------------------------------------------------------------------------------
+// **** Win32::HIDDevice
+
+HIDDevice::HIDDevice(HIDDeviceManager* manager)
+ : HIDManager(manager), inMinimalMode(false), Device(0), ReadRequested(false)
+{
+ memset(&ReadOverlapped, 0, sizeof(OVERLAPPED));
+}
+
+// This is a minimal constructor used during enumeration for us to pass
+// a HIDDevice to the visit function (so that it can query feature reports).
+HIDDevice::HIDDevice(HIDDeviceManager* manager, HANDLE device)
+ : HIDManager(manager), inMinimalMode(true), Device(device), ReadRequested(true)
+{
+ memset(&ReadOverlapped, 0, sizeof(OVERLAPPED));
+}
+
+HIDDevice::~HIDDevice()
+{
+ if (!inMinimalMode)
+ {
+ HIDShutdown();
+ }
+}
+
+bool HIDDevice::HIDInitialize(const String& path)
+{
+
+ DevDesc.Path = path;
+
+ if (!openDevice())
+ {
+ LogText("OVR::Win32::HIDDevice - Failed to open HIDDevice: ", path);
+ return false;
+ }
+
+
+ HIDManager->Manager->pThread->AddTicksNotifier(this);
+ HIDManager->Manager->pThread->AddMessageNotifier(this);
+
+ LogText("OVR::Win32::HIDDevice - Opened '%s'\n"
+ " Manufacturer:'%s' Product:'%s' Serial#:'%s'\n",
+ DevDesc.Path.ToCStr(),
+ DevDesc.Manufacturer.ToCStr(), DevDesc.Product.ToCStr(),
+ DevDesc.SerialNumber.ToCStr());
+
+ return true;
+}
+
+bool HIDDevice::initInfo()
+{
+ // Device must have been successfully opened.
+ OVR_ASSERT(Device);
+
+ // Get report lengths.
+ HIDP_PREPARSED_DATA* preparsedData = 0;
+ if (!HIDManager->HidD_GetPreparsedData(Device, &preparsedData))
+ {
+ return false;
+ }
+
+ HIDP_CAPS caps;
+ if (HIDManager->HidP_GetCaps(preparsedData, &caps) != HIDP_STATUS_SUCCESS)
+ {
+ HIDManager->HidD_FreePreparsedData(preparsedData);
+ return false;
+ }
+
+ InputReportBufferLength = caps.InputReportByteLength;
+ OutputReportBufferLength = caps.OutputReportByteLength;
+ FeatureReportBufferLength= caps.FeatureReportByteLength;
+ HIDManager->HidD_FreePreparsedData(preparsedData);
+
+ if (ReadBufferSize < InputReportBufferLength)
+ {
+ OVR_ASSERT_LOG(false, ("Input report buffer length is bigger than read buffer."));
+ return false;
+ }
+
+ // Get device desc.
+ if (!HIDManager->getFullDesc(Device, &DevDesc))
+ {
+ OVR_ASSERT_LOG(false, ("Failed to get device desc while initializing device."));
+ return false;
+ }
+
+ return true;
+}
+
+bool HIDDevice::openDevice()
+{
+ memset(&ReadOverlapped, 0, sizeof(OVERLAPPED));
+
+ Device = HIDManager->CreateHIDFile(DevDesc.Path.ToCStr());
+ if (Device == INVALID_HANDLE_VALUE)
+ {
+ OVR_DEBUG_LOG(("Failed 'CreateHIDFile' while opening device, error = 0x%X.",
+ ::GetLastError()));
+ Device = 0;
+ return false;
+ }
+
+ if (!HIDManager->HidD_SetNumInputBuffers(Device, 128))
+ {
+ OVR_ASSERT_LOG(false, ("Failed 'HidD_SetNumInputBuffers' while initializing device."));
+ ::CloseHandle(Device);
+ Device = 0;
+ return false;
+ }
+
+
+ // Create a manual-reset non-signaled event.
+ ReadOverlapped.hEvent = ::CreateEvent(0, TRUE, FALSE, 0);
+
+ if (!ReadOverlapped.hEvent)
+ {
+ OVR_ASSERT_LOG(false, ("Failed to create event."));
+ ::CloseHandle(Device);
+ Device = 0;
+ return false;
+ }
+
+ if (!initInfo())
+ {
+ OVR_ASSERT_LOG(false, ("Failed to get HIDDevice info."));
+
+ ::CloseHandle(ReadOverlapped.hEvent);
+ memset(&ReadOverlapped, 0, sizeof(OVERLAPPED));
+
+ ::CloseHandle(Device);
+ Device = 0;
+ return false;
+ }
+
+ if (!initializeRead())
+ {
+ OVR_ASSERT_LOG(false, ("Failed to get intialize read for HIDDevice."));
+
+ ::CloseHandle(ReadOverlapped.hEvent);
+ memset(&ReadOverlapped, 0, sizeof(OVERLAPPED));
+
+ ::CloseHandle(Device);
+ Device = 0;
+ return false;
+ }
+
+ return true;
+}
+
+void HIDDevice::HIDShutdown()
+{
+
+ HIDManager->Manager->pThread->RemoveTicksNotifier(this);
+ HIDManager->Manager->pThread->RemoveMessageNotifier(this);
+
+ closeDevice();
+ LogText("OVR::Win32::HIDDevice - Closed '%s'\n", DevDesc.Path.ToCStr());
+}
+
+bool HIDDevice::initializeRead()
+{
+
+ if (!ReadRequested)
+ {
+ HIDManager->Manager->pThread->AddOverlappedEvent(this, ReadOverlapped.hEvent);
+ ReadRequested = true;
+ }
+
+ // Read resets the event...
+ while(::ReadFile(Device, ReadBuffer, InputReportBufferLength, 0, &ReadOverlapped))
+ {
+ processReadResult();
+ }
+
+ if (GetLastError() != ERROR_IO_PENDING)
+ {
+ // Some other error (such as unplugged).
+ closeDeviceOnIOError();
+ return false;
+ }
+
+ return true;
+}
+
+bool HIDDevice::processReadResult()
+{
+
+ OVR_ASSERT(ReadRequested);
+
+ DWORD bytesRead = 0;
+
+ if (GetOverlappedResult(Device, &ReadOverlapped, &bytesRead, FALSE))
+ {
+ // We've got data.
+ if (Handler)
+ {
+ Handler->OnInputReport(ReadBuffer, bytesRead);
+ }
+
+ // TBD: Not needed?
+ // Event should be reset by Read call...
+ ReadOverlapped.Pointer = 0;
+ ReadOverlapped.Internal = 0;
+ ReadOverlapped.InternalHigh = 0;
+ return true;
+ }
+ else
+ {
+ if (GetLastError() != ERROR_IO_PENDING)
+ {
+ closeDeviceOnIOError();
+ return false;
+ }
+ }
+
+ return false;
+}
+
+void HIDDevice::closeDevice()
+{
+ if (ReadRequested)
+ {
+ HIDManager->Manager->pThread->RemoveOverlappedEvent(this, ReadOverlapped.hEvent);
+ ReadRequested = false;
+ // Must call this to avoid Win32 assertion; CloseHandle is not enough.
+ ::CancelIo(Device);
+ }
+
+ ::CloseHandle(ReadOverlapped.hEvent);
+ memset(&ReadOverlapped, 0, sizeof(OVERLAPPED));
+
+ ::CloseHandle(Device);
+ Device = 0;
+}
+
+void HIDDevice::closeDeviceOnIOError()
+{
+ LogText("OVR::Win32::HIDDevice - Lost connection to '%s'\n", DevDesc.Path.ToCStr());
+ closeDevice();
+}
+
+bool HIDDevice::SetFeatureReport(UByte* data, UInt32 length)
+{
+ if (!ReadRequested)
+ return false;
+
+ return HIDManager->HidD_SetFeature(Device, data, (ULONG) length) != FALSE;
+}
+
+bool HIDDevice::GetFeatureReport(UByte* data, UInt32 length)
+{
+ if (!ReadRequested)
+ return false;
+
+ return HIDManager->HidD_GetFeature(Device, data, (ULONG) length) != FALSE;
+}
+
+void HIDDevice::OnOverlappedEvent(HANDLE hevent)
+{
+ OVR_UNUSED(hevent);
+ OVR_ASSERT(hevent == ReadOverlapped.hEvent);
+
+ if (processReadResult())
+ {
+ // Proceed to read again.
+ initializeRead();
+ }
+}
+
+UInt64 HIDDevice::OnTicks(UInt64 ticksMks)
+{
+ if (Handler)
+ {
+ return Handler->OnTicks(ticksMks);
+ }
+
+ return DeviceManagerThread::Notifier::OnTicks(ticksMks);
+}
+
+bool HIDDevice::OnDeviceMessage(DeviceMessageType messageType,
+ const String& devicePath,
+ bool* error)
+{
+
+ // Is this the correct device?
+ if (DevDesc.Path.CompareNoCase(devicePath) != 0)
+ {
+ return false;
+ }
+
+ if (messageType == DeviceMessage_DeviceAdded && !Device)
+ {
+ // A closed device has been re-added. Try to reopen.
+ if (!openDevice())
+ {
+ LogError("OVR::Win32::HIDDevice - Failed to reopen a device '%s' that was re-added.\n", devicePath.ToCStr());
+ *error = true;
+ return true;
+ }
+
+ LogText("OVR::Win32::HIDDevice - Reopened device '%s'\n", devicePath.ToCStr());
+ }
+
+ HIDHandler::HIDDeviceMessageType handlerMessageType = HIDHandler::HIDDeviceMessage_DeviceAdded;
+ if (messageType == DeviceMessage_DeviceAdded)
+ {
+ }
+ else if (messageType == DeviceMessage_DeviceRemoved)
+ {
+ handlerMessageType = HIDHandler::HIDDeviceMessage_DeviceRemoved;
+ }
+ else
+ {
+ OVR_ASSERT(0);
+ }
+
+ if (Handler)
+ {
+ Handler->OnDeviceMessage(handlerMessageType);
+ }
+
+ *error = false;
+ return true;
+}
+
+HIDDeviceManager* HIDDeviceManager::CreateInternal(Win32::DeviceManager* devManager)
+{
+
+ if (!System::IsInitialized())
+ {
+ // Use custom message, since Log is not yet installed.
+ OVR_DEBUG_STATEMENT(Log::GetDefaultLog()->
+ LogMessage(Log_Debug, "HIDDeviceManager::Create failed - OVR::System not initialized"); );
+ return 0;
+ }
+
+ Ptr<Win32::HIDDeviceManager> manager = *new Win32::HIDDeviceManager(devManager);
+
+ if (manager)
+ {
+ if (manager->Initialize())
+ {
+ manager->AddRef();
+ }
+ else
+ {
+ manager.Clear();
+ }
+ }
+
+ return manager.GetPtr();
+}
+
+} // namespace Win32
+
+//-------------------------------------------------------------------------------------
+// ***** Creation
+
+// Creates a new HIDDeviceManager and initializes OVR.
+HIDDeviceManager* HIDDeviceManager::Create()
+{
+ OVR_ASSERT_LOG(false, ("Standalone mode not implemented yet."));
+
+ if (!System::IsInitialized())
+ {
+ // Use custom message, since Log is not yet installed.
+ OVR_DEBUG_STATEMENT(Log::GetDefaultLog()->
+ LogMessage(Log_Debug, "HIDDeviceManager::Create failed - OVR::System not initialized"); );
+ return 0;
+ }
+
+ Ptr<Win32::HIDDeviceManager> manager = *new Win32::HIDDeviceManager(NULL);
+
+ if (manager)
+ {
+ if (manager->Initialize())
+ {
+ manager->AddRef();
+ }
+ else
+ {
+ manager.Clear();
+ }
+ }
+
+ return manager.GetPtr();
+}
+
+} // namespace OVR
diff --git a/LibOVR/Src/OVR_Win32_HIDDevice.h b/LibOVR/Src/OVR_Win32_HIDDevice.h
new file mode 100644
index 0000000..efe308d
--- /dev/null
+++ b/LibOVR/Src/OVR_Win32_HIDDevice.h
@@ -0,0 +1,194 @@
+/************************************************************************************
+
+Filename : OVR_Win32_HIDDevice.h
+Content : Win32 HID device implementation.
+Created : February 22, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_Win32_HIDDevice_h
+#define OVR_Win32_HIDDevice_h
+
+#include "OVR_HIDDevice.h"
+#include "OVR_Win32_DeviceManager.h"
+
+#include <windows.h>
+#include <setupapi.h>
+
+//-------------------------------------------------------------------------------------
+// Define needed "hidsdi.h" functionality to avoid requiring DDK installation.
+// #include "hidsdi.h"
+
+#ifndef _HIDSDI_H
+#define _HIDSDI_H
+#include <pshpack4.h>
+
+#define HIDP_STATUS_SUCCESS (0x11 << 16)
+struct HIDP_PREPARSED_DATA;
+
+struct HIDD_ATTRIBUTES
+{
+ ULONG Size; // = sizeof (struct _HIDD_ATTRIBUTES)
+ USHORT VendorID;
+ USHORT ProductID;
+ USHORT VersionNumber;
+};
+
+struct HIDP_CAPS
+{
+ USHORT Usage;
+ USHORT UsagePage;
+ USHORT InputReportByteLength;
+ USHORT OutputReportByteLength;
+ USHORT FeatureReportByteLength;
+ USHORT Reserved[17];
+
+ USHORT NumberLinkCollectionNodes;
+ USHORT NumberInputButtonCaps;
+ USHORT NumberInputValueCaps;
+ USHORT NumberInputDataIndices;
+ USHORT NumberOutputButtonCaps;
+ USHORT NumberOutputValueCaps;
+ USHORT NumberOutputDataIndices;
+ USHORT NumberFeatureButtonCaps;
+ USHORT NumberFeatureValueCaps;
+ USHORT NumberFeatureDataIndices;
+};
+
+#include <poppack.h>
+#endif
+
+
+namespace OVR { namespace Win32 {
+
+class HIDDeviceManager;
+class DeviceManager;
+
+//-------------------------------------------------------------------------------------
+// ***** Win32 HIDDevice
+
+class HIDDevice : public OVR::HIDDevice, public DeviceManagerThread::Notifier
+{
+public:
+
+ HIDDevice(HIDDeviceManager* manager);
+
+ // This is a minimal constructor used during enumeration for us to pass
+ // a HIDDevice to the visit function (so that it can query feature reports).
+ HIDDevice(HIDDeviceManager* manager, HANDLE device);
+
+ ~HIDDevice();
+
+ bool HIDInitialize(const String& path);
+ void HIDShutdown();
+
+ // OVR::HIDDevice
+ bool SetFeatureReport(UByte* data, UInt32 length);
+ bool GetFeatureReport(UByte* data, UInt32 length);
+
+
+ // DeviceManagerThread::Notifier
+ void OnOverlappedEvent(HANDLE hevent);
+ UInt64 OnTicks(UInt64 ticksMks);
+ bool OnDeviceMessage(DeviceMessageType messageType, const String& devicePath, bool* error);
+
+private:
+ bool openDevice();
+ bool initInfo();
+ bool initializeRead();
+ bool processReadResult();
+ void closeDevice();
+ void closeDeviceOnIOError();
+
+ bool inMinimalMode;
+ HIDDeviceManager* HIDManager;
+ HANDLE Device;
+ HIDDeviceDesc DevDesc;
+
+ OVERLAPPED ReadOverlapped;
+ bool ReadRequested;
+
+ enum { ReadBufferSize = 96 };
+ UByte ReadBuffer[ReadBufferSize];
+
+ UInt16 InputReportBufferLength;
+ UInt16 OutputReportBufferLength;
+ UInt16 FeatureReportBufferLength;
+};
+
+//-------------------------------------------------------------------------------------
+// ***** Win32 HIDDeviceManager
+
+class HIDDeviceManager : public OVR::HIDDeviceManager
+{
+ friend class HIDDevice;
+public:
+
+ HIDDeviceManager(DeviceManager* manager);
+ virtual ~HIDDeviceManager();
+
+ virtual bool Initialize();
+ virtual void Shutdown();
+
+ virtual bool Enumerate(HIDEnumerateVisitor* enumVisitor);
+ virtual OVR::HIDDevice* Open(const String& path);
+
+ // Fills HIDDeviceDesc by using the path.
+ // Returns 'true' if successful, 'false' otherwise.
+ bool GetHIDDeviceDesc(const String& path, HIDDeviceDesc* pdevDesc) const;
+
+ GUID GetHIDGuid() { return HidGuid; }
+
+ static HIDDeviceManager* CreateInternal(DeviceManager* manager);
+
+private:
+
+ DeviceManager* Manager; // Back pointer can just be a raw pointer.
+
+ HMODULE hHidLib;
+ GUID HidGuid;
+
+ // Macros to declare and resolve needed functions from library.
+#define OVR_DECLARE_HIDFUNC(func, rettype, args) \
+typedef rettype (__stdcall *PFn_##func) args; \
+PFn_##func func;
+#define OVR_RESOLVE_HIDFUNC(func) \
+func = (PFn_##func)::GetProcAddress(hHidLib, #func)
+
+ OVR_DECLARE_HIDFUNC(HidD_GetHidGuid, void, (GUID *hidGuid));
+ OVR_DECLARE_HIDFUNC(HidD_SetNumInputBuffers, BOOLEAN, (HANDLE hidDev, ULONG numberBuffers));
+ OVR_DECLARE_HIDFUNC(HidD_GetFeature, BOOLEAN, (HANDLE hidDev, PVOID buffer, ULONG bufferLength));
+ OVR_DECLARE_HIDFUNC(HidD_SetFeature, BOOLEAN, (HANDLE hidDev, PVOID buffer, ULONG bufferLength));
+ OVR_DECLARE_HIDFUNC(HidD_GetAttributes, BOOLEAN, (HANDLE hidDev, HIDD_ATTRIBUTES *attributes));
+ OVR_DECLARE_HIDFUNC(HidD_GetManufacturerString, BOOLEAN, (HANDLE hidDev, PVOID buffer, ULONG bufferLength));
+ OVR_DECLARE_HIDFUNC(HidD_GetProductString, BOOLEAN, (HANDLE hidDev, PVOID buffer, ULONG bufferLength));
+ OVR_DECLARE_HIDFUNC(HidD_GetSerialNumberString, BOOLEAN, (HANDLE hidDev, PVOID buffer, ULONG bufferLength));
+ OVR_DECLARE_HIDFUNC(HidD_GetPreparsedData, BOOLEAN, (HANDLE hidDev, HIDP_PREPARSED_DATA **preparsedData));
+ OVR_DECLARE_HIDFUNC(HidD_FreePreparsedData, BOOLEAN, (HIDP_PREPARSED_DATA *preparsedData));
+ OVR_DECLARE_HIDFUNC(HidP_GetCaps, NTSTATUS,(HIDP_PREPARSED_DATA *preparsedData, HIDP_CAPS* caps));
+
+ HANDLE CreateHIDFile(const char* path, bool exclusiveAccess = true) const
+ {
+ return ::CreateFileA(path, GENERIC_WRITE|GENERIC_READ,
+ (!exclusiveAccess) ? (FILE_SHARE_READ|FILE_SHARE_WRITE) : 0x0,
+ NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
+ }
+
+ // Helper functions to fill in HIDDeviceDesc from open device handle.
+ bool initVendorProductVersion(HANDLE hidDev, HIDDeviceDesc* desc) const;
+ bool initUsage(HANDLE hidDev, HIDDeviceDesc* desc) const;
+ void initStrings(HANDLE hidDev, HIDDeviceDesc* desc) const;
+
+ bool getFullDesc(HANDLE hidDev, HIDDeviceDesc* desc) const;
+};
+
+}} // namespace OVR::Win32
+
+#endif // OVR_Win32_HIDDevice_h
diff --git a/LibOVR/Src/OVR_Win32_HMDDevice.cpp b/LibOVR/Src/OVR_Win32_HMDDevice.cpp
new file mode 100644
index 0000000..095d3a8
--- /dev/null
+++ b/LibOVR/Src/OVR_Win32_HMDDevice.cpp
@@ -0,0 +1,418 @@
+/************************************************************************************
+
+Filename : OVR_Win32_HMDDevice.cpp
+Content : Win32 Interface to HMD - detects HMD display
+Created : September 21, 2012
+Authors : Michael Antonov
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#include "OVR_Win32_HMDDevice.h"
+
+#include "OVR_Win32_DeviceManager.h"
+
+#include <tchar.h>
+
+namespace OVR { namespace Win32 {
+
+//-------------------------------------------------------------------------------------
+
+HMDDeviceCreateDesc::HMDDeviceCreateDesc(DeviceFactory* factory,
+ const String& deviceId, const String& displayDeviceName)
+ : DeviceCreateDesc(factory, Device_HMD),
+ DeviceId(deviceId), DisplayDeviceName(displayDeviceName),
+ DesktopX(0), DesktopY(0), Contents(0),
+ HResolution(0), VResolution(0), HScreenSize(0), VScreenSize(0)
+{
+}
+HMDDeviceCreateDesc::HMDDeviceCreateDesc(const HMDDeviceCreateDesc& other)
+ : DeviceCreateDesc(other.pFactory, Device_HMD),
+ DeviceId(other.DeviceId), DisplayDeviceName(other.DisplayDeviceName),
+ DesktopX(other.DesktopX), DesktopY(other.DesktopY), Contents(other.Contents),
+ HResolution(other.HResolution), VResolution(other.VResolution),
+ HScreenSize(other.HScreenSize), VScreenSize(other.VScreenSize)
+{
+}
+
+HMDDeviceCreateDesc::MatchResult HMDDeviceCreateDesc::MatchDevice(const DeviceCreateDesc& other,
+ DeviceCreateDesc** pcandidate) const
+{
+ if ((other.Type != Device_HMD) || (other.pFactory != pFactory))
+ return Match_None;
+
+ // There are several reasons we can come in here:
+ // a) Matching this HMD Monitor created desc to OTHER HMD Monitor desc
+ // - Require exact device DeviceId/DeviceName match
+ // b) Matching SensorDisplayInfo created desc to OTHER HMD Monitor desc
+ // - This DeviceId is empty; becomes candidate
+ // c) Matching this HMD Monitor created desc to SensorDisplayInfo desc
+ // - This other.DeviceId is empty; becomes candidate
+
+ const HMDDeviceCreateDesc& s2 = (const HMDDeviceCreateDesc&) other;
+
+ if ((DeviceId == s2.DeviceId) &&
+ (DisplayDeviceName == s2.DisplayDeviceName))
+ {
+ // Non-null DeviceId may match while size is different if screen size was overwritten
+ // by SensorDisplayInfo in prior iteration.
+ if (!DeviceId.IsEmpty() ||
+ ((HScreenSize == s2.HScreenSize) &&
+ (VScreenSize == s2.VScreenSize)) )
+ {
+ *pcandidate = 0;
+ return Match_Found;
+ }
+ }
+
+
+ // DisplayInfo takes precedence, although we try to match it first.
+ if ((HResolution == s2.HResolution) &&
+ (VResolution == s2.VResolution) &&
+ (HScreenSize == s2.HScreenSize) &&
+ (VScreenSize == s2.VScreenSize))
+ {
+ if (DeviceId.IsEmpty() && !s2.DeviceId.IsEmpty())
+ {
+ *pcandidate = const_cast<DeviceCreateDesc*>((const DeviceCreateDesc*)this);
+ return Match_Candidate;
+ }
+
+ *pcandidate = 0;
+ return Match_Found;
+ }
+
+ // SensorDisplayInfo may override resolution settings, so store as candidate.
+ if (s2.DeviceId.IsEmpty())
+ {
+ *pcandidate = const_cast<DeviceCreateDesc*>((const DeviceCreateDesc*)this);
+ return Match_Candidate;
+ }
+ // OTHER HMD Monitor desc may initialize DeviceName/Id
+ else if (DeviceId.IsEmpty())
+ {
+ *pcandidate = const_cast<DeviceCreateDesc*>((const DeviceCreateDesc*)this);
+ return Match_Candidate;
+ }
+
+ return Match_None;
+}
+
+
+bool HMDDeviceCreateDesc::UpdateMatchedCandidate(const DeviceCreateDesc& other,
+ bool* newDeviceFlag)
+{
+ // This candidate was the the "best fit" to apply sensor DisplayInfo to.
+ OVR_ASSERT(other.Type == Device_HMD);
+
+ const HMDDeviceCreateDesc& s2 = (const HMDDeviceCreateDesc&) other;
+
+ // Force screen size on resolution from SensorDisplayInfo.
+ // We do this because USB detection is more reliable as compared to HDMI EDID,
+ // which may be corrupted by splitter reporting wrong monitor
+ if (s2.DeviceId.IsEmpty())
+ {
+ HScreenSize = s2.HScreenSize;
+ VScreenSize = s2.VScreenSize;
+ Contents |= Contents_Screen;
+
+ if (s2.Contents & HMDDeviceCreateDesc::Contents_Distortion)
+ {
+ memcpy(DistortionK, s2.DistortionK, sizeof(float)*4);
+ Contents |= Contents_Distortion;
+ }
+ DeviceId = s2.DeviceId;
+ DisplayDeviceName = s2.DisplayDeviceName;
+ if (newDeviceFlag) *newDeviceFlag = true;
+ }
+ else if (DeviceId.IsEmpty())
+ {
+ DeviceId = s2.DeviceId;
+ DisplayDeviceName = s2.DisplayDeviceName;
+ if (newDeviceFlag) *newDeviceFlag = true;
+ }
+ else
+ {
+ if (newDeviceFlag) *newDeviceFlag = false;
+ }
+
+ return true;
+}
+
+bool HMDDeviceCreateDesc::MatchDevice(const String& path)
+{
+ return DeviceId.CompareNoCase(path) == 0;
+}
+
+//-------------------------------------------------------------------------------------
+
+
+const wchar_t* FormatDisplayStateFlags(wchar_t* buff, int length, DWORD flags)
+{
+ buff[0] = 0;
+ if (flags & DISPLAY_DEVICE_ACTIVE)
+ wcscat_s(buff, length, L"Active ");
+ if (flags & DISPLAY_DEVICE_MIRRORING_DRIVER)
+ wcscat_s(buff, length, L"Mirroring_Driver ");
+ if (flags & DISPLAY_DEVICE_MODESPRUNED)
+ wcscat_s(buff, length, L"ModesPruned ");
+ if (flags & DISPLAY_DEVICE_PRIMARY_DEVICE)
+ wcscat_s(buff, length, L"Primary ");
+ if (flags & DISPLAY_DEVICE_REMOVABLE)
+ wcscat_s(buff, length, L"Removable ");
+ if (flags & DISPLAY_DEVICE_VGA_COMPATIBLE)
+ wcscat_s(buff, length, L"VGA_Compatible ");
+ return buff;
+}
+
+
+//-------------------------------------------------------------------------------------
+// Callback for monitor enumeration to store all the monitor handles
+
+// Used to capture all the active monitor handles
+struct MonitorSet
+{
+ enum { MaxMonitors = 8 };
+ HMONITOR Monitors[MaxMonitors];
+ int MonitorCount;
+};
+
+BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor, HDC, LPRECT, LPARAM dwData)
+{
+ MonitorSet* monitorSet = (MonitorSet*)dwData;
+ if (monitorSet->MonitorCount > MonitorSet::MaxMonitors)
+ return FALSE;
+
+ monitorSet->Monitors[monitorSet->MonitorCount] = hMonitor;
+ monitorSet->MonitorCount++;
+ return TRUE;
+};
+
+//-------------------------------------------------------------------------------------
+// ***** HMDDeviceFactory
+
+HMDDeviceFactory HMDDeviceFactory::Instance;
+
+void HMDDeviceFactory::EnumerateDevices(EnumerateVisitor& visitor)
+{
+ MonitorSet monitors;
+ monitors.MonitorCount = 0;
+ // Get all the monitor handles
+ EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, (LPARAM)&monitors);
+
+ bool foundHMD = false;
+
+ // DeviceManager* manager = getManager();
+ DISPLAY_DEVICE dd, ddm;
+ UINT i, j;
+
+ for (i = 0;
+ (ZeroMemory(&dd, sizeof(dd)), dd.cb = sizeof(dd),
+ EnumDisplayDevices(0, i, &dd, 0)) != 0; i++)
+ {
+
+ /*
+ wchar_t buff[500], flagsBuff[200];
+
+ swprintf_s(buff, 500, L"\nDEV: \"%s\" \"%s\" 0x%08x=%s\n \"%s\" \"%s\"\n",
+ dd.DeviceName, dd.DeviceString,
+ dd.StateFlags, FormatDisplayStateFlags(flagsBuff, 200, dd.StateFlags),
+ dd.DeviceID, dd.DeviceKey);
+ ::OutputDebugString(buff);
+ */
+
+ for (j = 0;
+ (ZeroMemory(&ddm, sizeof(ddm)), ddm.cb = sizeof(ddm),
+ EnumDisplayDevices(dd.DeviceName, j, &ddm, 0)) != 0; j++)
+ {
+ /*
+ wchar_t mbuff[500];
+ swprintf_s(mbuff, 500, L"MON: \"%s\" \"%s\" 0x%08x=%s\n \"%s\" \"%s\"\n",
+ ddm.DeviceName, ddm.DeviceString,
+ ddm.StateFlags, FormatDisplayStateFlags(flagsBuff, 200, ddm.StateFlags),
+ ddm.DeviceID, ddm.DeviceKey);
+ ::OutputDebugString(mbuff);
+ */
+
+ // Our monitor hardware has string "RTD2205" in it
+ // Nate's device "CVT0003"
+ if (wcsstr(ddm.DeviceID, L"RTD2205") ||
+ wcsstr(ddm.DeviceID, L"CVT0003") ||
+ wcsstr(ddm.DeviceID, L"MST0030") ||
+ wcsstr(ddm.DeviceID, L"OVR00") ) // Part of Oculus EDID.
+ {
+ String deviceId(ddm.DeviceID);
+ String displayDeviceName(ddm.DeviceName);
+
+ // The default monitor coordinates
+ int mx = 0;
+ int my = 0;
+ int mwidth = 1280;
+ int mheight = 800;
+
+ // Find the matching MONITORINFOEX for this device so we can get the
+ // screen coordinates
+ MONITORINFOEX info;
+ for (int m=0; m < monitors.MonitorCount; m++)
+ {
+ info.cbSize = sizeof(MONITORINFOEX);
+ GetMonitorInfo(monitors.Monitors[m], &info);
+ if (_tcsstr(ddm.DeviceName, info.szDevice) == ddm.DeviceName)
+ { // If the device name starts with the monitor name
+ // then we found the matching DISPLAY_DEVICE and MONITORINFO
+ // so we can gather the monitor coordinates
+ mx = info.rcMonitor.left;
+ my = info.rcMonitor.top;
+ //mwidth = info.rcMonitor.right - info.rcMonitor.left;
+ //mheight = info.rcMonitor.bottom - info.rcMonitor.top;
+ break;
+ }
+ }
+
+ HMDDeviceCreateDesc hmdCreateDesc(this, deviceId, displayDeviceName);
+
+ if (hmdCreateDesc.Is7Inch())
+ {
+ // Physical dimension of SLA screen.
+ hmdCreateDesc.SetScreenParameters(mx, my, mwidth, mheight, 0.14976f, 0.0936f);
+ }
+ else
+ {
+ hmdCreateDesc.SetScreenParameters(mx, my, mwidth, mheight, 0.12096f, 0.0756f);
+ }
+
+ OVR_DEBUG_LOG_TEXT(("DeviceManager - HMD Found %s - %s\n",
+ deviceId.ToCStr(), displayDeviceName.ToCStr()));
+
+ // Notify caller about detected device. This will call EnumerateAddDevice
+ // if the this is the first time device was detected.
+ visitor.Visit(hmdCreateDesc);
+ foundHMD = true;
+ break;
+ }
+ }
+ }
+
+ // Real HMD device is not found; however, we still may have a 'fake' HMD
+ // device created via SensorDeviceImpl::EnumerateHMDFromSensorDisplayInfo.
+ // Need to find it and set 'Enumerated' to true to avoid Removal notification.
+ if (!foundHMD)
+ {
+ Ptr<DeviceCreateDesc> hmdDevDesc = getManager()->FindDevice("", Device_HMD);
+ if (hmdDevDesc)
+ hmdDevDesc->Enumerated = true;
+ }
+}
+
+DeviceBase* HMDDeviceCreateDesc::NewDeviceInstance()
+{
+ return new HMDDevice(this);
+}
+
+bool HMDDeviceCreateDesc::Is7Inch() const
+{
+ return (strstr(DeviceId.ToCStr(), "OVR0001") != 0) || (Contents & Contents_7Inch);
+}
+
+bool HMDDeviceCreateDesc::GetDeviceInfo(DeviceInfo* info) const
+{
+ if ((info->InfoClassType != Device_HMD) &&
+ (info->InfoClassType != Device_None))
+ return false;
+
+ bool is7Inch = Is7Inch();
+
+ OVR_strcpy(info->ProductName, DeviceInfo::MaxNameLength,
+ is7Inch ? "Oculus Rift DK1" : "Oculus Rift DK1-Prototype");
+ OVR_strcpy(info->Manufacturer, DeviceInfo::MaxNameLength, "Oculus VR");
+ info->Type = Device_HMD;
+ info->Version = 0;
+
+ // Display detection.
+ if (info->InfoClassType == Device_HMD)
+ {
+ HMDInfo* hmdInfo = static_cast<HMDInfo*>(info);
+
+ hmdInfo->DesktopX = DesktopX;
+ hmdInfo->DesktopY = DesktopY;
+ hmdInfo->HResolution = HResolution;
+ hmdInfo->VResolution = VResolution;
+ hmdInfo->HScreenSize = HScreenSize;
+ hmdInfo->VScreenSize = VScreenSize;
+ hmdInfo->VScreenCenter = VScreenSize * 0.5f;
+ hmdInfo->InterpupillaryDistance = 0.064f; // Default IPD; should be configurable.
+ hmdInfo->LensSeparationDistance = 0.0635f;
+
+ if (Contents & Contents_Distortion)
+ {
+ memcpy(hmdInfo->DistortionK, DistortionK, sizeof(float)*4);
+ }
+ else
+ {
+ if (is7Inch)
+ {
+ // 7" screen.
+ hmdInfo->DistortionK[0] = 1.0f;
+ hmdInfo->DistortionK[1] = 0.22f;
+ hmdInfo->DistortionK[2] = 0.24f;
+ hmdInfo->EyeToScreenDistance = 0.041f;
+
+ hmdInfo->ChromaAbCorrection[0] = 0.996f;
+ hmdInfo->ChromaAbCorrection[1] = -0.004f;
+ hmdInfo->ChromaAbCorrection[2] = 1.014f;
+ hmdInfo->ChromaAbCorrection[3] = 0.0f;
+ }
+ else
+ {
+ hmdInfo->DistortionK[0] = 1.0f;
+ hmdInfo->DistortionK[1] = 0.18f;
+ hmdInfo->DistortionK[2] = 0.115f;
+ hmdInfo->EyeToScreenDistance = 0.0387f;
+ }
+ }
+
+ OVR_strcpy(hmdInfo->DisplayDeviceName, sizeof(hmdInfo->DisplayDeviceName),
+ DisplayDeviceName.ToCStr());
+ }
+
+ return true;
+}
+
+//-------------------------------------------------------------------------------------
+// ***** HMDDevice
+
+HMDDevice::HMDDevice(HMDDeviceCreateDesc* createDesc)
+ : OVR::DeviceImpl<OVR::HMDDevice>(createDesc, 0)
+{
+}
+HMDDevice::~HMDDevice()
+{
+}
+
+bool HMDDevice::Initialize(DeviceBase* parent)
+{
+ pParent = parent;
+ return true;
+}
+void HMDDevice::Shutdown()
+{
+ pParent.Clear();
+}
+
+OVR::SensorDevice* HMDDevice::GetSensor()
+{
+ // Just return first sensor found since we have no way to match it yet.
+ OVR::SensorDevice* sensor = GetManager()->EnumerateDevices<SensorDevice>().CreateDevice();
+ if (sensor)
+ sensor->SetCoordinateFrame(SensorDevice::Coord_HMD);
+ return sensor;
+}
+
+}} // namespace OVR::Win32
+
+
diff --git a/LibOVR/Src/OVR_Win32_HMDDevice.h b/LibOVR/Src/OVR_Win32_HMDDevice.h
new file mode 100644
index 0000000..f653de9
--- /dev/null
+++ b/LibOVR/Src/OVR_Win32_HMDDevice.h
@@ -0,0 +1,133 @@
+/************************************************************************************
+
+Filename : OVR_Win32_HMDDevice.h
+Content : Win32 HMDDevice implementation
+Created : September 21, 2012
+Authors : Michael Antonov
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_Win32_HMDDevice_h
+#define OVR_Win32_HMDDevice_h
+
+#include "OVR_Win32_DeviceManager.h"
+
+namespace OVR { namespace Win32 {
+
+class HMDDevice;
+
+
+//-------------------------------------------------------------------------------------
+
+// HMDDeviceFactory enumerates attached Oculus HMD devices.
+//
+// This is currently done by matching monitor device strings.
+
+class HMDDeviceFactory : public DeviceFactory
+{
+public:
+ static HMDDeviceFactory Instance;
+
+ // Enumerates devices, creating and destroying relevant objects in manager.
+ virtual void EnumerateDevices(EnumerateVisitor& visitor);
+
+protected:
+ DeviceManager* getManager() const { return (DeviceManager*) pManager; }
+};
+
+
+class HMDDeviceCreateDesc : public DeviceCreateDesc
+{
+ friend class HMDDevice;
+
+protected:
+ enum
+ {
+ Contents_Screen = 1,
+ Contents_Distortion = 2,
+ Contents_7Inch = 4,
+ };
+ String DeviceId;
+ String DisplayDeviceName;
+ int DesktopX, DesktopY;
+ unsigned Contents;
+ unsigned HResolution, VResolution;
+ float HScreenSize, VScreenSize;
+ float DistortionK[4];
+
+public:
+ HMDDeviceCreateDesc(DeviceFactory* factory,
+ const String& deviceId, const String& displayDeviceName);
+ HMDDeviceCreateDesc(const HMDDeviceCreateDesc& other);
+
+ virtual DeviceCreateDesc* Clone() const
+ {
+ return new HMDDeviceCreateDesc(*this);
+ }
+
+ virtual DeviceBase* NewDeviceInstance();
+
+ virtual MatchResult MatchDevice(const DeviceCreateDesc& other,
+ DeviceCreateDesc**) const;
+
+ // Matches device by path.
+ virtual bool MatchDevice(const String& path);
+
+ virtual bool UpdateMatchedCandidate(const DeviceCreateDesc&, bool* newDeviceFlag = NULL);
+
+ virtual bool GetDeviceInfo(DeviceInfo* info) const;
+
+ void SetScreenParameters(int x, int y, unsigned hres, unsigned vres, float hsize, float vsize)
+ {
+ DesktopX = x;
+ DesktopY = y;
+ HResolution = hres;
+ VResolution = vres;
+ HScreenSize = hsize;
+ VScreenSize = vsize;
+ Contents |= Contents_Screen;
+ }
+ void SetDistortion(const float* dks)
+ {
+ for (int i = 0; i < 4; i++)
+ DistortionK[i] = dks[i];
+ Contents |= Contents_Distortion;
+ }
+
+ void Set7Inch() { Contents |= Contents_7Inch; }
+
+ bool Is7Inch() const;
+};
+
+
+//-------------------------------------------------------------------------------------
+
+// HMDDevice represents an Oculus HMD device unit. An instance of this class
+// is typically created from the DeviceManager.
+// After HMD device is created, we its sensor data can be obtained by
+// first creating a Sensor object and then wrappig it in SensorFusion.
+
+class HMDDevice : public DeviceImpl<OVR::HMDDevice>
+{
+public:
+ HMDDevice(HMDDeviceCreateDesc* createDesc);
+ ~HMDDevice();
+
+ virtual bool Initialize(DeviceBase* parent);
+ virtual void Shutdown();
+
+ // Query associated sensor.
+ virtual OVR::SensorDevice* GetSensor();
+};
+
+
+}} // namespace OVR::Win32
+
+#endif // OVR_Win32_HMDDevice_h
+
diff --git a/LibOVR/Src/OVR_Win32_SensorDevice.cpp b/LibOVR/Src/OVR_Win32_SensorDevice.cpp
new file mode 100644
index 0000000..ce02b9e
--- /dev/null
+++ b/LibOVR/Src/OVR_Win32_SensorDevice.cpp
@@ -0,0 +1,47 @@
+/************************************************************************************
+
+Filename : OVR_Win32_SensorDevice.cpp
+Content : Win32 SensorDevice implementation
+Created : March 12, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#include "OVR_Win32_SensorDevice.h"
+
+#include "OVR_Win32_HMDDevice.h"
+#include "OVR_SensorImpl.h"
+#include "OVR_DeviceImpl.h"
+
+namespace OVR { namespace Win32 {
+
+} // namespace Win32
+
+//-------------------------------------------------------------------------------------
+void SensorDeviceImpl::EnumerateHMDFromSensorDisplayInfo
+ (const SensorDisplayInfoImpl& displayInfo,
+ DeviceFactory::EnumerateVisitor& visitor)
+{
+
+ Win32::HMDDeviceCreateDesc hmdCreateDesc(&Win32::HMDDeviceFactory::Instance, String(), String());
+ hmdCreateDesc.SetScreenParameters( 0, 0,
+ displayInfo.HResolution, displayInfo.VResolution,
+ displayInfo.HScreenSize, displayInfo.VScreenSize);
+
+ if ((displayInfo.DistortionType & SensorDisplayInfoImpl::Mask_BaseFmt) == SensorDisplayInfoImpl::Base_Distortion)
+ hmdCreateDesc.SetDistortion(displayInfo.DistortionK);
+ if (displayInfo.HScreenSize > 0.14f)
+ hmdCreateDesc.Set7Inch();
+
+ visitor.Visit(hmdCreateDesc);
+}
+
+} // namespace OVR
+
+
diff --git a/LibOVR/Src/OVR_Win32_SensorDevice.h b/LibOVR/Src/OVR_Win32_SensorDevice.h
new file mode 100644
index 0000000..730620e
--- /dev/null
+++ b/LibOVR/Src/OVR_Win32_SensorDevice.h
@@ -0,0 +1,24 @@
+/************************************************************************************
+
+Filename : OVR_Win32_SensorDevice.h
+Content : Win32 SensorDevice implementation
+Created : March 12, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_Win32_SensorDevice_h
+#define OVR_Win32_SensorDevice_h
+
+namespace OVR { namespace Win32 {
+
+}} // namespace OVR::Win32
+
+#endif // OVR_Win32_SensorDevice_h
+
diff --git a/LibOVR/Src/Util/Util_LatencyTest.cpp b/LibOVR/Src/Util/Util_LatencyTest.cpp
new file mode 100644
index 0000000..1a1c303
--- /dev/null
+++ b/LibOVR/Src/Util/Util_LatencyTest.cpp
@@ -0,0 +1,560 @@
+/************************************************************************************
+
+Filename : Util_LatencyTest.cpp
+Content : Wraps the lower level LatencyTester interface and adds functionality.
+Created : February 14, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#include "Util_LatencyTest.h"
+
+#include "../Kernel/OVR_Log.h"
+#include "../Kernel/OVR_Timer.h"
+
+namespace OVR { namespace Util {
+
+static const UInt32 TIME_TO_WAIT_FOR_SETTLE_PRE_CALIBRATION = 16*10;
+static const UInt32 TIME_TO_WAIT_FOR_SETTLE_POST_CALIBRATION = 16*10;
+static const UInt32 TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT = 16*5;
+static const UInt32 TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS = 16*5;
+static const UInt32 DEFAULT_NUMBER_OF_SAMPLES = 10; // For both color 1->2 and color 2->1 transitions.
+static const UInt32 INITIAL_SAMPLES_TO_IGNORE = 4;
+static const UInt32 TIMEOUT_WAITING_FOR_TEST_STARTED = 1000;
+static const UInt32 TIMEOUT_WAITING_FOR_COLOR_DETECTED = 4000;
+static const Color CALIBRATE_BLACK(0, 0, 0);
+static const Color CALIBRATE_WHITE(255, 255, 255);
+static const Color COLOR1(0, 0, 0);
+static const Color COLOR2(255, 255, 255);
+static const Color SENSOR_DETECT_THRESHOLD(128, 255, 255);
+static const float BIG_FLOAT = 1000000.0f;
+static const float SMALL_FLOAT = -1000000.0f;
+
+//-------------------------------------------------------------------------------------
+// ***** LatencyTest
+
+LatencyTest::LatencyTest(LatencyTestDevice* device)
+ : Handler(getThis())
+{
+ if (device != NULL)
+ {
+ SetDevice(device);
+ }
+
+ reset();
+
+ srand(Timer::GetTicksMs());
+}
+
+LatencyTest::~LatencyTest()
+{
+ clearMeasurementResults();
+}
+
+bool LatencyTest::SetDevice(LatencyTestDevice* device)
+{
+
+ if (device != Device)
+ {
+ if (device != NULL)
+ {
+ if (device->GetMessageHandler() != NULL)
+ {
+ OVR_DEBUG_LOG(
+ ("LatencyTest::AttachToDevice failed - device %p already has handler", device));
+ return false;
+ }
+ }
+
+ if (Device != NULL)
+ {
+ Device->SetMessageHandler(0);
+ }
+ Device = device;
+
+ if (Device != NULL)
+ {
+ Device->SetMessageHandler(&Handler);
+
+ // Set trigger threshold.
+ LatencyTestConfiguration configuration(SENSOR_DETECT_THRESHOLD, false); // No samples streaming.
+ Device->SetConfiguration(configuration, true);
+ }
+ }
+
+ return true;
+}
+
+UInt32 LatencyTest::getRandomComponent(UInt32 range)
+{
+ UInt32 val = rand() % range;
+ return val;
+}
+
+void LatencyTest::BeginTest()
+{
+ if (State == State_WaitingForButton)
+ {
+ // Set color to black and wait a while.
+ RenderColor = CALIBRATE_BLACK;
+
+ State = State_WaitingForSettlePreCalibrationColorBlack;
+ OVR_DEBUG_LOG(("State_WaitingForButton -> State_WaitingForSettlePreCalibrationColorBlack."));
+
+ setTimer(TIME_TO_WAIT_FOR_SETTLE_PRE_CALIBRATION);
+ }
+}
+
+void LatencyTest::handleMessage(const Message& msg, LatencyTestMessageType latencyTestMessage)
+{
+ // For debugging.
+/* if (msg.Type == Message_LatencyTestSamples)
+ {
+ MessageLatencyTestSamples* pSamples = (MessageLatencyTestSamples*) &msg;
+
+ if (pSamples->Samples.GetSize() > 0)
+ {
+ // Just show the first one for now.
+ Color c = pSamples->Samples[0];
+ OVR_DEBUG_LOG(("%d %d %d", c.R, c.G, c.B));
+ }
+ return;
+ }
+*/
+
+ if (latencyTestMessage == LatencyTest_Timer)
+ {
+ if (!Device)
+ {
+ reset();
+ return;
+ }
+
+ if (State == State_WaitingForSettlePreCalibrationColorBlack)
+ {
+ // Send calibrate message to device and wait a while.
+ LatencyTestCalibrate calibrate(CALIBRATE_BLACK);
+ Device->SetCalibrate(calibrate);
+
+ State = State_WaitingForSettlePostCalibrationColorBlack;
+ OVR_DEBUG_LOG(("State_WaitingForSettlePreCalibrationColorBlack -> State_WaitingForSettlePostCalibrationColorBlack."));
+
+ setTimer(TIME_TO_WAIT_FOR_SETTLE_POST_CALIBRATION);
+ }
+ else if (State == State_WaitingForSettlePostCalibrationColorBlack)
+ {
+ // Change color to white and wait a while.
+ RenderColor = CALIBRATE_WHITE;
+
+ State = State_WaitingForSettlePreCalibrationColorWhite;
+ OVR_DEBUG_LOG(("State_WaitingForSettlePostCalibrationColorBlack -> State_WaitingForSettlePreCalibrationColorWhite."));
+
+ setTimer(TIME_TO_WAIT_FOR_SETTLE_PRE_CALIBRATION);
+ }
+ else if (State == State_WaitingForSettlePreCalibrationColorWhite)
+ {
+ // Send calibrate message to device and wait a while.
+ LatencyTestCalibrate calibrate(CALIBRATE_WHITE);
+ Device->SetCalibrate(calibrate);
+
+ State = State_WaitingForSettlePostCalibrationColorWhite;
+ OVR_DEBUG_LOG(("State_WaitingForSettlePreCalibrationColorWhite -> State_WaitingForSettlePostCalibrationColorWhite."));
+
+ setTimer(TIME_TO_WAIT_FOR_SETTLE_POST_CALIBRATION);
+ }
+ else if (State == State_WaitingForSettlePostCalibrationColorWhite)
+ {
+ // Calibration is done. Switch to color 1 and wait for it to settle.
+ RenderColor = COLOR1;
+
+ State = State_WaitingForSettlePostMeasurement;
+ OVR_DEBUG_LOG(("State_WaitingForSettlePostCalibrationColorWhite -> State_WaitingForSettlePostMeasurement."));
+
+ UInt32 waitTime = TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT + getRandomComponent(TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS);
+ setTimer(waitTime);
+ }
+ else if (State == State_WaitingForSettlePostMeasurement)
+ {
+ // Prepare for next measurement.
+
+ // Create a new result object.
+ MeasurementResult* pResult = new MeasurementResult();
+ Results.PushBack(pResult);
+
+ State = State_WaitingToTakeMeasurement;
+ OVR_DEBUG_LOG(("State_WaitingForSettlePostMeasurement -> State_WaitingToTakeMeasurement."));
+ }
+ else if (State == State_WaitingForTestStarted)
+ {
+ // We timed out waiting for 'TestStarted'. Abandon this measurement and setup for the next.
+ getActiveResult()->TimedOutWaitingForTestStarted = true;
+
+ State = State_WaitingForSettlePostMeasurement;
+ OVR_DEBUG_LOG(("** Timed out waiting for 'TestStarted'."));
+ OVR_DEBUG_LOG(("State_WaitingForTestStarted -> State_WaitingForSettlePostMeasurement."));
+
+ UInt32 waitTime = TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT + getRandomComponent(TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS);
+ setTimer(waitTime);
+ }
+ else if (State == State_WaitingForColorDetected)
+ {
+ // We timed out waiting for 'ColorDetected'. Abandon this measurement and setup for the next.
+ getActiveResult()->TimedOutWaitingForColorDetected = true;
+
+ State = State_WaitingForSettlePostMeasurement;
+ OVR_DEBUG_LOG(("** Timed out waiting for 'ColorDetected'."));
+ OVR_DEBUG_LOG(("State_WaitingForColorDetected -> State_WaitingForSettlePostMeasurement."));
+
+ UInt32 waitTime = TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT + getRandomComponent(TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS);
+ setTimer(waitTime);
+ }
+ }
+ else if (latencyTestMessage == LatencyTest_ProcessInputs)
+ {
+ if (State == State_WaitingToTakeMeasurement)
+ {
+ if (!Device)
+ {
+ reset();
+ return;
+ }
+
+ // Send 'StartTest' feature report with opposite target color.
+ if (RenderColor == COLOR1)
+ {
+ RenderColor = COLOR2;
+ }
+ else
+ {
+ RenderColor = COLOR1;
+ }
+
+ getActiveResult()->TargetColor = RenderColor;
+
+ // Record time so we can determine usb roundtrip time.
+ getActiveResult()->StartTestTicksMicroS = Timer::GetTicks();
+
+ LatencyTestStartTest startTest(RenderColor);
+ Device->SetStartTest(startTest);
+
+ State = State_WaitingForTestStarted;
+ OVR_DEBUG_LOG(("State_WaitingToTakeMeasurement -> State_WaitingForTestStarted."));
+
+ setTimer(TIMEOUT_WAITING_FOR_TEST_STARTED);
+ }
+ }
+ else if (msg.Type == Message_LatencyTestButton)
+ {
+ BeginTest();
+ }
+ else if (msg.Type == Message_LatencyTestStarted)
+ {
+ if (State == State_WaitingForTestStarted)
+ {
+ clearTimer();
+
+ // Record time so we can determine usb roundtrip time.
+ getActiveResult()->TestStartedTicksMicroS = Timer::GetTicks();
+
+ State = State_WaitingForColorDetected;
+ OVR_DEBUG_LOG(("State_WaitingForTestStarted -> State_WaitingForColorDetected."));
+
+ setTimer(TIMEOUT_WAITING_FOR_COLOR_DETECTED);
+ }
+ }
+ else if (msg.Type == Message_LatencyTestColorDetected)
+ {
+ if (State == State_WaitingForColorDetected)
+ {
+ // Record time to detect color.
+ MessageLatencyTestColorDetected* pDetected = (MessageLatencyTestColorDetected*) &msg;
+ UInt16 elapsedTime = pDetected->Elapsed;
+ OVR_DEBUG_LOG(("Time to 'ColorDetected' = %d", elapsedTime));
+
+ getActiveResult()->DeviceMeasuredElapsedMilliS = elapsedTime;
+
+ if (areResultsComplete())
+ {
+ // We're done.
+ processResults();
+ reset();
+ }
+ else
+ {
+ // Run another measurement.
+ State = State_WaitingForSettlePostMeasurement;
+ OVR_DEBUG_LOG(("State_WaitingForColorDetected -> State_WaitingForSettlePostMeasurement."));
+
+ UInt32 waitTime = TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT + getRandomComponent(TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS);
+ setTimer(waitTime);
+ }
+ }
+ }
+ else if (msg.Type == Message_DeviceRemoved)
+ {
+ reset();
+ }
+}
+
+LatencyTest::MeasurementResult* LatencyTest::getActiveResult()
+{
+ OVR_ASSERT(!Results.IsEmpty());
+ return Results.GetLast();
+}
+
+void LatencyTest::setTimer(UInt32 timeMilliS)
+{
+ ActiveTimerMilliS = timeMilliS;
+}
+
+void LatencyTest::clearTimer()
+{
+ ActiveTimerMilliS = 0;
+}
+
+void LatencyTest::reset()
+{
+ clearMeasurementResults();
+ State = State_WaitingForButton;
+
+ HaveOldTime = false;
+ ActiveTimerMilliS = 0;
+}
+
+void LatencyTest::clearMeasurementResults()
+{
+ while(!Results.IsEmpty())
+ {
+ MeasurementResult* pElem = Results.GetFirst();
+ pElem->RemoveNode();
+ delete pElem;
+ }
+}
+
+LatencyTest::LatencyTestHandler::~LatencyTestHandler()
+{
+ RemoveHandlerFromDevices();
+}
+
+void LatencyTest::LatencyTestHandler::OnMessage(const Message& msg)
+{
+ pLatencyTestUtil->handleMessage(msg);
+}
+
+void LatencyTest::ProcessInputs()
+{
+ updateForTimeouts();
+ handleMessage(Message(), LatencyTest_ProcessInputs);
+}
+
+bool LatencyTest::DisplayScreenColor(Color& colorToDisplay)
+{
+ updateForTimeouts();
+
+ if (State == State_WaitingForButton)
+ {
+ return false;
+ }
+
+ colorToDisplay = RenderColor;
+ return true;
+}
+
+const char* LatencyTest::GetResultsString()
+{
+ if (!ResultsString.IsEmpty() && ReturnedResultString != ResultsString.ToCStr())
+ {
+ ReturnedResultString = ResultsString;
+ return ReturnedResultString.ToCStr();
+ }
+
+ return NULL;
+}
+
+bool LatencyTest::areResultsComplete()
+{
+ UInt32 initialMeasurements = 0;
+
+ UInt32 measurements1to2 = 0;
+ UInt32 measurements2to1 = 0;
+
+ MeasurementResult* pCurr = Results.GetFirst();
+ while(true)
+ {
+ // Process.
+ if (!pCurr->TimedOutWaitingForTestStarted &&
+ !pCurr->TimedOutWaitingForColorDetected)
+ {
+ initialMeasurements++;
+
+ if (initialMeasurements > INITIAL_SAMPLES_TO_IGNORE)
+ {
+ if (pCurr->TargetColor == COLOR2)
+ {
+ measurements1to2++;
+ }
+ else
+ {
+ measurements2to1++;
+ }
+ }
+ }
+
+ if (Results.IsLast(pCurr))
+ {
+ break;
+ }
+ pCurr = Results.GetNext(pCurr);
+ }
+
+ if (measurements1to2 >= DEFAULT_NUMBER_OF_SAMPLES &&
+ measurements2to1 >= DEFAULT_NUMBER_OF_SAMPLES)
+ {
+ return true;
+ }
+
+ return false;
+}
+
+void LatencyTest::processResults()
+{
+
+ UInt32 minTime1To2 = UINT_MAX;
+ UInt32 maxTime1To2 = 0;
+ float averageTime1To2 = 0.0f;
+ UInt32 minTime2To1 = UINT_MAX;
+ UInt32 maxTime2To1 = 0;
+ float averageTime2To1 = 0.0f;
+
+ float minUSBTripMilliS = BIG_FLOAT;
+ float maxUSBTripMilliS = SMALL_FLOAT;
+ float averageUSBTripMilliS = 0.0f;
+ UInt32 countUSBTripTime = 0;
+
+ UInt32 measurementsCount = 0;
+ UInt32 measurements1to2 = 0;
+ UInt32 measurements2to1 = 0;
+
+ MeasurementResult* pCurr = Results.GetFirst();
+ UInt32 count = 0;
+ while(true)
+ {
+ count++;
+
+ if (!pCurr->TimedOutWaitingForTestStarted &&
+ !pCurr->TimedOutWaitingForColorDetected)
+ {
+ measurementsCount++;
+
+ if (measurementsCount > INITIAL_SAMPLES_TO_IGNORE)
+ {
+ if (pCurr->TargetColor == COLOR2)
+ {
+ measurements1to2++;
+
+ if (measurements1to2 <= DEFAULT_NUMBER_OF_SAMPLES)
+ {
+ UInt32 elapsed = pCurr->DeviceMeasuredElapsedMilliS;
+
+ minTime1To2 = Alg::Min(elapsed, minTime1To2);
+ maxTime1To2 = Alg::Max(elapsed, maxTime1To2);
+
+ averageTime1To2 += (float) elapsed;
+ }
+ }
+ else
+ {
+ measurements2to1++;
+
+ if (measurements2to1 <= DEFAULT_NUMBER_OF_SAMPLES)
+ {
+ UInt32 elapsed = pCurr->DeviceMeasuredElapsedMilliS;
+
+ minTime2To1 = Alg::Min(elapsed, minTime2To1);
+ maxTime2To1 = Alg::Max(elapsed, maxTime2To1);
+
+ averageTime2To1 += (float) elapsed;
+ }
+ }
+
+ float usbRountripElapsedMilliS = 0.001f * (float) (pCurr->TestStartedTicksMicroS - pCurr->StartTestTicksMicroS);
+ minUSBTripMilliS = Alg::Min(usbRountripElapsedMilliS, minUSBTripMilliS);
+ maxUSBTripMilliS = Alg::Max(usbRountripElapsedMilliS, maxUSBTripMilliS);
+ averageUSBTripMilliS += usbRountripElapsedMilliS;
+ countUSBTripTime++;
+ }
+ }
+
+ if (measurements1to2 >= DEFAULT_NUMBER_OF_SAMPLES &&
+ measurements2to1 >= DEFAULT_NUMBER_OF_SAMPLES)
+ {
+ break;
+ }
+
+ if (Results.IsLast(pCurr))
+ {
+ break;
+ }
+ pCurr = Results.GetNext(pCurr);
+ }
+
+ averageTime1To2 /= (float) DEFAULT_NUMBER_OF_SAMPLES;
+ averageTime2To1 /= (float) DEFAULT_NUMBER_OF_SAMPLES;
+
+ averageUSBTripMilliS /= countUSBTripTime;
+
+ float finalResult = 0.5f * (averageTime1To2 + averageTime2To1);
+ finalResult += averageUSBTripMilliS;
+
+ ResultsString.Clear();
+ ResultsString.AppendFormat("RESULT=%.1f (add half Tracker period) [b->w %d|%.1f|%d] [w->b %d|%.1f|%d] [usb rndtrp %.1f|%.1f|%.1f] [cnt %d] [tmouts %d]",
+ finalResult,
+ minTime1To2, averageTime1To2, maxTime1To2,
+ minTime2To1, averageTime2To1, maxTime2To1,
+ minUSBTripMilliS, averageUSBTripMilliS, maxUSBTripMilliS,
+ DEFAULT_NUMBER_OF_SAMPLES*2, count - measurementsCount);
+}
+
+void LatencyTest::updateForTimeouts()
+{
+ if (!HaveOldTime)
+ {
+ HaveOldTime = true;
+ OldTime = Timer::GetTicksMs();
+ return;
+ }
+
+ UInt32 newTime = Timer::GetTicksMs();
+ UInt32 elapsedMilliS = newTime - OldTime;
+ if (newTime < OldTime)
+ {
+ elapsedMilliS = OldTime - newTime;
+ elapsedMilliS = UINT_MAX - elapsedMilliS;
+ }
+ OldTime = newTime;
+
+ elapsedMilliS = Alg::Min(elapsedMilliS, (UInt32) 100); // Clamp at 100mS in case we're not being called very often.
+
+
+ if (ActiveTimerMilliS == 0)
+ {
+ return;
+ }
+
+ if (elapsedMilliS >= ActiveTimerMilliS)
+ {
+ ActiveTimerMilliS = 0;
+ handleMessage(Message(), LatencyTest_Timer);
+ return;
+ }
+
+ ActiveTimerMilliS -= elapsedMilliS;
+}
+
+}} // namespace OVR::Util
diff --git a/LibOVR/Src/Util/Util_LatencyTest.h b/LibOVR/Src/Util/Util_LatencyTest.h
new file mode 100644
index 0000000..47f98f7
--- /dev/null
+++ b/LibOVR/Src/Util/Util_LatencyTest.h
@@ -0,0 +1,160 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : Util_LatencyTest.h
+Content : Wraps the lower level LatencyTesterDevice and adds functionality.
+Created : February 14, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_Util_LatencyTest_h
+#define OVR_Util_LatencyTest_h
+
+#include "../OVR_Device.h"
+
+#include "../Kernel/OVR_String.h"
+#include "../Kernel/OVR_List.h"
+
+namespace OVR { namespace Util {
+
+
+//-------------------------------------------------------------------------------------
+// ***** LatencyTest
+//
+// LatencyTest utility class wraps the low level LatencyTestDevice and manages the scheduling
+// of a latency test. A single test is composed of a series of individual latency measurements
+// which are used to derive min, max, and an average latency value.
+//
+// Developers are required to call the following methods:
+// SetDevice - Sets the LatencyTestDevice to be used for the tests.
+// ProcessInputs - This should be called at the same place in the code where the game engine
+// reads the headset orientation from LibOVR (typically done by calling
+// 'GetOrientation' on the SensorFusion object). Calling this at the right time
+// enables us to measure the same latency that occurs for headset orientation
+// changes.
+// DisplayScreenColor - The latency tester works by sensing the color of the pixels directly
+// beneath it. The color of these pixels can be set by drawing a small
+// quad at the end of the rendering stage. The quad should be small
+// such that it doesn't significantly impact the rendering of the scene,
+// but large enough to be 'seen' by the sensor. See the SDK
+// documentation for more information.
+// GetResultsString - Call this to get a string containing the most recent results.
+// If the string has already been gotten then NULL will be returned.
+// The string pointer will remain valid until the next time this
+// method is called.
+//
+
+class LatencyTest : public NewOverrideBase
+{
+public:
+ LatencyTest(LatencyTestDevice* device = NULL);
+ ~LatencyTest();
+
+ // Set the Latency Tester device that we'll use to send commands to and receive
+ // notification messages from.
+ bool SetDevice(LatencyTestDevice* device);
+
+ // Returns true if this LatencyTestUtil has a Latency Tester device.
+ bool HasDevice() const
+ { return Handler.IsHandlerInstalled(); }
+
+ void ProcessInputs();
+ bool DisplayScreenColor(Color& colorToDisplay);
+ const char* GetResultsString();
+
+ // Begin test. Equivalent to pressing the button on the latency tester.
+ void BeginTest();
+
+private:
+ LatencyTest* getThis() { return this; }
+
+ enum LatencyTestMessageType
+ {
+ LatencyTest_None,
+ LatencyTest_Timer,
+ LatencyTest_ProcessInputs,
+ };
+
+ UInt32 getRandomComponent(UInt32 range);
+ void handleMessage(const Message& msg, LatencyTestMessageType latencyTestMessage = LatencyTest_None);
+ void reset();
+ void setTimer(UInt32 timeMilliS);
+ void clearTimer();
+
+ class LatencyTestHandler : public MessageHandler
+ {
+ LatencyTest* pLatencyTestUtil;
+ public:
+ LatencyTestHandler(LatencyTest* latencyTester) : pLatencyTestUtil(latencyTester) { }
+ ~LatencyTestHandler();
+
+ virtual void OnMessage(const Message& msg);
+ };
+
+ bool areResultsComplete();
+ void processResults();
+ void updateForTimeouts();
+
+ Ptr<LatencyTestDevice> Device;
+ LatencyTestHandler Handler;
+
+ enum TesterState
+ {
+ State_WaitingForButton,
+ State_WaitingForSettlePreCalibrationColorBlack,
+ State_WaitingForSettlePostCalibrationColorBlack,
+ State_WaitingForSettlePreCalibrationColorWhite,
+ State_WaitingForSettlePostCalibrationColorWhite,
+ State_WaitingToTakeMeasurement,
+ State_WaitingForTestStarted,
+ State_WaitingForColorDetected,
+ State_WaitingForSettlePostMeasurement
+ };
+ TesterState State;
+
+ bool HaveOldTime;
+ UInt32 OldTime;
+ UInt32 ActiveTimerMilliS;
+
+ Color RenderColor;
+
+ struct MeasurementResult : public ListNode<MeasurementResult>, public NewOverrideBase
+ {
+ MeasurementResult()
+ : DeviceMeasuredElapsedMilliS(0),
+ TimedOutWaitingForTestStarted(false),
+ TimedOutWaitingForColorDetected(false),
+ StartTestTicksMicroS(0),
+ TestStartedTicksMicroS(0)
+ {}
+
+ Color TargetColor;
+
+ UInt32 DeviceMeasuredElapsedMilliS;
+
+ bool TimedOutWaitingForTestStarted;
+ bool TimedOutWaitingForColorDetected;
+
+ UInt64 StartTestTicksMicroS;
+ UInt64 TestStartedTicksMicroS;
+ };
+
+ List<MeasurementResult> Results;
+ void clearMeasurementResults();
+
+ MeasurementResult* getActiveResult();
+
+ StringBuffer ResultsString;
+ String ReturnedResultString;
+};
+
+}} // namespace OVR::Util
+
+#endif // OVR_Util_LatencyTest_h
diff --git a/LibOVR/Src/Util/Util_MagCalibration.cpp b/LibOVR/Src/Util/Util_MagCalibration.cpp
new file mode 100644
index 0000000..f3e72f5
--- /dev/null
+++ b/LibOVR/Src/Util/Util_MagCalibration.cpp
@@ -0,0 +1,180 @@
+/************************************************************************************
+
+Filename : Util_MagCalibration.cpp
+Content : Procedures for calibrating the magnetometer
+Created : April 16, 2013
+Authors : Steve LaValle, Andrew Reisse
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#include "Util_MagCalibration.h"
+
+namespace OVR { namespace Util {
+
+void MagCalibration::BeginAutoCalibration(SensorFusion& sf)
+{
+ Status = Mag_AutoCalibrating;
+ // This is a "hard" reset of the mag, so need to clear stored values
+ sf.ClearMagCalibration();
+ SampleCount = 0;
+}
+
+unsigned MagCalibration::UpdateAutoCalibration(SensorFusion& sf)
+{
+ if (Status != Mag_AutoCalibrating)
+ return Status;
+
+ Quatf q = sf.GetOrientation();
+ Vector3f m = sf.GetMagnetometer();
+
+ InsertIfAcceptable(q, m);
+
+ if ((SampleCount == 4) && (Status == Mag_AutoCalibrating))
+ SetCalibration(sf);
+
+ return Status;
+
+}
+
+void MagCalibration::BeginManualCalibration(SensorFusion& sf)
+{
+ Status = Mag_ManuallyCalibrating;
+ sf.ClearMagCalibration();
+ SampleCount = 0;
+}
+
+bool MagCalibration::IsAcceptableSample(const Quatf& q, const Vector3f& m)
+{
+ switch (SampleCount)
+ {
+ // Initial sample is always acceptable
+ case 0:
+ return true;
+ break;
+ case 1:
+ return (q.DistanceSq(QuatSamples[0]) > MinQuatDistanceSq)&&
+ ((m - MagSamples[0]).LengthSq() > MinMagDistanceSq);
+ break;
+ case 2:
+ return (q.DistanceSq(QuatSamples[0]) > MinQuatDistanceSq)&&
+ (q.DistanceSq(QuatSamples[1]) > MinQuatDistanceSq)&&
+ ((m - MagSamples[0]).LengthSq() > MinMagDistanceSq)&&
+ ((m - MagSamples[1]).LengthSq() > MinMagDistanceSq);
+ break;
+ case 3:
+ return (q.DistanceSq(QuatSamples[0]) > MinQuatDistanceSq)&&
+ (q.DistanceSq(QuatSamples[1]) > MinQuatDistanceSq)&&
+ (q.DistanceSq(QuatSamples[2]) > MinQuatDistanceSq)&&
+ ((PointToPlaneDistance(MagSamples[0],MagSamples[1],MagSamples[2],m) > MinMagDistance)||
+ (PointToPlaneDistance(MagSamples[1],MagSamples[2],m,MagSamples[0]) > MinMagDistance)||
+ (PointToPlaneDistance(MagSamples[2],m,MagSamples[0],MagSamples[1]) > MinMagDistance)||
+ (PointToPlaneDistance(m,MagSamples[0],MagSamples[1],MagSamples[2]) > MinMagDistance));
+ }
+
+ return false;
+}
+
+
+bool MagCalibration::InsertIfAcceptable(const Quatf& q, const Vector3f& m)
+{
+ if (IsAcceptableSample(q, m))
+ {
+ MagSamples[SampleCount] = m;
+ QuatSamples[SampleCount] = q;
+ SampleCount++;
+ return true;
+ }
+
+ return false;
+}
+
+
+bool MagCalibration::SetCalibration(SensorFusion& sf)
+{
+ if (SampleCount < 4)
+ return false;
+
+ MagCenter = CalculateSphereCenter(MagSamples[0],MagSamples[1],MagSamples[2],MagSamples[3]);
+ Matrix4f calMat = Matrix4f();
+ calMat.M[0][3] = -MagCenter.x;
+ calMat.M[1][3] = -MagCenter.y;
+ calMat.M[2][3] = -MagCenter.z;
+ sf.SetMagCalibration(calMat);
+ Status = Mag_Calibrated;
+ //LogText("MagCenter: %f %f %f\n",MagCenter.x,MagCenter.y,MagCenter.z);
+
+ return true;
+}
+
+
+// Calculate the center of a sphere that passes through p1, p2, p3, p4
+Vector3f MagCalibration::CalculateSphereCenter(const Vector3f& p1, const Vector3f& p2,
+ const Vector3f& p3, const Vector3f& p4)
+{
+ Matrix4f A;
+ int i;
+ Vector3f p[4];
+ p[0] = p1;
+ p[1] = p2;
+ p[2] = p3;
+ p[3] = p4;
+
+ for (i = 0; i < 4; i++)
+ {
+ A.M[i][0] = p[i].x;
+ A.M[i][1] = p[i].y;
+ A.M[i][2] = p[i].z;
+ A.M[i][3] = 1.0f;
+ }
+ float m11 = A.Determinant();
+ OVR_ASSERT(m11 != 0.0f);
+
+ for (i = 0; i < 4; i++)
+ {
+ A.M[i][0] = p[i].x*p[i].x + p[i].y*p[i].y + p[i].z*p[i].z;
+ A.M[i][1] = p[i].y;
+ A.M[i][2] = p[i].z;
+ A.M[i][3] = 1.0f;
+ }
+ float m12 = A.Determinant();
+
+ for (i = 0; i < 4; i++)
+ {
+ A.M[i][0] = p[i].x*p[i].x + p[i].y*p[i].y + p[i].z*p[i].z;
+ A.M[i][1] = p[i].x;
+ A.M[i][2] = p[i].z;
+ A.M[i][3] = 1.0f;
+ }
+ float m13 = A.Determinant();
+
+ for (i = 0; i < 4; i++)
+ {
+ A.M[i][0] = p[i].x*p[i].x + p[i].y*p[i].y + p[i].z*p[i].z;
+ A.M[i][1] = p[i].x;
+ A.M[i][2] = p[i].y;
+ A.M[i][3] = 1.0f;
+ }
+ float m14 = A.Determinant();
+
+ float c = 0.5f / m11;
+ return Vector3f(c*m12, -c*m13, c*m14);
+}
+
+// Distance from p4 to the nearest point on a plane through p1, p2, p3
+float MagCalibration::PointToPlaneDistance(const Vector3f& p1, const Vector3f& p2,
+ const Vector3f& p3, const Vector3f& p4)
+{
+ Vector3f v1 = p1 - p2;
+ Vector3f v2 = p1 - p3;
+ Vector3f planeNormal = v1.Cross(v2);
+ planeNormal.Normalize();
+ return (fabs((planeNormal * p4) - planeNormal * p1));
+}
+
+}}
diff --git a/LibOVR/Src/Util/Util_MagCalibration.h b/LibOVR/Src/Util/Util_MagCalibration.h
new file mode 100644
index 0000000..9371125
--- /dev/null
+++ b/LibOVR/Src/Util/Util_MagCalibration.h
@@ -0,0 +1,115 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : Util_MagCalibration.h
+Content : Procedures for calibrating the magnetometer
+Created : April 16, 2013
+Authors : Steve LaValle, Andrew Reisse
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_Util_MagCalibration_h
+#define OVR_Util_MagCalibration_h
+
+#include "../OVR_SensorFusion.h"
+#include "../Kernel/OVR_String.h"
+#include "../Kernel/OVR_Log.h"
+
+namespace OVR { namespace Util {
+
+class MagCalibration
+{
+public:
+ enum MagStatus
+ {
+ Mag_Uninitialized = 0,
+ Mag_AutoCalibrating = 1,
+ Mag_ManuallyCalibrating = 2,
+ Mag_Calibrated = 3,
+ };
+
+ MagCalibration() :
+ Status(Mag_Uninitialized),
+ MinMagDistance(0.3f), MinQuatDistance(0.5f),
+ SampleCount(0)
+ {
+ MinMagDistanceSq = MinMagDistance * MinMagDistance;
+ MinQuatDistanceSq = MinQuatDistance * MinQuatDistance;
+ }
+
+ // Methods that are useful for either auto or manual calibration
+ bool IsUnitialized() const { return Status == Mag_Uninitialized; }
+ bool IsCalibrated() const { return Status == Mag_Calibrated; }
+ int NumberOfSamples() const { return SampleCount; }
+ int RequiredSampleCount() const { return 4; }
+ void ClearCalibration(SensorFusion& sf)
+ {
+ Status = Mag_Uninitialized;
+ SampleCount = 0;
+ sf.ClearMagCalibration();
+ };
+
+ // Methods for automatic magnetometer calibration
+ void BeginAutoCalibration(SensorFusion& sf);
+ unsigned UpdateAutoCalibration(SensorFusion& sf);
+ bool IsAutoCalibrating() const { return Status == Mag_AutoCalibrating; }
+
+ // Methods for building a manual (user-guided) calibraton procedure
+ void BeginManualCalibration(SensorFusion& sf);
+ bool IsAcceptableSample(const Quatf& q, const Vector3f& m);
+ bool InsertIfAcceptable(const Quatf& q, const Vector3f& m);
+ // Returns true if successful, requiring that SampleCount = 4
+ bool SetCalibration(SensorFusion& sf);
+ bool IsManuallyCalibrating() const { return Status == Mag_ManuallyCalibrating; }
+
+ // This is the minimum acceptable distance (Euclidean) between raw
+ // magnetometer values to be acceptable for usage in calibration.
+ void SetMinMagDistance(float dist)
+ {
+ MinMagDistance = dist;
+ MinMagDistanceSq = MinMagDistance * MinMagDistance;
+ }
+
+ // The minimum acceptable distance (4D Euclidean) between orientations
+ // to be acceptable for calibration usage.
+ void SetMinQuatDistance(float dist)
+ {
+ MinQuatDistance = dist;
+ MinQuatDistanceSq = MinQuatDistance * MinQuatDistance;
+ }
+
+ // A result of the calibration, which is the center of a sphere that
+ // roughly approximates the magnetometer data.
+ Vector3f GetMagCenter() const { return MagCenter; }
+
+private:
+ // Determine the unique sphere through 4 non-coplanar points
+ Vector3f CalculateSphereCenter(const Vector3f& p1, const Vector3f& p2,
+ const Vector3f& p3, const Vector3f& p4);
+
+ // Distance from p4 to the nearest point on a plane through p1, p2, p3
+ float PointToPlaneDistance(const Vector3f& p1, const Vector3f& p2,
+ const Vector3f& p3, const Vector3f& p4);
+
+ Vector3f MagCenter;
+ unsigned Status;
+ float MinMagDistance;
+ float MinQuatDistance;
+ float MinMagDistanceSq;
+ float MinQuatDistanceSq;
+
+ unsigned SampleCount;
+ Vector3f MagSamples[4];
+ Quatf QuatSamples[4];
+
+};
+
+}}
+
+#endif
diff --git a/LibOVR/Src/Util/Util_Render_Stereo.cpp b/LibOVR/Src/Util/Util_Render_Stereo.cpp
new file mode 100644
index 0000000..8986e30
--- /dev/null
+++ b/LibOVR/Src/Util/Util_Render_Stereo.cpp
@@ -0,0 +1,311 @@
+/************************************************************************************
+
+Filename : Util_Render_Stereo.cpp
+Content : Stereo rendering configuration implementation
+Created : October 22, 2012
+Authors : Michael Antonov, Andrew Reisse
+
+Copyright : Copyright 2012 Oculus, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus Inc license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#include "Util_Render_Stereo.h"
+
+namespace OVR { namespace Util { namespace Render {
+
+
+//-----------------------------------------------------------------------------------
+
+// DistortionFnInverse computes the inverse of the distortion function on an argument.
+float DistortionConfig::DistortionFnInverse(float r)
+{
+ OVR_ASSERT((r <= 10.0f));
+
+ float s, d;
+ float delta = r * 0.25f;
+
+ s = r * 0.5f;
+ d = fabs(r - DistortionFn(s));
+
+ for (int i = 0; i < 20; i++)
+ {
+ float sUp = s + delta;
+ float sDown = s - delta;
+ float dUp = fabs(r - DistortionFn(sUp));
+ float dDown = fabs(r - DistortionFn(sDown));
+
+ if (dUp < d)
+ {
+ s = sUp;
+ d = dUp;
+ }
+ else if (dDown < d)
+ {
+ s = sDown;
+ d = dDown;
+ }
+ else
+ {
+ delta *= 0.5f;
+ }
+ }
+
+ return s;
+}
+
+
+//-----------------------------------------------------------------------------------
+// **** StereoConfig Implementation
+
+StereoConfig::StereoConfig(StereoMode mode, const Viewport& vp)
+ : Mode(mode),
+ InterpupillaryDistance(0.064f), AspectMultiplier(1.0f),
+ FullView(vp), DirtyFlag(true),
+ YFov(0), Aspect(vp.w / float(vp.h)), ProjectionCenterOffset(0),
+ OrthoPixelOffset(0)
+{
+ // And default distortion for it.
+ Distortion.SetCoefficients(1.0f, 0.22f, 0.24f);
+ Distortion.Scale = 1.0f; // Will be computed later.
+
+ // Fit left of the image.
+ DistortionFitX = -1.0f;
+ DistortionFitY = 0.0f;
+
+ // Initialize "fake" default HMD values for testing without HMD plugged in.
+ // These default values match those returned by the HMD.
+ HMD.HResolution = 1280;
+ HMD.VResolution = 800;
+ HMD.HScreenSize = 0.14976f;
+ HMD.VScreenSize = HMD.HScreenSize / (1280.0f / 800.0f);
+ HMD.InterpupillaryDistance = InterpupillaryDistance;
+ HMD.LensSeparationDistance = 0.0635f;
+ HMD.EyeToScreenDistance = 0.041f;
+ HMD.DistortionK[0] = Distortion.K[0];
+ HMD.DistortionK[1] = Distortion.K[1];
+ HMD.DistortionK[2] = Distortion.K[2];
+ HMD.DistortionK[3] = 0;
+
+ Set2DAreaFov(DegreeToRad(85.0f));
+}
+
+void StereoConfig::SetFullViewport(const Viewport& vp)
+{
+ if (vp != FullView)
+ {
+ FullView = vp;
+ DirtyFlag = true;
+ }
+}
+
+void StereoConfig::SetHMDInfo(const HMDInfo& hmd)
+{
+ HMD = hmd;
+ Distortion.K[0] = hmd.DistortionK[0];
+ Distortion.K[1] = hmd.DistortionK[1];
+ Distortion.K[2] = hmd.DistortionK[2];
+ Distortion.K[3] = hmd.DistortionK[3];
+
+ Distortion.SetChromaticAberration(hmd.ChromaAbCorrection[0], hmd.ChromaAbCorrection[1],
+ hmd.ChromaAbCorrection[2], hmd.ChromaAbCorrection[3]);
+
+ DirtyFlag = true;
+}
+
+void StereoConfig::SetDistortionFitPointVP(float x, float y)
+{
+ DistortionFitX = x;
+ DistortionFitY = y;
+ DirtyFlag = true;
+}
+
+void StereoConfig::SetDistortionFitPointPixels(float x, float y)
+{
+ DistortionFitX = (4 * x / float(FullView.w)) - 1.0f;
+ DistortionFitY = (2 * y / float(FullView.h)) - 1.0f;
+ DirtyFlag = true;
+}
+
+void StereoConfig::Set2DAreaFov(float fovRadians)
+{
+ Area2DFov = fovRadians;
+ DirtyFlag = true;
+}
+
+
+const StereoEyeParams& StereoConfig::GetEyeRenderParams(StereoEye eye)
+{
+ static const UByte eyeParamIndices[3] = { 0, 0, 1 };
+
+ updateIfDirty();
+ OVR_ASSERT(eye < sizeof(eyeParamIndices));
+ return EyeRenderParams[eyeParamIndices[eye]];
+}
+
+
+void StereoConfig::updateComputedState()
+{
+ // Need to compute all of the following:
+ // - Aspect Ratio
+ // - FOV
+ // - Projection offsets for 3D
+ // - Distortion XCenterOffset
+ // - Update 2D
+ // - Initialize EyeRenderParams
+
+ // Compute aspect ratio. Stereo mode cuts width in half.
+ Aspect = float(FullView.w) / float(FullView.h);
+ Aspect *= (Mode == Stereo_None) ? 1.0f : 0.5f;
+ Aspect *= AspectMultiplier;
+
+ updateDistortionOffsetAndScale();
+
+ // Compute Vertical FOV based on distance, distortion, etc.
+ // Distance from vertical center to render vertical edge perceived through the lens.
+ // This will be larger then normal screen size due to magnification & distortion.
+ //
+ // This percievedHalfRTDistance equation should hold as long as the render target
+ // and display have the same aspect ratios. What we'd like to know is where the edge
+ // of the render target will on the perceived screen surface. With NO LENS,
+ // the answer would be:
+ //
+ // halfRTDistance = (VScreenSize / 2) * aspect *
+ // DistortionFn_Inverse( DistortionScale / aspect )
+ //
+ // To model the optical lens we eliminates DistortionFn_Inverse. Aspect ratios
+ // cancel out, so we get:
+ //
+ // halfRTDistance = (VScreenSize / 2) * DistortionScale
+ //
+ if (Mode == Stereo_None)
+ {
+ YFov = DegreeToRad(80.0f);
+ }
+ else
+ {
+ float percievedHalfRTDistance = (HMD.VScreenSize / 2) * Distortion.Scale;
+ YFov = 2.0f * atan(percievedHalfRTDistance/HMD.EyeToScreenDistance);
+ }
+
+ updateProjectionOffset();
+ update2D();
+ updateEyeParams();
+
+ DirtyFlag = false;
+}
+
+void StereoConfig::updateDistortionOffsetAndScale()
+{
+ // Distortion center shift is stored separately, since it isn't affected
+ // by the eye distance.
+ float lensOffset = HMD.LensSeparationDistance * 0.5f;
+ float lensShift = HMD.HScreenSize * 0.25f - lensOffset;
+ float lensViewportShift = 4.0f * lensShift / HMD.HScreenSize;
+ Distortion.XCenterOffset= lensViewportShift;
+
+ // Compute distortion scale from DistortionFitX & DistortionFitY.
+ // Fit value of 0.0 means "no fit".
+ if ((fabs(DistortionFitX) < 0.0001f) && (fabs(DistortionFitY) < 0.0001f))
+ {
+ Distortion.Scale = 1.0f;
+ }
+ else
+ {
+ // Convert fit value to distortion-centered coordinates before fit radius
+ // calculation.
+ float stereoAspect = 0.5f * float(FullView.w) / float(FullView.h);
+ float dx = DistortionFitX - Distortion.XCenterOffset;
+ float dy = DistortionFitY / stereoAspect;
+ float fitRadius = sqrt(dx * dx + dy * dy);
+ Distortion.Scale = Distortion.DistortionFn(fitRadius)/fitRadius;
+ }
+}
+
+void StereoConfig::updateProjectionOffset()
+{
+ // Post-projection viewport coordinates range from (-1.0, 1.0), with the
+ // center of the left viewport falling at (1/4) of horizontal screen size.
+ // We need to shift this projection center to match with the lens center;
+ // note that we don't use the IPD here due to collimated light property of the lens.
+ // We compute this shift in physical units (meters) to
+ // correct for different screen sizes and then rescale to viewport coordinates.
+ float viewCenter = HMD.HScreenSize * 0.25f;
+ float eyeProjectionShift = viewCenter - HMD.LensSeparationDistance*0.5f;
+ ProjectionCenterOffset = 4.0f * eyeProjectionShift / HMD.HScreenSize;
+}
+
+void StereoConfig::update2D()
+{
+ // Orthographic projection fakes a screen at a distance of 0.8m from the
+ // eye, where hmd screen projection surface is at 0.05m distance.
+ // This introduces an extra off-center pixel projection shift based on eye distance.
+ // This offCenterShift is the pixel offset of the other camera's center
+ // in your reference camera based on surface distance.
+ float metersToPixels = (HMD.HResolution / HMD.HScreenSize);
+ float lensDistanceScreenPixels= metersToPixels * HMD.LensSeparationDistance;
+ float eyeDistanceScreenPixels = metersToPixels * InterpupillaryDistance;
+ float offCenterShiftPixels = (HMD.EyeToScreenDistance / 0.8f) * eyeDistanceScreenPixels;
+ float leftPixelCenter = (HMD.HResolution / 2) - lensDistanceScreenPixels * 0.5f;
+ float rightPixelCenter = lensDistanceScreenPixels * 0.5f;
+ float pixelDifference = leftPixelCenter - rightPixelCenter;
+
+ // This computes the number of pixels that fit within specified 2D FOV (assuming
+ // distortion scaling will be done).
+ float percievedHalfScreenDistance = tan(Area2DFov * 0.5f) * HMD.EyeToScreenDistance;
+ float vfovSize = 2.0f * percievedHalfScreenDistance / Distortion.Scale;
+ FovPixels = HMD.VResolution * vfovSize / HMD.VScreenSize;
+
+ // Create orthographic matrix.
+ Matrix4f& m = OrthoCenter;
+ m.SetIdentity();
+ m.M[0][0] = FovPixels / (FullView.w * 0.5f);
+ m.M[1][1] = -FovPixels / FullView.h;
+ m.M[0][3] = 0;
+ m.M[1][3] = 0;
+ m.M[2][2] = 0;
+
+ float orthoPixelOffset = (pixelDifference + offCenterShiftPixels/Distortion.Scale) * 0.5f;
+ OrthoPixelOffset = orthoPixelOffset * 2.0f / FovPixels;
+}
+
+void StereoConfig::updateEyeParams()
+{
+ // Projection matrix for the center eye, which the left/right matrices are based on.
+ Matrix4f projCenter = Matrix4f::PerspectiveRH(YFov, Aspect, 0.01f, 1000.0f);
+
+ switch(Mode)
+ {
+ case Stereo_None:
+ {
+ EyeRenderParams[0].Init(StereoEye_Center, FullView, 0, projCenter, OrthoCenter);
+ }
+ break;
+
+ case Stereo_LeftRight_Multipass:
+ {
+ Matrix4f projLeft = Matrix4f::Translation(ProjectionCenterOffset, 0, 0) * projCenter,
+ projRight = Matrix4f::Translation(-ProjectionCenterOffset, 0, 0) * projCenter;
+
+ EyeRenderParams[0].Init(StereoEye_Left,
+ Viewport(FullView.x, FullView.y, FullView.w/2, FullView.h),
+ +InterpupillaryDistance * 0.5f, // World view shift.
+ projLeft, OrthoCenter * Matrix4f::Translation(OrthoPixelOffset, 0, 0),
+ &Distortion);
+ EyeRenderParams[1].Init(StereoEye_Right,
+ Viewport(FullView.x + FullView.w/2, FullView.y, FullView.w/2, FullView.h),
+ -InterpupillaryDistance * 0.5f,
+ projRight, OrthoCenter * Matrix4f::Translation(-OrthoPixelOffset, 0, 0),
+ &Distortion);
+ }
+ break;
+ }
+
+}
+
+
+}}} // OVR::Util::Render
+
diff --git a/LibOVR/Src/Util/Util_Render_Stereo.h b/LibOVR/Src/Util/Util_Render_Stereo.h
new file mode 100644
index 0000000..9dc110c
--- /dev/null
+++ b/LibOVR/Src/Util/Util_Render_Stereo.h
@@ -0,0 +1,299 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : Util_Render_Stereo.h
+Content : Sample stereo rendering configuration classes.
+Created : October 22, 2012
+Authors : Michael Antonov
+
+Copyright : Copyright 2012 Oculus, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus Inc license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_Util_Render_Stereo_h
+#define OVR_Util_Render_Stereo_h
+
+#include "../OVR_Device.h"
+
+namespace OVR { namespace Util { namespace Render {
+
+
+//-----------------------------------------------------------------------------------
+// ***** Stereo Enumerations
+
+// StereoMode describes rendering modes that can be used by StereoConfig.
+// These modes control whether stereo rendering is used or not (Stereo_None),
+// and how it is implemented.
+enum StereoMode
+{
+ Stereo_None = 0,
+ Stereo_LeftRight_Multipass = 1
+};
+
+
+// StereoEye specifies which eye we are rendering for; it is used to
+// retrieve StereoEyeParams.
+enum StereoEye
+{
+ StereoEye_Center,
+ StereoEye_Left,
+ StereoEye_Right
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** Viewport
+
+// Viewport describes a rectangular area used for rendering, in pixels.
+struct Viewport
+{
+ int x, y;
+ int w, h;
+
+ Viewport() {}
+ Viewport(int x1, int y1, int w1, int h1) : x(x1), y(y1), w(w1), h(h1) { }
+
+ bool operator == (const Viewport& vp) const
+ { return (x == vp.x) && (y == vp.y) && (w == vp.w) && (h == vp.h); }
+ bool operator != (const Viewport& vp) const
+ { return !operator == (vp); }
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** DistortionConfig
+
+// DistortionConfig Provides controls for the distortion shader.
+// - K[0] - K[3] are coefficients for the distortion function.
+// - XCenterOffset is the offset of lens distortion center from the
+// center of one-eye screen half. [-1, 1] Range.
+// - Scale is a factor of how much larger will the input image be,
+// with a factor of 1.0f being no scaling. An inverse of this
+// value is applied to sampled UV coordinates (1/Scale).
+// - ChromaticAberration is an array of parameters for controlling
+// additional Red and Blue scaling in order to reduce chromatic aberration
+// caused by the Rift lenses.
+class DistortionConfig
+{
+public:
+ DistortionConfig(float k0 = 1.0f, float k1 = 0.0f, float k2 = 0.0f, float k3 = 0.0f)
+ : XCenterOffset(0), YCenterOffset(0), Scale(1.0f)
+ {
+ SetCoefficients(k0, k1, k2, k3);
+ SetChromaticAberration();
+ }
+
+ void SetCoefficients(float k0, float k1 = 0.0f, float k2 = 0.0f, float k3 = 0.0f)
+ { K[0] = k0; K[1] = k1; K[2] = k2; K[3] = k3; }
+
+ void SetChromaticAberration(float red1 = 1.0f, float red2 = 0.0f, float blue1 = 1.0f, float blue2 = 0.0f)
+ { ChromaticAberration[0] = red1; ChromaticAberration[1] = red2; ChromaticAberration[2] = blue1; ChromaticAberration[3] = blue2; }
+
+
+ // DistortionFn applies distortion equation to the argument. The returned
+ // value should match distortion equation used in shader.
+ float DistortionFn(float r) const
+ {
+ float rsq = r * r;
+ float scale = r * (K[0] + K[1] * rsq + K[2] * rsq * rsq + K[3] * rsq * rsq * rsq);
+ return scale;
+ }
+
+ // DistortionFnInverse computes the inverse of the distortion function on an argument.
+ float DistortionFnInverse(float r);
+
+ float K[4];
+ float XCenterOffset, YCenterOffset;
+ float Scale;
+
+ float ChromaticAberration[4]; // Additional per-channel scaling is applied after distortion:
+ // Index [0] - Red channel constant coefficient.
+ // Index [1] - Red channel r^2 coefficient.
+ // Index [2] - Blue channel constant coefficient.
+ // Index [3] - Blue channel r^2 coefficient.
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** StereoEyeParams
+
+// StereoEyeParams describes RenderDevice configuration needed to render
+// the scene for one eye.
+class StereoEyeParams
+{
+public:
+ StereoEye Eye;
+ Viewport VP; // Viewport that we are rendering to
+ const DistortionConfig* pDistortion;
+
+ Matrix4f ViewAdjust; // Translation to be applied to view matrix.
+ Matrix4f Projection; // Projection matrix used with this eye.
+ Matrix4f OrthoProjection; // Orthographic projection used with this eye.
+
+ void Init(StereoEye eye, const Viewport &vp, float vofs,
+ const Matrix4f& proj, const Matrix4f& orthoProj,
+ const DistortionConfig* distortion = 0)
+ {
+ Eye = eye;
+ VP = vp;
+ ViewAdjust = Matrix4f::Translation(Vector3f(vofs,0,0));
+ Projection = proj;
+ OrthoProjection = orthoProj;
+ pDistortion = distortion;
+ }
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** StereoConfig
+
+// StereoConfig maintains a scene stereo state and allow switching between different
+// stereo rendering modes. To support rendering, StereoConfig keeps track of HMD
+// variables such as screen size, eye-to-screen distance and distortion, and computes
+// extra data such as FOV and distortion center offsets based on it. Rendering
+// parameters are returned though StereoEyeParams for each eye.
+//
+// Beyond regular 3D projection, this class supports rendering a 2D orthographic
+// surface for UI and text. The 2D surface will be defined as fitting within a 2D
+// field of view (85 degrees by default) and used [-1,1] coordinate system with
+// square pixels. The (0,0) coordinate corresponds to eye center location
+// that is properly adjusted during rendering through SterepRenderParams::Adjust2D.
+// Genreally speaking, text outside [-1,1] coordinate range will not be readable.
+
+class StereoConfig
+{
+public:
+
+ StereoConfig(StereoMode mode = Stereo_LeftRight_Multipass,
+ const Viewport& fullViewport = Viewport(0,0, 1280,800));
+
+
+ // *** Modifiable State Access
+
+ // Sets a stereo rendering mode and updates internal cached
+ // state (matrices, per-eye view) based on it.
+ void SetStereoMode(StereoMode mode) { Mode = mode; DirtyFlag = true; }
+ StereoMode GetStereoMode() const { return Mode; }
+
+ // Sets HMD parameters; also initializes distortion coefficients.
+ void SetHMDInfo(const HMDInfo& hmd);
+ const HMDInfo& GetHMDInfo() const { return HMD; }
+
+ // Query physical eye-to-screen distance in meters, which combines screen-to-lens and
+ // and lens-to-eye pupil distances. Modifying this value adjusts FOV.
+ float GetEyeToScreenDistance() const { return HMD.EyeToScreenDistance; }
+ void SetEyeToScreenDistance(float esd) { HMD.EyeToScreenDistance = esd; DirtyFlag = true; }
+
+ // Interpupillary distance used for stereo, in meters. Default is 0.064m (64 mm).
+ void SetIPD(float ipd) { InterpupillaryDistance = ipd; DirtyFlag = true; }
+ float GetIPD() const { return InterpupillaryDistance; }
+
+ // Set full render target viewport; for HMD this includes both eyes.
+ void SetFullViewport(const Viewport& vp);
+ const Viewport& GetFullViewport() const { return FullView; }
+
+ // Aspect ratio defaults to ((w/h)*multiplier) computed per eye.
+ // Aspect multiplier allows adjusting aspect ratio consistently for Stereo/NoStereo.
+ void SetAspectMultiplier(float m) { AspectMultiplier = m; DirtyFlag = true; }
+ float GetAspectMultiplier() const { return AspectMultiplier; }
+
+
+ // For the distorted image to fill rendered viewport, input texture render target needs to be
+ // scaled by DistortionScale before sampling. The scale factor is computed by fitting a point
+ // on of specified radius from a distortion center, more easily specified as a coordinate.
+ // SetDistortionFitPointVP sets the (x,y) coordinate of the point that scale will be "fit" to,
+ // assuming [-1,1] coordinate range for full left-eye viewport. A fit point is a location
+ // where source (pre-distortion) and target (post-distortion) image match each other.
+ // For the right eye, the interpretation of 'u' will be inverted.
+ void SetDistortionFitPointVP(float x, float y);
+ // SetDistortionFitPointPixels sets the (x,y) coordinate of the point that scale will be "fit" to,
+ // specified in pixeld for full left-eye texture.
+ void SetDistortionFitPointPixels(float x, float y);
+
+ // Changes all distortion settings.
+ // Note that setting HMDInfo also changes Distortion coefficients.
+ void SetDistortionConfig(const DistortionConfig& d) { Distortion = d; DirtyFlag = true; }
+
+ // Modify distortion coefficients; useful for adjustment tweaking.
+ void SetDistortionK(int i, float k) { Distortion.K[i] = k; DirtyFlag = true; }
+ float GetDistortionK(int i) const { return Distortion.K[i]; }
+
+ // Sets the fieldOfView that the 2D coordinate area stretches to.
+ void Set2DAreaFov(float fovRadians);
+
+
+ // *** Computed State
+
+ // Return current aspect ratio.
+ float GetAspect() { updateIfDirty(); return Aspect; }
+
+ // Return computed vertical FOV in radians/degrees.
+ float GetYFOVRadians() { updateIfDirty(); return YFov; }
+ float GetYFOVDegrees() { return RadToDegree(GetYFOVRadians()); }
+
+ // Query horizontal projection center offset as a distance away from the
+ // one-eye [-1,1] unit viewport.
+ // Positive return value should be used for left eye, negative for right eye.
+ float GetProjectionCenterOffset() { updateIfDirty(); return ProjectionCenterOffset; }
+
+ // GetDistortionConfig isn't const because XCenterOffset bay need to be recomputed.
+ const DistortionConfig& GetDistortionConfig() { updateIfDirty(); return Distortion; }
+
+ // Returns DistortionScale factor by which input texture size is increased to make
+ // post-distortion result distortion fit the viewport.
+ float GetDistortionScale() { updateIfDirty(); return Distortion.Scale; }
+
+ // Returns the size of a pixel within 2D coordinate system.
+ float Get2DUnitPixel() { updateIfDirty(); return (2.0f / (FovPixels * Distortion.Scale)); }
+
+ // Returns full set of Stereo rendering parameters for the specified eye.
+ const StereoEyeParams& GetEyeRenderParams(StereoEye eye);
+
+private:
+
+ void updateIfDirty() { if (DirtyFlag) updateComputedState(); }
+ void updateComputedState();
+
+ void updateDistortionOffsetAndScale();
+ void updateProjectionOffset();
+ void update2D();
+ void updateEyeParams();
+
+
+ // *** Modifiable State
+
+ StereoMode Mode;
+ float InterpupillaryDistance;
+ float AspectMultiplier; // Multiplied into aspect ratio to change it.
+ HMDInfo HMD;
+ DistortionConfig Distortion;
+ float DistortionFitX, DistortionFitY; // In [-1,1] half-screen viewport units.
+ Viewport FullView; // Entire window viewport.
+
+ float Area2DFov; // FOV range mapping to [-1, 1] 2D area.
+
+ // *** Computed State
+
+ bool DirtyFlag; // Set when any if the modifiable state changed.
+ float YFov; // Vertical FOV.
+ float Aspect; // Aspect ratio: (w/h)*AspectMultiplier.
+ float ProjectionCenterOffset;
+ StereoEyeParams EyeRenderParams[2];
+
+
+ // ** 2D Rendering
+
+ // Number of 2D pixels in the FOV. This defines [-1,1] coordinate range for 2D.
+ float FovPixels;
+ Matrix4f OrthoCenter;
+ float OrthoPixelOffset;
+};
+
+
+}}} // OVR::Util::Render
+
+#endif
diff --git a/Mac_OculusWorldDemo.app/Contents/Info.plist b/Mac_OculusWorldDemo.app/Contents/Info.plist
new file mode 100644
index 0000000..6ae01b6
--- /dev/null
+++ b/Mac_OculusWorldDemo.app/Contents/Info.plist
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>BuildMachineOSBuild</key>
+ <string>12C3103</string>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
+ <key>CFBundleExecutable</key>
+ <string>OculusWorldDemo</string>
+ <key>CFBundleIconFile</key>
+ <string>Oculus</string>
+ <key>CFBundleIdentifier</key>
+ <string>com.oculusvr.OculusWorldDemo</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>OculusWorldDemo</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1</string>
+ <key>DTCompiler</key>
+ <string></string>
+ <key>DTPlatformBuild</key>
+ <string>4H127</string>
+ <key>DTPlatformVersion</key>
+ <string>GM</string>
+ <key>DTSDKBuild</key>
+ <string>12C37</string>
+ <key>DTSDKName</key>
+ <string>macosx10.8</string>
+ <key>DTXcode</key>
+ <string>0460</string>
+ <key>DTXcodeBuild</key>
+ <string>4H127</string>
+ <key>LSMinimumSystemVersion</key>
+ <string>10.6</string>
+ <key>NSHumanReadableCopyright</key>
+ <string>Copyright © 2013 Oculus VR. All rights reserved.</string>
+ <key>NSMainNibFile</key>
+ <string>MainMenu</string>
+ <key>NSPrincipalClass</key>
+ <string>NSApplication</string>
+</dict>
+</plist>
diff --git a/Mac_OculusWorldDemo.app/Contents/MacOS/OculusWorldDemo b/Mac_OculusWorldDemo.app/Contents/MacOS/OculusWorldDemo
new file mode 100644
index 0000000..b4c9208
--- /dev/null
+++ b/Mac_OculusWorldDemo.app/Contents/MacOS/OculusWorldDemo
Binary files differ
diff --git a/Mac_OculusWorldDemo.app/Contents/PkgInfo b/Mac_OculusWorldDemo.app/Contents/PkgInfo
new file mode 100644
index 0000000..bd04210
--- /dev/null
+++ b/Mac_OculusWorldDemo.app/Contents/PkgInfo
@@ -0,0 +1 @@
+APPL???? \ No newline at end of file
diff --git a/Samples/CommonSrc/Makefile b/Samples/CommonSrc/Makefile
new file mode 100644
index 0000000..60b84ed
--- /dev/null
+++ b/Samples/CommonSrc/Makefile
@@ -0,0 +1,7 @@
+
+PLATFORM_SRCS := Samples/CommonSrc/Platform/Platform.cpp Samples/CommonSrc/Platform/X11_Platform.cpp
+
+RENDER_SRCS := Samples/CommonSrc/Render/Render_Device.cpp Samples/CommonSrc/Render/Render_Stereo.cpp \
+ Samples/CommonSrc/Render/Render_GL_Device.cpp \
+ Samples/CommonSrc/Render/Render_LoadTextureTGA.cpp
+
diff --git a/Samples/CommonSrc/Oculus.ico b/Samples/CommonSrc/Oculus.ico
new file mode 100644
index 0000000..bb61593
--- /dev/null
+++ b/Samples/CommonSrc/Oculus.ico
Binary files differ
diff --git a/Samples/CommonSrc/Platform/Gamepad.h b/Samples/CommonSrc/Platform/Gamepad.h
new file mode 100644
index 0000000..01eaab4
--- /dev/null
+++ b/Samples/CommonSrc/Platform/Gamepad.h
@@ -0,0 +1,102 @@
+/************************************************************************************
+
+Filename : Gamepad.h
+Content : Cross platform Gamepad interface.
+Created : May 6, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#ifndef OVR_Gamepad_h
+#define OVR_Gamepad_h
+
+#include "OVR.h"
+
+namespace OVR { namespace Platform {
+
+// Buttons on a typical gamepad controller.
+enum GamepadButtons
+{
+ Gamepad_A = 0x1000,
+ Gamepad_CROSS = 0x1000,
+ Gamepad_B = 0x2000,
+ Gamepad_CIRCLE = 0x2000,
+ Gamepad_X = 0x4000,
+ Gamepad_SQUARE = 0x4000,
+ Gamepad_Y = 0x8000,
+ Gamepad_TRIANGLE = 0x8000,
+ Gamepad_Up = 0x0001,
+ Gamepad_Down = 0x0002,
+ Gamepad_Left = 0x0004,
+ Gamepad_Right = 0x0008,
+ Gamepad_Start = 0x0010,
+ Gamepad_Back = 0x0020,
+ Gamepad_LStick = 0x0040,
+ Gamepad_RStick = 0x0080,
+ Gamepad_L1 = 0x0100,
+ Gamepad_R1 = 0x0200,
+};
+
+//-------------------------------------------------------------------------------------
+// ***** GamepadState
+
+// Describes the state of the controller buttons and analog inputs.
+struct GamepadState
+{
+ UInt32 Buttons; // Bitfield representing button state.
+ float LX; // Left stick X axis [-1,1]
+ float LY; // Left stick Y axis [-1,1]
+ float RX; // Right stick X axis [-1,1]
+ float RY; // Right stick Y axis [-1,1]
+ float LT; // Left trigger [0,1]
+ float RT; // Right trigger [0,1]
+
+ GamepadState() : Buttons(0), LX(0), LY(0), RX(0), RY(0), LT(0), RT(0) {}
+
+ bool operator==(const GamepadState& b) const
+ {
+ return Buttons == b.Buttons && LX == b.LX && LY == b.LY && RX == b.RX && RY == b.RY && LT == b.LT && RT == b.RT;
+ }
+ bool operator!=(const GamepadState& b) const
+ {
+ return !(*this == b);
+ }
+ void Debug()
+ {
+ OVR_DEBUG_LOG(("Buttons:0x%4x LX:%.2f LY:%.2f RX:%.2f RY:%.2f LT:%.2f RT:%.2f", Buttons, LX, LY, RX, RY, LT, RT));
+ }
+};
+
+//-------------------------------------------------------------------------------------
+// ***** GamepadManager
+
+// GamepadManager provides a cross platform interface for accessing gamepad controller
+// state.
+class GamepadManager : public RefCountBase<GamepadManager>
+{
+public:
+
+ // Get the number of connected gamepads.
+ virtual UInt32 GetGamepadCount() = 0;
+
+ // Get the state of the gamepad with a given index.
+ virtual bool GetGamepadState(UInt32 index, GamepadState* pState) = 0;
+};
+
+}} // OVR::Platform
+
+#endif // OVR_Gamepad_h
diff --git a/Samples/CommonSrc/Platform/OSX_Gamepad.cpp b/Samples/CommonSrc/Platform/OSX_Gamepad.cpp
new file mode 100644
index 0000000..3ac53ab
--- /dev/null
+++ b/Samples/CommonSrc/Platform/OSX_Gamepad.cpp
@@ -0,0 +1,424 @@
+/************************************************************************************
+
+Filename : OSX_Gamepad.cpp
+Content : OSX implementation of Gamepad functionality.
+Created : May 6, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#include "OSX_Gamepad.h"
+
+
+static const UInt32 Logitech_F710_VendorID = 0x046D;
+static const UInt32 Logitech_F710_ProductID = 0xC219;
+
+static const UInt32 Sony_DualShock3_VendorID = 0x054C;
+static const UInt32 Sony_DualShock3_ProductID = 0x0268;
+
+
+namespace OVR { namespace Platform { namespace OSX {
+
+
+GamepadManager::GamepadManager()
+ : bStateChanged(false)
+{
+
+ HidManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
+ IOHIDManagerOpen(HidManager, kIOHIDOptionsTypeNone);
+ IOHIDManagerScheduleWithRunLoop(HidManager,
+ CFRunLoopGetCurrent(),
+ kCFRunLoopDefaultMode);
+
+
+ // Setup device matching.
+ CFStringRef keys[] = { CFSTR(kIOHIDDeviceUsagePageKey),
+ CFSTR(kIOHIDDeviceUsageKey)};
+
+ int value;
+ CFNumberRef values[2];
+ CFDictionaryRef dictionaries[2];
+
+ // Match joysticks.
+ value = kHIDPage_GenericDesktop;
+ values[0] = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value);
+
+ value = kHIDUsage_GD_Joystick;
+ values[1] = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value);
+
+ dictionaries[0] = CFDictionaryCreate(kCFAllocatorDefault,
+ (const void **) keys,
+ (const void **) values,
+ 2,
+ &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+ CFRelease(values[0]);
+ CFRelease(values[1]);
+
+ // Match gamepads.
+ value = kHIDPage_GenericDesktop;
+ values[0] = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value);
+
+ value = kHIDUsage_GD_GamePad;
+ values[1] = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value);
+
+ dictionaries[1] = CFDictionaryCreate(kCFAllocatorDefault,
+ (const void **) keys,
+ (const void **) values,
+ 2,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ CFRelease(values[0]);
+ CFRelease(values[1]);
+
+ CFArrayRef array = CFArrayCreate( kCFAllocatorDefault,
+ (const void **) dictionaries,
+ 2,
+ &kCFTypeArrayCallBacks);
+ CFRelease(dictionaries[0]);
+ CFRelease(dictionaries[1]);
+
+ IOHIDManagerSetDeviceMatchingMultiple(HidManager, array);
+
+ CFRelease(array);
+
+
+ IOHIDManagerRegisterDeviceMatchingCallback(HidManager, staticOnDeviceMatched, this);
+ IOHIDManagerRegisterDeviceRemovalCallback(HidManager, staticOnDeviceRemoved, this);
+
+}
+
+GamepadManager::~GamepadManager()
+{
+ CFRelease(HidManager);
+}
+
+UInt32 GamepadManager::GetGamepadCount()
+{
+ return 1;
+}
+
+bool GamepadManager::GetGamepadState(UInt32 index, GamepadState* pState)
+{
+ // For now we just support one gamepad.
+ OVR_UNUSED(index);
+
+ if (!bStateChanged)
+ {
+ return false;
+ }
+
+ bStateChanged = false;
+// State.Debug();
+
+ *pState = State;
+ return true;
+}
+
+int GamepadManager::getIntDeviceProperty(IOHIDDeviceRef device, CFStringRef key)
+{
+ CFTypeRef type = IOHIDDeviceGetProperty(device, key);
+ OVR_ASSERT(type != NULL && CFGetTypeID(type) == CFNumberGetTypeID());
+
+ int value;
+ CFNumberGetValue((CFNumberRef) type, kCFNumberSInt32Type, &value);
+
+ return value;
+}
+
+void GamepadManager::staticOnDeviceMatched(void* context, IOReturn result, void* sender, IOHIDDeviceRef device)
+{
+ GamepadManager* pManager = (GamepadManager*) context;
+ pManager->onDeviceMatched(device);
+}
+
+void GamepadManager::onDeviceMatched(IOHIDDeviceRef device)
+{
+ IOHIDDeviceRegisterInputValueCallback(device, staticOnDeviceValueChanged, this);
+}
+
+void GamepadManager::staticOnDeviceRemoved(void* context, IOReturn result, void* sender, IOHIDDeviceRef device)
+{
+ GamepadManager* pManager = (GamepadManager*) context;
+ pManager->onDeviceRemoved(device);
+}
+
+void GamepadManager::onDeviceRemoved(IOHIDDeviceRef device)
+{
+ IOHIDDeviceRegisterInputValueCallback(device, NULL, NULL);
+}
+
+void GamepadManager::staticOnDeviceValueChanged(void* context, IOReturn result, void* sender, IOHIDValueRef value)
+{
+ GamepadManager* pManager = (GamepadManager*) context;
+ pManager->onDeviceValueChanged(value);
+}
+
+float GamepadManager::mapAnalogAxis(IOHIDValueRef value, IOHIDElementRef element)
+{
+
+ CFIndex val = IOHIDValueGetIntegerValue(value);
+ CFIndex min = IOHIDElementGetLogicalMin(element);
+ CFIndex max = IOHIDElementGetLogicalMax(element);
+
+ float v = (float) (val - min) / (float) (max - min);
+ v = v * 2.0f - 1.0f;
+
+ // Dead zone.
+ if (v < 0.1f && v > -0.1f)
+ {
+ v = 0.0f;
+ }
+
+ return v;
+}
+
+bool GamepadManager::setStateIfDifferent(float& state, float newState)
+{
+ if (state == newState)
+ return false;
+
+ state = newState;
+
+ return true;
+}
+
+void GamepadManager::onDeviceValueChanged(IOHIDValueRef value)
+{
+
+ IOHIDElementRef element = IOHIDValueGetElement(value);
+ IOHIDDeviceRef device = IOHIDElementGetDevice(element);
+
+ int vendorID = getIntDeviceProperty(device, CFSTR(kIOHIDVendorIDKey));
+ int productID = getIntDeviceProperty(device, CFSTR(kIOHIDProductIDKey));
+ OVR_UNUSED(productID);
+
+ uint32_t usagePage = IOHIDElementGetUsagePage(element);
+ uint32_t usage = IOHIDElementGetUsage(element);
+
+ // The following controller mapping is based on the Logitech F710, however we use it for
+ // all Logitech devices on the assumption that they're likely to share the same mapping.
+ if (vendorID == Logitech_F710_VendorID)
+ {
+ // Logitech F710 mapping.
+ if (usagePage == kHIDPage_Button)
+ {
+ bool buttonState = IOHIDValueGetIntegerValue(value);
+
+ switch(usage)
+ {
+ case kHIDUsage_Button_1:
+ manipulateBitField(State.Buttons, Gamepad_X, buttonState);
+ break;
+ case kHIDUsage_Button_2:
+ manipulateBitField(State.Buttons, Gamepad_A, buttonState);
+ break;
+ case kHIDUsage_Button_3:
+ manipulateBitField(State.Buttons, Gamepad_B, buttonState);
+ break;
+ case kHIDUsage_Button_4:
+ manipulateBitField(State.Buttons, Gamepad_Y, buttonState);
+ break;
+ case 0x05:
+ manipulateBitField(State.Buttons, Gamepad_L1, buttonState);
+ break;
+ case 0x06:
+ manipulateBitField(State.Buttons, Gamepad_R1, buttonState);
+ break;
+ case 0x07:
+ State.LT = buttonState ? 1.0f:0.0f;
+ break;
+ case 0x08:
+ State.RT = buttonState ? 1.0f:0.0f;
+ break;
+ case 0x09:
+ manipulateBitField(State.Buttons, Gamepad_Back, buttonState);
+ break;
+ case 0x0A:
+ manipulateBitField(State.Buttons, Gamepad_Start, buttonState);
+ break;
+ case 0x0B:
+ manipulateBitField(State.Buttons, Gamepad_LStick, buttonState);
+ break;
+ case 0x0C:
+ manipulateBitField(State.Buttons, Gamepad_RStick, buttonState);
+ break;
+ default:
+ return;
+ }
+ }
+ else if (usagePage == kHIDPage_GenericDesktop)
+ {
+ float v;
+ switch(usage)
+ {
+ case kHIDUsage_GD_X:
+ v = mapAnalogAxis(value, element);
+ if (!setStateIfDifferent(State.LX, v))
+ return;
+ break;
+ case kHIDUsage_GD_Y:
+ v = mapAnalogAxis(value, element);
+ if (!setStateIfDifferent(State.LY, -v))
+ return;
+ break;
+ case kHIDUsage_GD_Z:
+ v = mapAnalogAxis(value, element);
+ if (!setStateIfDifferent(State.RX, v))
+ return;
+ break;
+ case kHIDUsage_GD_Rz:
+ v = mapAnalogAxis(value, element);
+ if (!setStateIfDifferent(State.RY, -v))
+ return;
+ break;
+ case kHIDUsage_GD_Hatswitch:
+ {
+ CFIndex integerValue = IOHIDValueGetIntegerValue(value);
+
+ manipulateBitField(State.Buttons,
+ Gamepad_Up,
+ integerValue == 7 || integerValue == 0 || integerValue == 1);
+ manipulateBitField(State.Buttons,
+ Gamepad_Down,
+ integerValue == 3 || integerValue == 4 || integerValue == 5);
+ manipulateBitField(State.Buttons,
+ Gamepad_Left,
+ integerValue == 5 || integerValue == 6 || integerValue == 7);
+ manipulateBitField(State.Buttons,
+ Gamepad_Right,
+ integerValue == 1 || integerValue == 2 || integerValue == 3);
+ }
+ break;
+ default:
+ return;
+ }
+ }
+ }
+ // The following controller mapping is based on the Sony DualShock3, however we use it for
+ // all Sony devices on the assumption that they're likely to share the same mapping.
+ else if (vendorID == Sony_DualShock3_VendorID)
+ {
+ // PS3 Controller.
+ if (usagePage == kHIDPage_Button)
+ {
+ bool buttonState = IOHIDValueGetIntegerValue(value);
+
+ switch(usage)
+ {
+ case kHIDUsage_Button_1:
+ manipulateBitField(State.Buttons, Gamepad_Back, buttonState);
+ break;
+ case kHIDUsage_Button_2:
+ manipulateBitField(State.Buttons, Gamepad_LStick, buttonState);
+ break;
+ case kHIDUsage_Button_3:
+ manipulateBitField(State.Buttons, Gamepad_RStick, buttonState);
+ break;
+ case kHIDUsage_Button_4:
+ manipulateBitField(State.Buttons, Gamepad_Start, buttonState);
+ break;
+ case 0x05:
+ manipulateBitField(State.Buttons, Gamepad_Up, buttonState);
+ break;
+ case 0x06:
+ manipulateBitField(State.Buttons, Gamepad_Right, buttonState);
+ break;
+ case 0x07:
+ manipulateBitField(State.Buttons, Gamepad_Down, buttonState);
+ break;
+ case 0x08:
+ manipulateBitField(State.Buttons, Gamepad_Left, buttonState);
+ break;
+ case 0x09:
+ State.LT = buttonState ? 1.0f:0.0f;
+ break;
+ case 0x0A:
+ State.RT = buttonState ? 1.0f:0.0f;
+ break;
+ case 0x0B:
+ manipulateBitField(State.Buttons, Gamepad_L1, buttonState);
+ break;
+ case 0x0C:
+ manipulateBitField(State.Buttons, Gamepad_R1, buttonState);
+ break;
+ case 0x0D:
+ // PS3 Triangle.
+ manipulateBitField(State.Buttons, Gamepad_TRIANGLE, buttonState);
+ break;
+ case 0x0E:
+ // PS3 Circle
+ manipulateBitField(State.Buttons, Gamepad_CIRCLE, buttonState);
+ break;
+ case 0x0F:
+ // PS3 Cross
+ manipulateBitField(State.Buttons, Gamepad_CROSS, buttonState);
+ break;
+ case 0x10:
+ // PS3 Square
+ manipulateBitField(State.Buttons, Gamepad_SQUARE, buttonState);
+ break;
+ default:
+ return;
+ }
+ }
+ else if (usagePage == kHIDPage_GenericDesktop)
+ {
+ float v;
+ switch(usage)
+ {
+ case kHIDUsage_GD_X:
+ v = mapAnalogAxis(value, element);
+ if (!setStateIfDifferent(State.LX, v))
+ return;
+ break;
+ case kHIDUsage_GD_Y:
+ v = mapAnalogAxis(value, element);
+ if (!setStateIfDifferent(State.LY, -v))
+ return;
+ break;
+ case kHIDUsage_GD_Z:
+ v = mapAnalogAxis(value, element);
+ if (!setStateIfDifferent(State.RX, v))
+ return;
+ break;
+ case kHIDUsage_GD_Rz:
+ v = mapAnalogAxis(value, element);
+ if (!setStateIfDifferent(State.RY, -v))
+ return;
+ break;
+ default:
+ return;
+ }
+ }
+ }
+
+ bStateChanged = true;
+}
+
+void GamepadManager::manipulateBitField(unsigned int& bitfield, unsigned int mask, bool val)
+{
+ if (val)
+ {
+ bitfield |= mask;
+ }
+ else
+ {
+ bitfield &= ~mask;
+ }
+}
+
+}}} // OVR::Platform::OSX
diff --git a/Samples/CommonSrc/Platform/OSX_Gamepad.h b/Samples/CommonSrc/Platform/OSX_Gamepad.h
new file mode 100644
index 0000000..6d3344c
--- /dev/null
+++ b/Samples/CommonSrc/Platform/OSX_Gamepad.h
@@ -0,0 +1,67 @@
+/************************************************************************************
+
+Filename : OSX_Gamepad.h
+Content : OSX implementation of Gamepad functionality.
+Created : May 6, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#ifndef OVR_OSX_Gamepad_h
+#define OVR_OSX_Gamepad_h
+
+#include "Gamepad.h"
+
+#include <IOKit/IOKitLib.h>
+#include <IOKit/hid/IOHIDManager.h>
+
+
+namespace OVR { namespace Platform { namespace OSX {
+
+
+class GamepadManager : public Platform::GamepadManager
+{
+public:
+ GamepadManager();
+ ~GamepadManager();
+
+ virtual UInt32 GetGamepadCount();
+ virtual bool GetGamepadState(UInt32 index, GamepadState* pState);
+
+private:
+ static void staticOnDeviceMatched(void* context, IOReturn result, void* sender, IOHIDDeviceRef device);
+ void onDeviceMatched(IOHIDDeviceRef device);
+
+ static void staticOnDeviceRemoved(void* context, IOReturn result, void* sender, IOHIDDeviceRef device);
+ void onDeviceRemoved(IOHIDDeviceRef device);
+
+ static void staticOnDeviceValueChanged(void* context, IOReturn result, void* sender, IOHIDValueRef value);
+ void onDeviceValueChanged(IOHIDValueRef value);
+
+ int getIntDeviceProperty(IOHIDDeviceRef device, CFStringRef key);
+ float mapAnalogAxis(IOHIDValueRef value, IOHIDElementRef element);
+ void manipulateBitField(unsigned int& bitfield, unsigned int mask, bool val);
+ bool setStateIfDifferent(float& state, float newState);
+
+ IOHIDManagerRef HidManager;
+ GamepadState State;
+ bool bStateChanged;
+};
+
+}}}
+
+#endif // OVR_OSX_Gamepad_h
diff --git a/Samples/CommonSrc/Platform/OSX_Platform.h b/Samples/CommonSrc/Platform/OSX_Platform.h
new file mode 100644
index 0000000..748c5fa
--- /dev/null
+++ b/Samples/CommonSrc/Platform/OSX_Platform.h
@@ -0,0 +1,80 @@
+
+#include "../Platform/Platform.h"
+#include "../Render/Render_GL_Device.h"
+
+namespace OVR { namespace Platform { namespace OSX {
+
+class PlatformCore : public Platform::PlatformCore
+{
+public:
+ void* Win;
+ void* View;
+ void* NsApp;
+ bool Quit;
+ int ExitCode;
+ int Width, Height;
+ MouseMode MMode;
+
+ void RunIdle();
+
+public:
+ PlatformCore(Application* app, void* nsapp);
+ ~PlatformCore();
+
+ bool SetupWindow(int w, int h);
+ void Exit(int exitcode);
+
+ RenderDevice* SetupGraphics(const SetupGraphicsDeviceSet& setupGraphicsDesc,
+ const char* gtype, const Render::RendererParams& rp);
+
+ void SetMouseMode(MouseMode mm);
+ void GetWindowSize(int* w, int* h) const;
+
+ void SetWindowTitle(const char*title);
+
+ void ShowWindow(bool show);
+ void DestroyWindow();
+ bool SetFullscreen(const Render::RendererParams& rp, int fullscreen);
+ int GetDisplayCount();
+ Render::DisplayId GetDisplay(int screen);
+
+ String GetContentDirectory() const;
+};
+
+}}
+namespace Render { namespace GL { namespace OSX {
+
+class RenderDevice : public Render::GL::RenderDevice
+{
+public:
+ void* Context;
+
+ RenderDevice(const Render::RendererParams& p, void* context)
+ : GL::RenderDevice(p), Context(context) {}
+
+ virtual void Shutdown();
+ virtual void Present();
+
+ virtual bool SetFullscreen(DisplayMode fullscreen);
+
+ // oswnd = X11::PlatformCore*
+ static Render::RenderDevice* CreateDevice(const RendererParams& rp, void* oswnd);
+};
+
+}}}}
+
+
+// OVR_PLATFORM_APP_ARGS specifies the Application class to use for startup,
+// providing it with startup arguments.
+#define OVR_PLATFORM_APP_ARGS(AppClass, args) \
+OVR::Platform::Application* OVR::Platform::Application::CreateApplication() \
+{ OVR::System::Init(OVR::Log::ConfigureDefaultLog(OVR::LogMask_All)); \
+return new AppClass args; } \
+void OVR::Platform::Application::DestroyApplication(OVR::Platform::Application* app) \
+{ OVR::Platform::PlatformCore* platform = app->pPlatform; \
+delete app; delete platform; OVR::System::Destroy(); };
+
+// OVR_PLATFORM_APP_ARGS specifies the Application startup class with no args.
+#define OVR_PLATFORM_APP(AppClass) OVR_PLATFORM_APP_ARGS(AppClass, ())
+
+
diff --git a/Samples/CommonSrc/Platform/OSX_Platform.mm b/Samples/CommonSrc/Platform/OSX_Platform.mm
new file mode 100644
index 0000000..4c7010f
--- /dev/null
+++ b/Samples/CommonSrc/Platform/OSX_Platform.mm
@@ -0,0 +1,514 @@
+
+#import "../Platform/OSX_PlatformObjc.h"
+
+using namespace OVR;
+using namespace OVR::Platform;
+
+@implementation OVRApp
+
+- (void)dealloc
+{
+ [super dealloc];
+}
+
+- (void)run
+{
+ NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+ _running = YES;
+ OVR::Platform::Application* app;
+ {
+ using namespace OVR;
+ using namespace OVR::Platform;
+
+ // CreateApplication must be the first call since it does OVR::System::Initialize.
+ app = Application::CreateApplication();
+ OSX::PlatformCore* platform = new OSX::PlatformCore(app, self);
+ // The platform attached to an app will be deleted by DestroyApplication.
+ app->SetPlatformCore(platform);
+
+ [self setApp:app];
+ [self setPlatform:platform];
+
+ const char* argv[] = {"OVRApp"};
+ int exitCode = app->OnStartup(1, argv);
+ if (exitCode)
+ {
+ Application::DestroyApplication(app);
+ exit(exitCode);
+ }
+ }
+ [self finishLaunching];
+ [pool drain];
+
+ while ([self isRunning])
+ {
+ pool = [[NSAutoreleasePool alloc] init];
+ NSEvent* event = [self nextEventMatchingMask:NSAnyEventMask untilDate:nil inMode:NSDefaultRunLoopMode dequeue:YES];
+ if (event)
+ {
+ [self sendEvent:event];
+ }
+ _App->OnIdle();
+ [pool drain];
+ }
+ OVR::Platform::Application::DestroyApplication(app);
+}
+
+@end
+
+static int KeyMap[][2] =
+{
+ { NSDeleteFunctionKey, OVR::Key_Delete },
+ { '\t', OVR::Key_Tab },
+ { '\n', OVR::Key_Return },
+ { NSPauseFunctionKey, OVR::Key_Pause },
+ { 27, OVR::Key_Escape },
+ { 127, OVR::Key_Backspace },
+ { ' ', OVR::Key_Space },
+ { NSPageUpFunctionKey, OVR::Key_PageUp },
+ { NSPageDownFunctionKey, OVR::Key_PageDown },
+ { NSNextFunctionKey, OVR::Key_PageDown },
+ { NSEndFunctionKey, OVR::Key_End },
+ { NSHomeFunctionKey, OVR::Key_Home },
+ { NSLeftArrowFunctionKey, OVR::Key_Left },
+ { NSUpArrowFunctionKey, OVR::Key_Up },
+ { NSRightArrowFunctionKey, OVR::Key_Right },
+ { NSDownArrowFunctionKey, OVR::Key_Down },
+ { NSInsertFunctionKey, OVR::Key_Insert },
+ { NSDeleteFunctionKey, OVR::Key_Delete },
+ { NSHelpFunctionKey, OVR::Key_Insert },
+};
+
+
+static KeyCode MapToKeyCode(wchar_t vk)
+{
+ unsigned key = Key_None;
+
+ if ((vk >= 'a') && (vk <= 'z'))
+ {
+ key = vk - 'a' + Key_A;
+ }
+ else if ((vk >= ' ') && (vk <= '~'))
+ {
+ key = vk;
+ }
+ else if ((vk >= '0') && (vk <= '9'))
+ {
+ key = vk - '0' + Key_Num0;
+ }
+ else if ((vk >= NSF1FunctionKey) && (vk <= NSF15FunctionKey))
+ {
+ key = vk - NSF1FunctionKey + Key_F1;
+ }
+ else
+ {
+ for (unsigned i = 0; i< (sizeof(KeyMap) / sizeof(KeyMap[1])); i++)
+ {
+ if (vk == KeyMap[i][0])
+ {
+ key = KeyMap[i][1];
+ break;
+ }
+ }
+ }
+
+ return (KeyCode)key;
+}
+
+static int MapModifiers(unsigned long xmod)
+{
+ int mod = 0;
+ if (xmod & NSShiftKeyMask)
+ mod |= OVR::Platform::Mod_Shift;
+ if (xmod & NSCommandKeyMask)
+ mod |= OVR::Platform::Mod_Control;
+ if (xmod & NSAlternateKeyMask)
+ mod |= OVR::Platform::Mod_Alt;
+ if (xmod & NSControlKeyMask)
+ mod |= OVR::Platform::Mod_Meta;
+ return mod;
+}
+
+@implementation OVRView
+
+-(BOOL) acceptsFirstResponder
+{
+ return YES;
+}
+-(BOOL) acceptsFirstMouse:(NSEvent *)ev
+{
+ return YES;
+}
+
++(CGDirectDisplayID) displayFromScreen:(NSScreen *)s
+{
+ NSNumber* didref = (NSNumber*)[[s deviceDescription] objectForKey:@"NSScreenNumber"];
+ CGDirectDisplayID disp = (CGDirectDisplayID)[didref longValue];
+ return disp;
+}
+
+-(void) warpMouseToCenter
+{
+ NSPoint w;
+ w.x = _Platform->Width/2.0f;
+ w.y = _Platform->Height/2.0f;
+ w = [[self window] convertBaseToScreen:w];
+ CGDirectDisplayID disp = [OVRView displayFromScreen:[[self window] screen]];
+ CGPoint p = {w.x, CGDisplayPixelsHigh(disp)-w.y};
+ CGDisplayMoveCursorToPoint(disp, p);
+}
+
+static bool LookupKey(NSEvent* ev, wchar_t& ch, OVR::KeyCode& key, unsigned& mods)
+{
+ NSString* chars = [ev charactersIgnoringModifiers];
+ if ([chars length] == 0)
+ return false;
+ ch = [chars characterAtIndex:0];
+ mods = MapModifiers([ev modifierFlags]);
+
+ // check for Cmd+Latin Letter
+ NSString* modchars = [ev characters];
+ if ([modchars length])
+ {
+ wchar_t modch = [modchars characterAtIndex:0];
+ if (modch >= 'a' && modch <= 'z')
+ ch = modch;
+ }
+ key = MapToKeyCode(ch);
+ return true;
+}
+
+-(void) keyDown:(NSEvent*)ev
+{
+ OVR::KeyCode key;
+ unsigned mods;
+ wchar_t ch;
+ if (!LookupKey(ev, ch, key, mods))
+ return;
+ if (key == Key_Escape && _Platform->MMode == Mouse_Relative)
+ {
+ [self warpMouseToCenter];
+ CGAssociateMouseAndMouseCursorPosition(true);
+ [NSCursor unhide];
+ _Platform->MMode = Mouse_RelativeEscaped;
+ }
+ _App->OnKey(key, ch, true, mods);
+}
+-(void) keyUp:(NSEvent*)ev
+{
+ OVR::KeyCode key;
+ unsigned mods;
+ wchar_t ch;
+ if (LookupKey(ev, ch, key, mods))
+ _App->OnKey(key, ch, false, mods);
+}
+
+static const OVR::KeyCode ModifierKeys[] = {OVR::Key_None, OVR::Key_Shift, OVR::Key_Control, OVR::Key_Alt, OVR::Key_Meta};
+
+-(void)flagsChanged:(NSEvent *)ev
+{
+ unsigned long cmods = [ev modifierFlags];
+ if ((cmods & 0xffff0000) != _Modifiers)
+ {
+ uint32_t mods = MapModifiers(cmods);
+ for (int i = 1; i <= 4; i++)
+ {
+ unsigned long m = (1 << (16+i));
+ if ((cmods & m) != (_Modifiers & m))
+ {
+ if (cmods & m)
+ _App->OnKey(ModifierKeys[i], 0, true, mods);
+ else
+ _App->OnKey(ModifierKeys[i], 0, false, mods);
+ }
+ }
+ _Modifiers = cmods & 0xffff0000;
+ }
+}
+
+-(void)ProcessMouse:(NSEvent*)ev
+{
+ switch ([ev type])
+ {
+ case NSLeftMouseDragged:
+ case NSRightMouseDragged:
+ case NSOtherMouseDragged:
+ case NSMouseMoved:
+ {
+ if (_Platform->MMode == OVR::Platform::Mouse_Relative)
+ {
+ int dx = [ev deltaX];
+ int dy = [ev deltaY];
+
+ if (dx != 0 || dy != 0)
+ {
+ _App->OnMouseMove(dx, dy, Mod_MouseRelative|MapModifiers([ev modifierFlags]));
+ [self warpMouseToCenter];
+ }
+ }
+ else
+ {
+ NSPoint p = [ev locationInWindow];
+ _App->OnMouseMove(p.x, p.y, MapModifiers([ev modifierFlags]));
+ }
+ }
+ break;
+ case NSLeftMouseDown:
+ case NSRightMouseDown:
+ case NSOtherMouseDown:
+ break;
+ }
+}
+
+-(void) mouseMoved:(NSEvent*)ev
+{
+ [self ProcessMouse:ev];
+}
+-(void) mouseDragged:(NSEvent*)ev
+{
+ [self ProcessMouse:ev];
+}
+-(void) mouseDown:(NSEvent*)ev
+{
+ if (_Platform->MMode == Mouse_RelativeEscaped)
+ {
+ [self warpMouseToCenter];
+ CGAssociateMouseAndMouseCursorPosition(false);
+ [NSCursor hide];
+ _Platform->MMode = Mouse_Relative;
+ }
+}
+
+//-(void)
+
+-(id) initWithFrame:(NSRect)frameRect
+{
+ NSOpenGLPixelFormatAttribute attr[] =
+ {NSOpenGLPFAWindow, NSOpenGLPFADoubleBuffer, NSOpenGLPFADepthSize, 24, nil};
+
+ NSOpenGLPixelFormat *pf = [[[NSOpenGLPixelFormat alloc] initWithAttributes:attr] autorelease];
+
+ self = [super initWithFrame:frameRect pixelFormat:pf];
+ GLint swap = 0;
+ [[self openGLContext] setValues:&swap forParameter:NSOpenGLCPSwapInterval];
+ //[self setWantsBestResolutionOpenGLSurface:YES];
+ return self;
+}
+
+-(void) reshape
+{
+ NSRect bounds = [self bounds];
+ _App->OnResize(bounds.size.width, bounds.size.height);
+
+ _Platform->Width = bounds.size.width;
+ _Platform->Height = bounds.size.height;
+
+ if (_Platform->GetRenderer())
+ _Platform->GetRenderer()->SetWindowSize(bounds.size.width, bounds.size.height);
+}
+
+-(BOOL)windowShouldClose:(id)sender
+{
+ if (_Platform)
+ _Platform->Exit(0);
+ else
+ exit(0);
+ return 1;
+}
+
+@end
+
+namespace OVR { namespace Platform { namespace OSX {
+
+PlatformCore::PlatformCore(Application* app, void* nsapp)
+ : Platform::PlatformCore(app), NsApp(nsapp), Win(NULL), View(NULL), Quit(0), MMode(Mouse_Normal)
+{
+ pGamepadManager = *new OSX::GamepadManager();
+}
+PlatformCore::~PlatformCore()
+{
+}
+
+void PlatformCore::Exit(int exitcode)
+{
+ OVRApp* nsApp = (OVRApp*)NsApp;
+ [nsApp stop:nil];
+}
+
+String PlatformCore::GetContentDirectory() const
+{
+ NSBundle* bundle = [NSBundle mainBundle];
+ if (bundle)
+ return String([[bundle bundlePath] UTF8String]) + "/Contents/Resources";
+ else
+ return ".";
+}
+
+
+void PlatformCore::SetMouseMode(MouseMode mm)
+{
+ if (mm == MMode)
+ return;
+
+ if (Win)
+ {
+ if (mm == Mouse_Relative)
+ {
+ [NSCursor hide];
+ [(OVRView*)View warpMouseToCenter];
+ CGAssociateMouseAndMouseCursorPosition(false);
+ }
+ else
+ {
+ if (MMode == Mouse_Relative)
+ {
+ CGAssociateMouseAndMouseCursorPosition(true);
+ [NSCursor unhide];
+ [(OVRView*)View warpMouseToCenter];
+ }
+ }
+ }
+ MMode = mm;
+}
+
+
+void PlatformCore::GetWindowSize(int* w, int* h) const
+{
+ *w = Width;
+ *h = Height;
+}
+
+bool PlatformCore::SetupWindow(int w, int h)
+{
+ NSRect winrect;
+ winrect.origin.x = 0;
+ winrect.origin.y = 1000;
+ winrect.size.width = w;
+ winrect.size.height = h;
+ NSWindow* win = [[NSWindow alloc] initWithContentRect:winrect styleMask:NSTitledWindowMask|NSClosableWindowMask backing:NSBackingStoreBuffered defer:NO];
+
+ OVRView* view = [[OVRView alloc] initWithFrame:winrect];
+ [view setPlatform:this];
+ [win setContentView:view];
+ [win setAcceptsMouseMovedEvents:YES];
+ [win setDelegate:view];
+ [view setApp:pApp];
+ Win = win;
+ View = view;
+ return 1;
+}
+
+void PlatformCore::SetWindowTitle(const char* title)
+{
+ [((NSWindow*)Win) setTitle:[[NSString alloc] initWithBytes:title length:strlen(title) encoding:NSUTF8StringEncoding]];
+}
+
+void PlatformCore::ShowWindow(bool show)
+{
+ if (show)
+ [((NSWindow*)Win) makeKeyAndOrderFront:nil];
+ else
+ [((NSWindow*)Win) orderOut:nil];
+}
+
+void PlatformCore::DestroyWindow()
+{
+ [((NSWindow*)Win) close];
+ Win = NULL;
+}
+
+RenderDevice* PlatformCore::SetupGraphics(const SetupGraphicsDeviceSet& setupGraphicsDesc,
+ const char* type, const Render::RendererParams& rp)
+{
+ const SetupGraphicsDeviceSet* setupDesc = setupGraphicsDesc.PickSetupDevice(type);
+ OVR_ASSERT(setupDesc);
+
+ pRender = *setupDesc->pCreateDevice(rp, this);
+ if (pRender)
+ pRender->SetWindowSize(Width, Height);
+
+ return pRender.GetPtr();
+}
+
+int PlatformCore::GetDisplayCount()
+{
+ return (int)[[NSScreen screens] count];
+}
+
+Render::DisplayId PlatformCore::GetDisplay(int i)
+{
+ NSScreen* s = (NSScreen*)[[NSScreen screens] objectAtIndex:i];
+ return Render::DisplayId([OVRView displayFromScreen:s]);
+}
+
+bool PlatformCore::SetFullscreen(const Render::RendererParams& rp, int fullscreen)
+{
+ if (fullscreen == Render::Display_Window)
+ [(OVRView*)View exitFullScreenModeWithOptions:nil];
+ else
+ {
+ NSScreen* usescreen = [NSScreen mainScreen];
+ NSArray* screens = [NSScreen screens];
+ for (int i = 0; i < [screens count]; i++)
+ {
+ NSScreen* s = (NSScreen*)[screens objectAtIndex:i];
+ CGDirectDisplayID disp = [OVRView displayFromScreen:s];
+
+ if (disp == rp.Display.CgDisplayId)
+ usescreen = s;
+ }
+
+ [(OVRView*)View enterFullScreenMode:usescreen withOptions:nil];
+ }
+
+ if (pRender)
+ pRender->SetFullscreen((Render::DisplayMode)fullscreen);
+ return 1;
+}
+
+}}
+// GL
+namespace Render { namespace GL { namespace OSX {
+
+Render::RenderDevice* RenderDevice::CreateDevice(const RendererParams& rp, void* oswnd)
+{
+ Platform::OSX::PlatformCore* PC = (Platform::OSX::PlatformCore*)oswnd;
+
+ OVRView* view = (OVRView*)PC->View;
+ NSOpenGLContext *context = [view openGLContext];
+ if (!context)
+ return NULL;
+
+ [context makeCurrentContext];
+ [((NSWindow*)PC->Win) makeKeyAndOrderFront:nil];
+
+ return new Render::GL::OSX::RenderDevice(rp, context);
+}
+
+void RenderDevice::Present()
+{
+ NSOpenGLContext *context = (NSOpenGLContext*)Context;
+ [context flushBuffer];
+}
+
+void RenderDevice::Shutdown()
+{
+ Context = NULL;
+}
+
+bool RenderDevice::SetFullscreen(DisplayMode fullscreen)
+{
+ Params.Fullscreen = fullscreen;
+ return 1;
+}
+
+}}}}
+
+
+int main(int argc, char *argv[])
+{
+ NSApplication* nsapp = [OVRApp sharedApplication];
+ [nsapp run];
+ return 0;
+}
+
diff --git a/Samples/CommonSrc/Platform/OSX_PlatformObjc.h b/Samples/CommonSrc/Platform/OSX_PlatformObjc.h
new file mode 100644
index 0000000..7e69a4d
--- /dev/null
+++ b/Samples/CommonSrc/Platform/OSX_PlatformObjc.h
@@ -0,0 +1,31 @@
+
+#import <Cocoa/Cocoa.h>
+#import "OSX_Platform.h"
+#import "OSX_Gamepad.h"
+
+#import <CoreGraphics/CoreGraphics.h>
+#import <CoreGraphics/CGDirectDisplay.h>
+
+@interface OVRApp : NSApplication
+
+@property (assign) IBOutlet NSWindow* win;
+@property (assign) OVR::Platform::OSX::PlatformCore* Platform;
+@property (assign) OVR::Platform::Application* App;
+
+-(void) run;
+
+@end
+
+@interface OVRView : NSOpenGLView <NSWindowDelegate>
+
+@property (assign) OVR::Platform::OSX::PlatformCore* Platform;
+@property (assign) OVR::Platform::Application* App;
+@property unsigned long Modifiers;
+
+-(void)ProcessMouse:(NSEvent*)event;
+-(void)warpMouseToCenter;
+
++(CGDirectDisplayID) displayFromScreen:(NSScreen*)s;
+
+@end
+
diff --git a/Samples/CommonSrc/Platform/OSX_WavPlayer.cpp b/Samples/CommonSrc/Platform/OSX_WavPlayer.cpp
new file mode 100644
index 0000000..6119888
--- /dev/null
+++ b/Samples/CommonSrc/Platform/OSX_WavPlayer.cpp
@@ -0,0 +1,242 @@
+/************************************************************************************
+
+Filename : WavPlayer_OSX.cpp
+Content : An Apple OSX audio handler.
+Created : March 5, 2013
+Authors : Robotic Arm Software - Peter Hoff, Dan Goodman, Bryan Croteau
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus LLC license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#include "OSX_WavPlayer.h"
+
+namespace OVR { namespace Platform { namespace OSX {
+
+WavPlayer::WavPlayer(const char* fileName)
+{
+ FileName = fileName;
+}
+
+bool WavPlayer::isDataChunk(unsigned char* buffer, int index)
+{
+ unsigned char a = buffer[index];
+ unsigned char b = buffer[index + 1];
+ unsigned char c = buffer[index + 2];
+ unsigned char d = buffer[index + 3];
+ return (a == 'D' || a == 'd') && (b == 'A' || b == 'a') &&
+ (c == 'T' || c == 't') && (d == 'A' || d == 'a');
+}
+
+int WavPlayer::getWord(unsigned char* buffer, int index)
+{
+ unsigned char a = buffer[index];
+ unsigned char b = buffer[index + 1];
+ unsigned char c = buffer[index + 2];
+ unsigned char d = buffer[index + 3];
+ int result = 0;
+ result |= a;
+ result |= b << 8;
+ result |= c << 16;
+ result |= d << 24;
+ return result;
+}
+
+short WavPlayer::getHalf(unsigned char* buffer, int index)
+{
+ unsigned char a = buffer[index];
+ unsigned char b = buffer[index + 1];
+ short result = 0;
+ result |= a;
+ result |= b << 8;
+ return result;
+}
+
+void *WavPlayer::LoadPCM(const char *filename, unsigned long *len)
+{
+ FILE *file;
+ struct stat s;
+ void *pcm;
+
+ if(stat(filename, &s))
+ {
+ return NULL;
+ }
+ *len = s.st_size;
+ pcm = (void *) malloc(s.st_size);
+ if(!pcm)
+ {
+ return NULL;
+ }
+ file = fopen(filename, "rb");
+ if(!file)
+ {
+ free(pcm);
+ return NULL;
+ }
+ fread(pcm, s.st_size, 1, file);
+ fclose(file);
+ return pcm;
+}
+
+int WavPlayer::PlayBuffer(void *pcmbuffer, unsigned long len)
+{
+ AQCallbackStruct aqc;
+ UInt32 err, bufferSize;
+ int i;
+
+ aqc.DataFormat.mSampleRate = SampleRate;
+ aqc.DataFormat.mFormatID = kAudioFormatLinearPCM;
+ if(BitsPerSample == 16)
+ {
+ aqc.DataFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger
+ | kAudioFormatFlagIsPacked;
+ }
+ aqc.DataFormat.mBytesPerPacket = NumChannels * (BitsPerSample / 8);
+ aqc.DataFormat.mFramesPerPacket = 1;
+ aqc.DataFormat.mBytesPerFrame = NumChannels * (BitsPerSample / 8);
+ aqc.DataFormat.mChannelsPerFrame = NumChannels;
+ aqc.DataFormat.mBitsPerChannel = BitsPerSample;
+ aqc.FrameCount = SampleRate / 60;
+ aqc.SampleLen = (UInt32)(len);
+ aqc.PlayPtr = 0;
+ aqc.PCMBuffer = static_cast<unsigned char*>(pcmbuffer);
+
+ err = AudioQueueNewOutput(&aqc.DataFormat,
+ aqBufferCallback,
+ &aqc,
+ NULL,
+ kCFRunLoopCommonModes,
+ 0,
+ &aqc.Queue);
+ if(err)
+ {
+ return err;
+ }
+
+ aqc.FrameCount = SampleRate / 60;
+ bufferSize = aqc.FrameCount * aqc.DataFormat.mBytesPerPacket;
+
+ for(i = 0; i < AUDIO_BUFFERS; i++)
+ {
+ err = AudioQueueAllocateBuffer(aqc.Queue, bufferSize,
+ &aqc.Buffers[i]);
+ if(err)
+ {
+ return err;
+ }
+ aqBufferCallback(&aqc, aqc.Queue, aqc.Buffers[i]);
+ }
+
+ err = AudioQueueStart(aqc.Queue, NULL);
+ if(err)
+ {
+ return err;
+ }
+
+ while(true)
+ {
+ }
+ sleep(1);
+ return 0;
+}
+
+void WavPlayer::aqBufferCallback(void *in, AudioQueueRef inQ, AudioQueueBufferRef outQB)
+{
+ AQCallbackStruct *aqc;
+ unsigned char *coreAudioBuffer;
+
+ aqc = (AQCallbackStruct *) in;
+ coreAudioBuffer = (unsigned char*) outQB->mAudioData;
+
+ printf("Sync: %u / %u\n", aqc->PlayPtr, aqc->SampleLen);
+
+ if(aqc->FrameCount > 0)
+ {
+ outQB->mAudioDataByteSize = aqc->DataFormat.mBytesPerFrame * aqc->FrameCount;
+ for(int i = 0; i < aqc->FrameCount * aqc->DataFormat.mBytesPerFrame; i++)
+ {
+ if(aqc->PlayPtr > aqc->SampleLen)
+ {
+ aqc->PlayPtr = 0;
+ i = 0;
+ }
+ coreAudioBuffer[i] = aqc->PCMBuffer[aqc->PlayPtr];
+ aqc->PlayPtr++;
+ }
+ AudioQueueEnqueueBuffer(inQ, outQB, 0, NULL);
+ }
+}
+
+int WavPlayer::PlayAudio()
+{
+ unsigned long len;
+ void *pcmbuffer;
+ int ret;
+
+ pcmbuffer = LoadPCM(FileName, &len);
+ if(!pcmbuffer)
+ {
+ fprintf(stderr, "%s: %s\n", FileName, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ unsigned char* bytes = (unsigned char*)pcmbuffer;
+ int index = 0;
+
+ // 'RIFF'
+ getWord(bytes, index);
+ index += 4;
+ // int Length
+ getWord(bytes, index);
+ index += 4;
+ // 'WAVE'
+ getWord(bytes, index);
+ index += 4;
+ // 'fmt '
+ getWord(bytes, index);
+ index += 4;
+
+ // int Format Length
+ int fmtLen = getWord(bytes, index);
+ index += 4;
+ AudioFormat = getHalf(bytes, index);
+ index += 2;
+ NumChannels = getHalf(bytes, index);
+ index += 2;
+ SampleRate = getWord(bytes, index);
+ index += 4;
+ ByteRate = getWord(bytes, index);
+ index += 4;
+ BlockAlign = getHalf(bytes, index);
+ index += 2;
+ BitsPerSample = getHalf(bytes, index);
+ index += 2;
+ index += fmtLen - 16;
+ while(!isDataChunk(bytes, index))
+ {
+ // Any Chunk
+ getWord(bytes, index);
+ index += 4;
+ // Any Chunk Length
+ int anyChunkLen = getWord(bytes, index);
+ index += 4 + anyChunkLen;
+ }
+ // 'data'
+ getWord(bytes, index);
+ index += 4;
+ // int Data Length
+ unsigned long dataLen = getWord(bytes, index);
+ index += 4;
+ unsigned char* target = &bytes[index];
+
+ ret = PlayBuffer((void *)target, dataLen);
+ free(pcmbuffer);
+ return ret;
+}
+
+}}}
diff --git a/Samples/CommonSrc/Platform/OSX_WavPlayer.h b/Samples/CommonSrc/Platform/OSX_WavPlayer.h
new file mode 100644
index 0000000..1b88b8c
--- /dev/null
+++ b/Samples/CommonSrc/Platform/OSX_WavPlayer.h
@@ -0,0 +1,64 @@
+/************************************************************************************
+
+Filename : WavPlayer_OSX.h
+Content : An Apple OSX audio handler.
+Created : March 5, 2013
+Authors : Robotic Arm Software - Peter Hoff, Dan Goodman, Bryan Croteau
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus LLC license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#ifndef OVR_WavPlayer_h
+#define OVR_WavPlayer_h
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <AudioToolbox/AudioQueue.h>
+
+#define AUDIO_BUFFERS 4
+
+namespace OVR { namespace Platform { namespace OSX {
+
+typedef struct AQCallbackStruct
+{
+ AudioQueueRef Queue;
+ UInt32 FrameCount;
+ AudioQueueBufferRef Buffers[AUDIO_BUFFERS];
+ AudioStreamBasicDescription DataFormat;
+ UInt32 PlayPtr;
+ UInt32 SampleLen;
+ unsigned char* PCMBuffer;
+} AQCallbackStruct;
+
+class WavPlayer
+{
+public:
+ WavPlayer(const char* fileName);
+ int PlayAudio();
+private:
+ bool isDataChunk(unsigned char* buffer, int index);
+ int getWord(unsigned char* buffer, int index);
+ short getHalf(unsigned char* buffer, int index);
+ void *LoadPCM(const char *filename, unsigned long *len);
+ int PlayBuffer(void *pcm, unsigned long len);
+ static void aqBufferCallback(void *in, AudioQueueRef inQ, AudioQueueBufferRef outQB);
+
+ short AudioFormat;
+ short NumChannels;
+ int SampleRate;
+ int ByteRate;
+ short BlockAlign;
+ short BitsPerSample;
+ const char* FileName;
+};
+
+}}}
+
+#endif
diff --git a/Samples/CommonSrc/Platform/Platform.cpp b/Samples/CommonSrc/Platform/Platform.cpp
new file mode 100644
index 0000000..bf5b8a5
--- /dev/null
+++ b/Samples/CommonSrc/Platform/Platform.cpp
@@ -0,0 +1,75 @@
+/************************************************************************************
+
+Filename : Platform.cpp
+Content : Platform-independent app framework for Oculus samples
+Created : September 6, 2012
+Authors : Andrew Reisse
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+
+
+#include "Platform.h"
+#include <Kernel/OVR_Std.h>
+#include <Kernel/OVR_Timer.h>
+#include "../Render/Render_Device.h"
+#include "Gamepad.h"
+
+namespace OVR { namespace Platform {
+
+
+const SetupGraphicsDeviceSet* SetupGraphicsDeviceSet::PickSetupDevice(const char* typeArg) const
+{
+ // Search for graphics creation object that matches type arg.
+ if (typeArg)
+ {
+ for (const SetupGraphicsDeviceSet* p = this; p != 0; p = p->pNext)
+ {
+ if (!OVR_stricmp(p->pTypeArg, typeArg))
+ return p;
+ }
+ }
+ return this;
+}
+
+//-------------------------------------------------------------------------------------
+
+PlatformCore::PlatformCore(Application *app)
+{
+ pApp = app;
+ pApp->SetPlatformCore(this);
+ StartupTicks = OVR::Timer::GetTicks();
+}
+
+double PlatformCore::GetAppTime() const
+{
+ return (OVR::Timer::GetTicks() - StartupTicks) * (1.0 / (double)OVR::Timer::MksPerSecond);
+}
+
+bool PlatformCore::SetFullscreen(const Render::RendererParams&, int fullscreen)
+{
+ if (pRender)
+ return pRender->SetFullscreen((Render::DisplayMode)fullscreen);
+ return 0;
+}
+
+Render::DisplayId PlatformCore::GetDisplay(int screen)
+{
+ OVR_UNUSED(screen); return Render::DisplayId();
+}
+
+}}
diff --git a/Samples/CommonSrc/Platform/Platform.h b/Samples/CommonSrc/Platform/Platform.h
new file mode 100644
index 0000000..8ef4593
--- /dev/null
+++ b/Samples/CommonSrc/Platform/Platform.h
@@ -0,0 +1,190 @@
+/************************************************************************************
+
+Filename : Platform.h
+Content : Platform-independent app and rendering framework for Oculus samples
+Created : September 6, 2012
+Authors : Andrew Reisse
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#ifndef OVR_Platform_h
+#define OVR_Platform_h
+
+#include "OVR.h"
+
+#include "Kernel/OVR_KeyCodes.h"
+
+namespace OVR { namespace Render {
+ class RenderDevice;
+ struct DisplayId;
+ struct RendererParams;
+}}
+
+namespace OVR { namespace Platform {
+
+using Render::RenderDevice;
+
+class PlatformCore;
+class Application;
+class GamepadManager;
+
+// MouseMode configures mouse input behavior of the app. Three states are
+// currently supported:
+// Normal - Reports absolute coordinates with cursor shown.
+// Relative - Reports relative delta coordinates with cursor hidden
+// until 'Esc' key is pressed or window loses focus.
+// RelativeEscaped - Relative input is desired, but has been escaped until
+// mouse is clicked in the window, which will return the state
+// to relative. Absolute coordinates are reported.
+
+enum MouseMode
+{
+ Mouse_Normal,
+ Mouse_Relative, // Cursor hidden, mouse grab, OnMouseMove reports relative deltas.
+ Mouse_RelativeEscaped, // Clicking in window will return to Relative state.
+};
+
+
+enum Modifiers
+{
+ Mod_Shift = 0x001,
+ Mod_Control = 0x002,
+ Mod_Meta = 0x004,
+ Mod_Alt = 0x008,
+
+ // Set for input Mouse_Relative mode, indicating that x,y are relative deltas.
+ Mod_MouseRelative = 0x100,
+};
+
+//-------------------------------------------------------------------------------------
+// ***** SetupGraphicsDeviceSet
+
+typedef RenderDevice* (*RenderDeviceCreateFunc)(const Render::RendererParams&, void*);
+
+// SetupGraphicsDeviceSet is a PlatformCore::SetupGraphics initialization helper class,
+// used to build up a list of RenderDevices that can be used for rendering.
+// Specifying a smaller set allows application to avoid linking unused graphics devices.
+struct SetupGraphicsDeviceSet
+{
+ SetupGraphicsDeviceSet(const char* typeArg, RenderDeviceCreateFunc createFunc)
+ : pTypeArg(typeArg), pCreateDevice(createFunc), pNext(0) { }
+ SetupGraphicsDeviceSet(const char* typeArg, RenderDeviceCreateFunc createFunc,
+ const SetupGraphicsDeviceSet& next)
+ : pTypeArg(typeArg), pCreateDevice(createFunc), pNext(&next) { }
+
+ // Selects graphics object based on type string; returns 'this' if not found.
+ const SetupGraphicsDeviceSet* PickSetupDevice(const char* typeArg) const;
+
+ const char* pTypeArg;
+ RenderDeviceCreateFunc pCreateDevice;
+
+private:
+ const SetupGraphicsDeviceSet* pNext;
+};
+
+//-------------------------------------------------------------------------------------
+// ***** PlatformCore
+
+// PlatformCore defines abstract system window/viewport setup functionality and
+// maintains a renderer. This class is separated from Application because it can have
+// derived platform-specific implementations.
+// Specific implementation classes are hidden within platform-specific versions
+// such as Win32::PlatformCore.
+
+class PlatformCore : public NewOverrideBase
+{
+protected:
+ Application* pApp;
+ Ptr<RenderDevice> pRender;
+ Ptr<GamepadManager> pGamepadManager;
+ UInt64 StartupTicks;
+
+public:
+ PlatformCore(Application *app);
+ virtual ~PlatformCore() { }
+ Application* GetApp() { return pApp; }
+ RenderDevice* GetRenderer() const { return pRender; }
+ GamepadManager* GetGamepadManager() const { return pGamepadManager; }
+
+ virtual bool SetupWindow(int w, int h) = 0;
+ // Destroys window and also releases renderer.
+ virtual void DestroyWindow() = 0;
+ virtual void Exit(int exitcode) = 0;
+
+ virtual void ShowWindow(bool visible) = 0;
+
+ virtual bool SetFullscreen(const Render::RendererParams& rp, int fullscreen);
+
+ // Search for a matching graphics renderer based on type argument and initializes it.
+ virtual RenderDevice* SetupGraphics(const SetupGraphicsDeviceSet& setupGraphicsDesc,
+ const char* gtype,
+ const Render::RendererParams& rp) = 0;
+
+ virtual void SetMouseMode(MouseMode mm) { OVR_UNUSED(mm); }
+
+ virtual void GetWindowSize(int* w, int* h) const = 0;
+
+ virtual void SetWindowTitle(const char*title) = 0;
+ virtual void PlayMusicFile(const char *fileName) { OVR_UNUSED(fileName); }
+ virtual int GetDisplayCount() { return 0; }
+ virtual Render::DisplayId GetDisplay(int screen);
+
+ // Get time since start of application in seconds.
+ double GetAppTime() const;
+
+ virtual String GetContentDirectory() const { return "."; }
+};
+
+//-------------------------------------------------------------------------------------
+// PlatformApp is a base application class from which end-user application
+// classes derive.
+
+class Application : public NewOverrideBase
+{
+protected:
+ class PlatformCore* pPlatform;
+
+public:
+ virtual ~Application() { }
+
+ virtual int OnStartup(int argc, const char** argv) = 0;
+ virtual void OnQuitRequest() { pPlatform->Exit(0); }
+
+ virtual void OnIdle() {}
+
+ virtual void OnKey(KeyCode key, int chr, bool down, int modifiers)
+ { OVR_UNUSED4(key, chr, down, modifiers); }
+ virtual void OnMouseMove(int x, int y, int modifiers)
+ { OVR_UNUSED3(x, y, modifiers); }
+
+ virtual void OnResize(int width, int height)
+ { OVR_UNUSED2(width, height); }
+
+ void SetPlatformCore(PlatformCore* p) { pPlatform = p; }
+ PlatformCore* GetPlatformCore() const { return pPlatform; }
+
+
+ // Static functions defined by OVR_PLATFORM_APP and used to initialize and
+ // shut down the application class.
+ static Application* CreateApplication();
+ static void DestroyApplication(Application* app);
+};
+
+
+}}
+
+#endif
diff --git a/Samples/CommonSrc/Platform/Platform_Default.h b/Samples/CommonSrc/Platform/Platform_Default.h
new file mode 100644
index 0000000..d82be10
--- /dev/null
+++ b/Samples/CommonSrc/Platform/Platform_Default.h
@@ -0,0 +1,59 @@
+/************************************************************************************
+
+Filename : Platform_Default.h
+Content : Default Platform class and RenderDevice selection file
+Created : October 4, 2012
+Authors :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+*************************************************************************************/
+
+#ifndef OVR_Platform_Default_h
+#define OVR_Platform_Default_h
+
+// This should select proper header file for the platform/compiler.
+#include <Kernel/OVR_Types.h>
+
+#if defined(OVR_OS_WIN32)
+ #include "Win32_Platform.h"
+
+ #include "../Render/Render_D3D11_Device.h"
+ #undef OVR_D3D_VERSION
+ #include "../Render/Render_D3D10_Device.h"
+// #include "../Render/Render_GL_Win32_Device.h"
+
+// Modify this list or pass a smaller set to select a specific render device,
+// while avoiding linking extra classes.
+ #define OVR_DEFAULT_RENDER_DEVICE_SET \
+ SetupGraphicsDeviceSet("D3D11", &OVR::Render::D3D11::RenderDevice::CreateDevice, \
+ SetupGraphicsDeviceSet("D3D10", &OVR::Render::D3D10::RenderDevice::CreateDevice) )
+
+#elif defined(OVR_OS_MAC) && !defined(OVR_MAC_X11)
+ #include "OSX_Platform.h"
+
+ #define OVR_DEFAULT_RENDER_DEVICE_SET \
+ SetupGraphicsDeviceSet("GL", &OVR::Render::GL::OSX::RenderDevice::CreateDevice)
+
+#else
+
+ #include "X11_Platform.h"
+
+ #define OVR_DEFAULT_RENDER_DEVICE_SET \
+ SetupGraphicsDeviceSet("GL", &OVR::Render::GL::X11::RenderDevice::CreateDevice)
+
+#endif
+
+#endif // OVR_Platform_Default_h
diff --git a/Samples/CommonSrc/Platform/Win32_Gamepad.cpp b/Samples/CommonSrc/Platform/Win32_Gamepad.cpp
new file mode 100644
index 0000000..db6524a
--- /dev/null
+++ b/Samples/CommonSrc/Platform/Win32_Gamepad.cpp
@@ -0,0 +1,101 @@
+/************************************************************************************
+
+Filename : Win32_Gamepad.cpp
+Content : Win32 implementation of Platform app infrastructure
+Created : May 6, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#include "Win32_Gamepad.h"
+
+namespace OVR { namespace Platform { namespace Win32 {
+
+GamepadManager::GamepadManager()
+{
+ hXInputModule = ::LoadLibraryA("Xinput9_1_0.dll");
+ if (hXInputModule)
+ {
+ pXInputGetState = (PFn_XInputGetState)
+ ::GetProcAddress(hXInputModule, "XInputGetState");
+ }
+}
+
+GamepadManager::~GamepadManager()
+{
+ if (hXInputModule)
+ ::FreeLibrary(hXInputModule);
+}
+
+static inline float GamepadStick(short in)
+{
+ float v;
+ if (abs(in) < 9000)
+ return 0;
+ else if (in > 9000)
+ v = (float) in - 9000;
+ else
+ v = (float) in + 9000;
+ return v / (32767 - 9000);
+}
+
+static inline float GamepadTrigger(BYTE in)
+{
+ if (in < 30)
+ return 0;
+ else
+ return float(in-30) / 225;
+}
+
+UInt32 GamepadManager::GetGamepadCount()
+{
+ return 1;
+}
+
+bool GamepadManager::GetGamepadState(UInt32 index, GamepadState* pState)
+{
+ // For now we just support one gamepad.
+ OVR_UNUSED(index);
+
+ if (pXInputGetState)
+ {
+ XINPUT_STATE xis;
+
+ if (pXInputGetState(0, &xis))
+ return false;
+
+ if (xis.dwPacketNumber == LastPadPacketNo)
+ return false;
+
+ // State changed.
+ pState->Buttons = xis.Gamepad.wButtons; // Currently matches Xinput
+ pState->LT = GamepadTrigger(xis.Gamepad.bLeftTrigger);
+ pState->RT = GamepadTrigger(xis.Gamepad.bRightTrigger);
+ pState->LX = GamepadStick(xis.Gamepad.sThumbLX);
+ pState->LY = GamepadStick(xis.Gamepad.sThumbLY);
+ pState->RX = GamepadStick(xis.Gamepad.sThumbRX);
+ pState->RY = GamepadStick(xis.Gamepad.sThumbRY);
+
+ LastPadPacketNo = xis.dwPacketNumber;
+
+ return true;
+ }
+
+ return false;
+}
+
+}}} // OVR::Platform::Win32
diff --git a/Samples/CommonSrc/Platform/Win32_Gamepad.h b/Samples/CommonSrc/Platform/Win32_Gamepad.h
new file mode 100644
index 0000000..9066798
--- /dev/null
+++ b/Samples/CommonSrc/Platform/Win32_Gamepad.h
@@ -0,0 +1,54 @@
+/************************************************************************************
+
+Filename : Win32_Gamepad.h
+Content : Win32 implementation of Gamepad functionality.
+Created : May 6, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#ifndef OVR_Win32_Gamepad_h
+#define OVR_Win32_Gamepad_h
+
+#include "Gamepad.h"
+
+#include <windows.h>
+#include <xinput.h>
+
+namespace OVR { namespace Platform { namespace Win32 {
+
+class GamepadManager : public Platform::GamepadManager
+{
+public:
+ GamepadManager();
+ ~GamepadManager();
+
+ virtual UInt32 GetGamepadCount();
+ virtual bool GetGamepadState(UInt32 index, GamepadState* pState);
+
+private:
+ // Dynamically ink to XInput to simplify projects.
+ HMODULE hXInputModule;
+ typedef DWORD (WINAPI *PFn_XInputGetState)(DWORD dwUserIndex, XINPUT_STATE* pState);
+ PFn_XInputGetState pXInputGetState;
+
+ UInt32 LastPadPacketNo;
+};
+
+}}}
+
+#endif // OVR_Win32_Gamepad_h
diff --git a/Samples/CommonSrc/Platform/Win32_Platform.cpp b/Samples/CommonSrc/Platform/Win32_Platform.cpp
new file mode 100644
index 0000000..cdd1e1f
--- /dev/null
+++ b/Samples/CommonSrc/Platform/Win32_Platform.cpp
@@ -0,0 +1,600 @@
+/************************************************************************************
+
+Filename : Win32_Platform.cpp
+Content : Win32 implementation of Platform app infrastructure
+Created : September 6, 2012
+Authors : Andrew Reisse
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#include "Kernel/OVR_System.h"
+#include "Kernel/OVR_Array.h"
+#include "Kernel/OVR_String.h"
+
+#include "Win32_Platform.h"
+#include "Win32_Gamepad.h"
+#include "../Render/Render_Device.h"
+
+namespace OVR { namespace Platform { namespace Win32 {
+
+
+PlatformCore::PlatformCore(Application* app, HINSTANCE hinst)
+ : Platform::PlatformCore(app), hWnd(NULL), hInstance(hinst), Quit(0), MMode(Mouse_Normal),
+ Cursor(0), Modifiers(0), WindowTitle("App")
+{
+ pGamepadManager = *new Win32::GamepadManager();
+}
+
+PlatformCore::~PlatformCore()
+{
+}
+
+bool PlatformCore::SetupWindow(int w, int h)
+{
+ WNDCLASS wc;
+ memset(&wc, 0, sizeof(wc));
+ wc.lpszClassName = L"OVRAppWindow";
+ wc.style = CS_OWNDC;
+ wc.lpfnWndProc = systemWindowProc;
+ wc.cbWndExtra = sizeof(PlatformCore*);
+
+ RegisterClass(&wc);
+
+ Width = w;
+ Height = h;
+ RECT winSize;
+ winSize.left = winSize.top = 0;
+ winSize.right = Width;
+ winSize.bottom = Height;
+ AdjustWindowRect(&winSize, WS_OVERLAPPEDWINDOW, false);
+ hWnd = CreateWindowA("OVRAppWindow", WindowTitle.ToCStr(), WS_OVERLAPPEDWINDOW,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ // 1950, 10,
+ winSize.right-winSize.left, winSize.bottom-winSize.top,
+ NULL, NULL, hInstance, (LPVOID)this);
+ Modifiers = 0;
+
+ Cursor = LoadCursor(NULL, IDC_CROSS);
+
+ // Initialize Window center in screen coordinates
+ POINT center = { Width / 2, Height / 2 };
+ ::ClientToScreen(hWnd, &center);
+ WindowCenter = center;
+
+ if (MMode == Mouse_Relative)
+ {
+ ::SetCursorPos(WindowCenter.x, WindowCenter.y);
+ ShowCursor(FALSE);
+ }
+ ::SetFocus(hWnd);
+
+ return (hWnd != NULL);
+}
+
+void PlatformCore::DestroyWindow()
+{
+ // Release renderer.
+ pRender.Clear();
+
+ // Release gamepad.
+ pGamepadManager.Clear();
+
+ // Release window resources.
+ ::DestroyWindow(hWnd);
+ UnregisterClass(L"OVRAppWindow", hInstance);
+ hWnd = 0;
+ Width = Height = 0;
+
+ //DestroyCursor(Cursor);
+ Cursor = 0;
+}
+
+void PlatformCore::ShowWindow(bool visible)
+{
+ ::ShowWindow(hWnd, visible ? SW_SHOW : SW_HIDE);
+}
+
+void PlatformCore::SetMouseMode(MouseMode mm)
+{
+ if (mm == MMode)
+ return;
+
+ if (hWnd)
+ {
+ if (mm == Mouse_Relative)
+ {
+ ShowCursor(FALSE);
+ ::SetCursorPos(WindowCenter.x, WindowCenter.y);
+ }
+ else
+ {
+ if (MMode == Mouse_Relative)
+ ShowCursor(TRUE);
+ }
+ }
+ MMode = mm;
+}
+
+void PlatformCore::GetWindowSize(int* w, int* h) const
+{
+ *w = Width;
+ *h = Height;
+}
+
+
+void PlatformCore::SetWindowTitle(const char* title)
+{
+ WindowTitle = title;
+ if (hWnd)
+ ::SetWindowTextA(hWnd, title);
+}
+
+static UByte KeyMap[][2] =
+{
+ { VK_BACK, Key_Backspace },
+ { VK_TAB, Key_Tab },
+ { VK_CLEAR, Key_Clear },
+ { VK_RETURN, Key_Return },
+ { VK_SHIFT, Key_Shift },
+ { VK_CONTROL, Key_Control },
+ { VK_MENU, Key_Alt },
+ { VK_PAUSE, Key_Pause },
+ { VK_CAPITAL, Key_CapsLock },
+ { VK_ESCAPE, Key_Escape },
+ { VK_SPACE, Key_Space },
+ { VK_PRIOR, Key_PageUp },
+ { VK_NEXT, Key_PageDown },
+ { VK_END, Key_End },
+ { VK_HOME, Key_Home },
+ { VK_LEFT, Key_Left },
+ { VK_UP, Key_Up },
+ { VK_RIGHT, Key_Right },
+ { VK_DOWN, Key_Down },
+ { VK_INSERT, Key_Insert },
+ { VK_DELETE, Key_Delete },
+ { VK_HELP, Key_Help },
+
+ { VK_NUMLOCK, Key_NumLock },
+ { VK_SCROLL, Key_ScrollLock },
+
+ { VK_OEM_1, Key_Semicolon },
+ { VK_OEM_PLUS, Key_Equal },
+ { VK_OEM_COMMA, Key_Comma },
+ { VK_OEM_MINUS, Key_Minus },
+ { VK_OEM_PERIOD,Key_Period },
+ { VK_OEM_2, Key_Slash },
+ { VK_OEM_3, Key_Bar },
+ { VK_OEM_4, Key_BracketLeft },
+ { VK_OEM_5, Key_Backslash },
+ { VK_OEM_6, Key_BracketRight },
+ { VK_OEM_7, Key_Quote },
+
+ { VK_OEM_AX, Key_OEM_AX }, // 'AX' key on Japanese AX keyboard.
+ { VK_OEM_102, Key_OEM_102 }, // "<>" or "\|" on RT 102-key keyboard.
+ { VK_ICO_HELP, Key_ICO_HELP },
+ { VK_ICO_00, Key_ICO_00 }
+};
+
+
+KeyCode MapVKToKeyCode(unsigned vk)
+{
+ unsigned key = Key_None;
+
+ if ((vk >= '0') && (vk <= '9'))
+ {
+ key = vk - '0' + Key_Num0;
+ }
+ else if ((vk >= 'A') && (vk <= 'Z'))
+ {
+ key = vk - 'A' + Key_A;
+ }
+ else if ((vk >= VK_NUMPAD0) && (vk <= VK_DIVIDE))
+ {
+ key = vk - VK_NUMPAD0 + Key_KP_0;
+ }
+ else if ((vk >= VK_F1) && (vk <= VK_F15))
+ {
+ key = vk - VK_F1 + Key_F1;
+ }
+ else
+ {
+ for (unsigned i = 0; i< (sizeof(KeyMap) / sizeof(KeyMap[1])); i++)
+ {
+ if (vk == KeyMap[i][0])
+ {
+ key = KeyMap[i][1];
+ break;
+ }
+ }
+ }
+
+ return (KeyCode)key;
+}
+
+
+
+LRESULT CALLBACK PlatformCore::systemWindowProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
+{
+ PlatformCore* self;
+
+ // WM_NCCREATE should be the first message to come it; use it to set class pointer.
+ if (msg == WM_NCCREATE)
+ {
+ self = static_cast<PlatformCore*>(((LPCREATESTRUCT)lp)->lpCreateParams);
+
+ if (self)
+ {
+ SetWindowLongPtr(hwnd, 0, (LONG_PTR)self);
+ self->hWnd = hwnd;
+ }
+ }
+ else
+ {
+ self = (PlatformCore*)(UPInt)GetWindowLongPtr(hwnd, 0);
+ }
+
+ return self ? self->WindowProc(msg, wp, lp) :
+ DefWindowProc(hwnd, msg, wp, lp);
+}
+
+
+LRESULT PlatformCore::WindowProc(UINT msg, WPARAM wp, LPARAM lp)
+{
+ KeyCode keyCode;
+
+ switch (msg)
+ {
+ case WM_PAINT:
+ {
+ PAINTSTRUCT ps;
+ BeginPaint(hWnd, &ps);
+ EndPaint(hWnd, &ps);
+ }
+ return 0;
+
+ case WM_SETCURSOR:
+ ::SetCursor(Cursor);
+ return 0;
+
+ case WM_MOUSEMOVE:
+ if (MMode == Mouse_Relative)
+ {
+ POINT newPos = { LOWORD(lp), HIWORD(lp) };
+ ::ClientToScreen(hWnd, &newPos);
+ if ((newPos.x == WindowCenter.x) && (newPos.y == WindowCenter.y))
+ break;
+ ::SetCursorPos(WindowCenter.x, WindowCenter.y);
+
+ LONG dx = newPos.x - WindowCenter.x;
+ LONG dy = newPos.y - WindowCenter.y;
+
+ pApp->OnMouseMove(dx, dy, Mod_MouseRelative);
+ }
+ else
+ {
+ pApp->OnMouseMove(LOWORD(lp), HIWORD(lp), 0);
+ }
+ break;
+
+ case WM_MOVE:
+ {
+ RECT r;
+ GetClientRect(hWnd, &r);
+ WindowCenter.x = r.right/2;
+ WindowCenter.y = r.bottom/2;
+ ::ClientToScreen(hWnd, &WindowCenter);
+ }
+ break;
+
+ case WM_KEYDOWN:
+ switch (wp)
+ {
+ case VK_CONTROL: Modifiers |= Mod_Control; break;
+ case VK_MENU: Modifiers |= Mod_Alt; break;
+ case VK_SHIFT: Modifiers |= Mod_Shift; break;
+ case VK_LWIN:
+ case VK_RWIN: Modifiers |= Mod_Meta; break;
+ }
+ if ((keyCode = MapVKToKeyCode((unsigned)wp)) != Key_None)
+ pApp->OnKey(keyCode, 0, true, Modifiers);
+
+ if (keyCode == Key_Escape && MMode == Mouse_Relative)
+ {
+ MMode = Mouse_RelativeEscaped;
+ ShowCursor(TRUE);
+ }
+ break;
+
+ case WM_KEYUP:
+ if ((keyCode = MapVKToKeyCode((unsigned)wp)) != Key_None)
+ pApp->OnKey(keyCode, 0, false, Modifiers);
+ switch (wp)
+ {
+ case VK_CONTROL: Modifiers &= ~Mod_Control; break;
+ case VK_MENU: Modifiers &= ~Mod_Alt; break;
+ case VK_SHIFT: Modifiers &= ~Mod_Shift; break;
+ case VK_LWIN:
+ case VK_RWIN: Modifiers &= ~Mod_Meta; break;
+ }
+ break;
+
+ case WM_LBUTTONDOWN:
+ //App->OnMouseButton(0,
+
+ ::SetCapture(hWnd);
+
+ if (MMode == Mouse_RelativeEscaped)
+ {
+ ::SetCursorPos(WindowCenter.x, WindowCenter.y);
+ ::ShowCursor(FALSE);
+ MMode = Mouse_Relative;
+ }
+ break;
+
+ case WM_LBUTTONUP:
+ ReleaseCapture();
+ break;
+
+ case WM_SETFOCUS:
+ // Do NOT restore the Relative mode here, since calling SetCursorPos
+ // would screw up titlebar window dragging.
+ // Let users click in the center instead to resume.
+ break;
+
+ case WM_KILLFOCUS:
+ if (MMode == Mouse_Relative)
+ {
+ MMode = Mouse_RelativeEscaped;
+ ShowCursor(TRUE);
+ }
+ break;
+
+ case WM_SIZE:
+ // Change window size as long as we're not being minimized.
+ if (wp != SIZE_MINIMIZED)
+ {
+ Width = LOWORD(lp);
+ Height = HIWORD(lp);
+ if (pRender)
+ pRender->SetWindowSize(Width, Height);
+ pApp->OnResize(Width,Height);
+ }
+ break;
+
+ case WM_STYLECHANGING:
+ // Resize the window. This is needed because the size includes any present system controls, and
+ // windows does not adjust it when changing to fullscreen.
+ {
+ STYLESTRUCT* pss = (STYLESTRUCT*)lp;
+ RECT winSize;
+ winSize.left = winSize.top = 0;
+ winSize.right = Width;
+ winSize.bottom = Height;
+ int w = winSize.right-winSize.left;
+ int h = winSize.bottom-winSize.top;
+ AdjustWindowRect(&winSize, pss->styleNew, false);
+ ::SetWindowPos(hWnd, NULL, 0, 0, w, h, SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER);
+ }
+ break;
+
+ case WM_QUIT:
+ case WM_CLOSE:
+ pApp->OnQuitRequest();
+ return false;
+ }
+
+ return DefWindowProc(hWnd, msg, wp, lp);
+}
+
+int PlatformCore::Run()
+{
+ while (!Quit)
+ {
+ MSG msg;
+ if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ else
+ {
+ pApp->OnIdle();
+
+ // Keep sleeping when we're minimized.
+ if (IsIconic(hWnd))
+ {
+ Sleep(10);
+ }
+ }
+ }
+
+ return ExitCode;
+}
+
+
+RenderDevice* PlatformCore::SetupGraphics(const SetupGraphicsDeviceSet& setupGraphicsDesc,
+ const char* type, const Render::RendererParams& rp)
+{
+ const SetupGraphicsDeviceSet* setupDesc = setupGraphicsDesc.PickSetupDevice(type);
+ OVR_ASSERT(setupDesc);
+
+ pRender = *setupDesc->pCreateDevice(rp, (void*)hWnd);
+ if (pRender)
+ pRender->SetWindowSize(Width, Height);
+
+ ::ShowWindow(hWnd, SW_RESTORE);
+ return pRender.GetPtr();
+}
+
+
+void PlatformCore::PlayMusicFile(const char *fileName)
+{
+ PlaySoundA(fileName, NULL, SND_FILENAME | SND_LOOP | SND_ASYNC);
+}
+
+
+//-----------------------------------------------------------------------------
+
+// Used to capture all the active monitor handles
+struct MonitorSet
+{
+ enum { MaxMonitors = 8 };
+ HMONITOR Monitors[MaxMonitors];
+ int MonitorCount;
+ int PrimaryCount;
+};
+
+
+BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor, HDC, LPRECT, LPARAM dwData)
+{
+ MonitorSet* monitorSet = (MonitorSet*)dwData;
+ if (monitorSet->MonitorCount > MonitorSet::MaxMonitors)
+ return FALSE;
+
+ monitorSet->Monitors[monitorSet->MonitorCount] = hMonitor;
+ monitorSet->MonitorCount++;
+ return TRUE;
+};
+
+
+// Returns the number of active screens for extended displays and 1 for mirrored display
+int PlatformCore::GetDisplayCount()
+{
+ // Get all the monitor handles
+ MonitorSet monitors;
+ monitors.MonitorCount = 0;
+ EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, (LPARAM)&monitors);
+
+ // Count the primary monitors
+ int primary = 0;
+ MONITORINFOEX info;
+ for (int m=0; m < monitors.MonitorCount; m++)
+ {
+ info.cbSize = sizeof(MONITORINFOEX);
+ GetMonitorInfo(monitors.Monitors[m], &info);
+
+ if (info.dwFlags & MONITORINFOF_PRIMARY)
+ primary++;
+ }
+
+ if (primary > 1)
+ return 1; // Regard mirrored displays as a single screen
+ else
+ return monitors.MonitorCount; // Return all extended displays
+}
+
+//-----------------------------------------------------------------------------
+// Returns the device name for the given screen index or empty string for invalid index
+// The zero index will always return the primary screen name
+Render::DisplayId PlatformCore::GetDisplay(int screen)
+{
+ // Get all the monitor handles
+ MonitorSet monitors;
+ monitors.MonitorCount = 0;
+ EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, (LPARAM)&monitors);
+
+ String screen_name;
+
+ // Get the name of the suppled screen index with the requirement
+ // that screen 0 is the primary monitor
+ int non_primary_count = 0;
+ MONITORINFOEX info;
+ for (int m=0; m < monitors.MonitorCount; m++)
+ {
+ info.cbSize = sizeof(MONITORINFOEX);
+ GetMonitorInfo(monitors.Monitors[m], &info);
+
+ if (info.dwFlags & MONITORINFOF_PRIMARY)
+ {
+ if (screen == 0)
+ {
+ screen_name = info.szDevice;
+ break;
+ }
+ }
+ else
+ {
+ non_primary_count++;
+ if (screen == non_primary_count)
+ {
+ screen_name = info.szDevice;
+ break;
+ }
+ }
+ }
+
+ return screen_name;
+}
+
+
+}}}
+
+
+int WINAPI WinMain(HINSTANCE hinst, HINSTANCE prevInst, LPSTR inArgs, int show)
+{
+ using namespace OVR;
+ using namespace OVR::Platform;
+
+ OVR_UNUSED2(prevInst, show);
+
+ // CreateApplication must be the first call since it does OVR::System::Initialize.
+ Application* app = Application::CreateApplication();
+ Win32::PlatformCore* platform = new Win32::PlatformCore(app, hinst);
+ // The platform attached to an app will be deleted by DestroyApplication.
+ app->SetPlatformCore(platform);
+
+ int exitCode = 0;
+
+ // Nested scope for container destructors to shutdown before DestroyApplication.
+ {
+ Array<String> args;
+ Array<const char*> argv;
+ argv.PushBack("app");
+
+ const char* p = inArgs;
+ const char* pstart = inArgs;
+ while (*p)
+ {
+ if (*p == ' ')
+ {
+ args.PushBack(String(pstart, p - pstart));
+ while (*p == ' ')
+ p++;
+ pstart = p;
+ }
+ else
+ {
+ p++;
+ }
+ }
+ if (p != pstart)
+ args.PushBack(String(pstart, p - pstart));
+ for (UPInt i = 0; i < args.GetSize(); i++)
+ argv.PushBack(args[i].ToCStr());
+
+ exitCode = app->OnStartup((int)argv.GetSize(), &argv[0]);
+ if (!exitCode)
+ exitCode = platform->Run();
+ }
+
+ // No OVR functions involving memory are allowed after this.
+ Application::DestroyApplication(app);
+ app = 0;
+
+ OVR_DEBUG_STATEMENT(_CrtDumpMemoryLeaks());
+ return exitCode;
+}
diff --git a/Samples/CommonSrc/Platform/Win32_Platform.h b/Samples/CommonSrc/Platform/Win32_Platform.h
new file mode 100644
index 0000000..5d40861
--- /dev/null
+++ b/Samples/CommonSrc/Platform/Win32_Platform.h
@@ -0,0 +1,101 @@
+/************************************************************************************
+
+Filename : Win32_Platform.h
+Content : Win32 implementation of Platform app infrastructure
+Created : September 6, 2012
+Authors : Andrew Reisse
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#ifndef OVR_Win32_Platform_h
+#define OVR_Win32_Platform_h
+
+#include "Platform.h"
+#include <windows.h>
+
+
+namespace OVR { namespace Render {
+ class RenderDevice;
+ struct DisplayId;
+}}
+
+namespace OVR { namespace Platform { namespace Win32 {
+
+class PlatformCore : public Platform::PlatformCore
+{
+ HWND hWnd;
+ HINSTANCE hInstance;
+ bool Quit;
+ int ExitCode;
+ int Width, Height;
+
+ MouseMode MMode;
+ POINT WindowCenter; // In desktop coordinates
+ HCURSOR Cursor;
+ int Modifiers;
+ String WindowTitle;
+
+ // Win32 static function that delegates to WindowProc member function.
+ static LRESULT CALLBACK systemWindowProc(HWND window, UINT msg, WPARAM wp, LPARAM lp);
+
+ LRESULT WindowProc(UINT msg, WPARAM wp, LPARAM lp);
+
+public:
+ PlatformCore(Application* app, HINSTANCE hinst);
+ ~PlatformCore();
+
+ bool SetupWindow(int w, int h);
+ void DestroyWindow();
+ void ShowWindow(bool visible);
+ void Exit(int exitcode) { Quit = 1; ExitCode = exitcode; }
+
+ RenderDevice* SetupGraphics(const SetupGraphicsDeviceSet& setupGraphicsDesc,
+ const char* type,
+ const Render::RendererParams& rp);
+
+ void SetMouseMode(MouseMode mm);
+ void GetWindowSize(int* w, int* h) const;
+
+ void SetWindowTitle(const char*title);
+ void PlayMusicFile(const char *fileName);
+ int GetDisplayCount();
+ Render::DisplayId GetDisplay(int screen);
+
+ int Run();
+};
+
+
+// Win32 key conversion helper.
+KeyCode MapVKToKeyCode(unsigned vk);
+
+}}}
+
+
+// OVR_PLATFORM_APP_ARGS specifies the Application class to use for startup,
+// providing it with startup arguments.
+#define OVR_PLATFORM_APP_ARGS(AppClass, args) \
+ OVR::Platform::Application* OVR::Platform::Application::CreateApplication() \
+ { OVR::System::Init(OVR::Log::ConfigureDefaultLog(OVR::LogMask_All)); \
+ return new AppClass args; } \
+ void OVR::Platform::Application::DestroyApplication(OVR::Platform::Application* app) \
+ { OVR::Platform::PlatformCore* platform = app->pPlatform; \
+ delete app; delete platform; OVR::System::Destroy(); };
+
+// OVR_PLATFORM_APP_ARGS specifies the Application startup class with no args.
+#define OVR_PLATFORM_APP(AppClass) OVR_PLATFORM_APP_ARGS(AppClass, ())
+
+#endif // OVR_Win32_Platform_h
diff --git a/Samples/CommonSrc/Render/Render_D3D10_Device.cpp b/Samples/CommonSrc/Render/Render_D3D10_Device.cpp
new file mode 100644
index 0000000..b2fe9be
--- /dev/null
+++ b/Samples/CommonSrc/Render/Render_D3D10_Device.cpp
@@ -0,0 +1,26 @@
+/************************************************************************************
+
+Filename : Renderer_D3D10_Device.cpp
+Content : Builds D3D10 renderer versions (to avoid duplication).
+Created : September 10, 2012
+Authors : Andrew Reisse
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#define OVR_D3D_VERSION 10
+#include "Render_D3D1X_Device.cpp"
+#undef OVR_D3D_VERSION \ No newline at end of file
diff --git a/Samples/CommonSrc/Render/Render_D3D10_Device.h b/Samples/CommonSrc/Render/Render_D3D10_Device.h
new file mode 100644
index 0000000..504e6ae
--- /dev/null
+++ b/Samples/CommonSrc/Render/Render_D3D10_Device.h
@@ -0,0 +1,26 @@
+/************************************************************************************
+
+Filename : Renderer_D3D10_Device.h
+Content : Builds D3D10 renderer versions (to avoid duplication).
+Created : September 10, 2012
+Authors : Andrew Reisse
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#define OVR_D3D_VERSION 10
+#include "Render_D3D1X_Device.h"
+#undef OVR_D3D_VERSION \ No newline at end of file
diff --git a/Samples/CommonSrc/Render/Render_D3D11_Device.cpp b/Samples/CommonSrc/Render/Render_D3D11_Device.cpp
new file mode 100644
index 0000000..33821da
--- /dev/null
+++ b/Samples/CommonSrc/Render/Render_D3D11_Device.cpp
@@ -0,0 +1,26 @@
+/************************************************************************************
+
+Filename : Renderer_D3D11_Device.cpp
+Content : Builds D3D11 renderer versions (to avoid duplication).
+Created : September 10, 2012
+Authors : Andrew Reisse
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#define OVR_D3D_VERSION 11
+#include "Render_D3D1X_Device.cpp"
+#undef OVR_D3D_VERSION \ No newline at end of file
diff --git a/Samples/CommonSrc/Render/Render_D3D11_Device.h b/Samples/CommonSrc/Render/Render_D3D11_Device.h
new file mode 100644
index 0000000..4a41e99
--- /dev/null
+++ b/Samples/CommonSrc/Render/Render_D3D11_Device.h
@@ -0,0 +1,26 @@
+/************************************************************************************
+
+Filename : Renderer_D3D11_Device.h
+Content : Builds D3D11 renderer versions (to avoid duplication).
+Created : September 10, 2012
+Authors : Andrew Reisse
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#define OVR_D3D_VERSION 11
+#include "Render_D3D1X_Device.h"
+#undef OVR_D3D_VERSION \ No newline at end of file
diff --git a/Samples/CommonSrc/Render/Render_D3D1X_Device.cpp b/Samples/CommonSrc/Render/Render_D3D1X_Device.cpp
new file mode 100644
index 0000000..aac5816
--- /dev/null
+++ b/Samples/CommonSrc/Render/Render_D3D1X_Device.cpp
@@ -0,0 +1,1831 @@
+/************************************************************************************
+
+Filename : Renderer_D3D1x.cpp
+Content : RenderDevice implementation for D3DX10/11.
+Created : September 10, 2012
+Authors : Andrew Reisse
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#include "Kernel/OVR_Log.h"
+#include "Kernel/OVR_Std.h"
+
+#include "Render_D3D1X_Device.h"
+
+#include <d3dcompiler.h>
+
+#if (OVR_D3D_VERSION == 10)
+namespace OVR { namespace Render { namespace D3D10 {
+#else
+namespace OVR { namespace Render { namespace D3D11 {
+#endif
+
+static D3D1x_(INPUT_ELEMENT_DESC) ModelVertexDesc[] =
+{
+ {"Position", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, offsetof(Vertex, Pos), D3D1x_(INPUT_PER_VERTEX_DATA), 0},
+ {"Color", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, offsetof(Vertex, C), D3D1x_(INPUT_PER_VERTEX_DATA), 0},
+ {"TexCoord", 0, DXGI_FORMAT_R32G32_FLOAT, 0, offsetof(Vertex, U), D3D1x_(INPUT_PER_VERTEX_DATA), 0},
+ {"TexCoord", 1, DXGI_FORMAT_R32G32_FLOAT, 0, offsetof(Vertex, U2), D3D1x_(INPUT_PER_VERTEX_DATA), 0},
+ {"Normal", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, offsetof(Vertex, Norm), D3D1x_(INPUT_PER_VERTEX_DATA), 0},
+};
+
+
+static const char* StdVertexShaderSrc =
+ "float4x4 Proj;\n"
+ "float4x4 View;\n"
+ "struct Varyings\n"
+ "{\n"
+ " float4 Position : SV_Position;\n"
+ " float4 Color : COLOR0;\n"
+ " float2 TexCoord : TEXCOORD0;\n"
+ " float2 TexCoord1 : TEXCOORD1;\n"
+ " float3 Normal : NORMAL;\n"
+ " float3 VPos : TEXCOORD4;\n"
+ "};\n"
+ "void main(in float4 Position : POSITION, in float4 Color : COLOR0, in float2 TexCoord : TEXCOORD0, in float2 TexCoord1 : TEXCOORD1, in float3 Normal : NORMAL,\n"
+ " out Varyings ov)\n"
+ "{\n"
+ " ov.Position = mul(Proj, mul(View, Position));\n"
+ " ov.Normal = mul(View, Normal);\n"
+ " ov.VPos = mul(View, Position);\n"
+ " ov.TexCoord = TexCoord;\n"
+ " ov.TexCoord1 = TexCoord1;\n"
+ " ov.Color = Color;\n"
+ "}\n";
+
+static const char* DirectVertexShaderSrc =
+ "float4x4 View : register(c4);\n"
+ "void main(in float4 Position : POSITION, in float4 Color : COLOR0, in float2 TexCoord : TEXCOORD0, in float2 TexCoord1 : TEXCOORD1, in float3 Normal : NORMAL,\n"
+ " out float4 oPosition : SV_Position, out float4 oColor : COLOR, out float2 oTexCoord : TEXCOORD0, out float2 oTexCoord1 : TEXCOORD1, out float3 oNormal : NORMAL)\n"
+ "{\n"
+ " oPosition = mul(View, Position);\n"
+ " oTexCoord = TexCoord;\n"
+ " oTexCoord1 = TexCoord1;\n"
+ " oColor = Color;\n"
+ " oNormal = mul(View, Normal);\n"
+ "}\n";
+
+static const char* SolidPixelShaderSrc =
+ "float4 Color;\n"
+ "struct Varyings\n"
+ "{\n"
+ " float4 Position : SV_Position;\n"
+ " float4 Color : COLOR0;\n"
+ " float2 TexCoord : TEXCOORD0;\n"
+ "};\n"
+ "float4 main(in Varyings ov) : SV_Target\n"
+ "{\n"
+ " return Color;\n"
+ "}\n";
+
+static const char* GouraudPixelShaderSrc =
+ "struct Varyings\n"
+ "{\n"
+ " float4 Position : SV_Position;\n"
+ " float4 Color : COLOR0;\n"
+ " float2 TexCoord : TEXCOORD0;\n"
+ "};\n"
+ "float4 main(in Varyings ov) : SV_Target\n"
+ "{\n"
+ " return ov.Color;\n"
+ "}\n";
+
+static const char* TexturePixelShaderSrc =
+ "Texture2D Texture : register(t0);\n"
+ "SamplerState Linear : register(s0);\n"
+ "struct Varyings\n"
+ "{\n"
+ " float4 Position : SV_Position;\n"
+ " float4 Color : COLOR0;\n"
+ " float2 TexCoord : TEXCOORD0;\n"
+ "};\n"
+ "float4 main(in Varyings ov) : SV_Target\n"
+ "{\n"
+ " float4 color2 = ov.Color * Texture.Sample(Linear, ov.TexCoord);\n"
+ " if (color2.a <= 0.4)\n"
+ " discard;\n"
+ " return color2;\n"
+ "}\n";
+
+static const char* MultiTexturePixelShaderSrc =
+ "Texture2D Texture[2] : register(t0);\n"
+ "SamplerState Linear : register(s0);\n"
+ "struct Varyings\n"
+ "{\n"
+ " float4 Position : SV_Position;\n"
+ " float4 Color : COLOR0;\n"
+ " float2 TexCoord : TEXCOORD0;\n"
+ " float2 TexCoord1 : TEXCOORD1;\n"
+ "};\n"
+ "float4 main(in Varyings ov) : SV_Target\n"
+ "{\n"
+ "float4 color1;\n"
+ "float4 color2;\n"
+ " color1 = Texture[0].Sample(Linear, ov.TexCoord);\n"
+ " color2 = Texture[1].Sample(Linear, ov.TexCoord1);\n"
+ " color2.rgb = color2.rgb * lerp(1.2, 1.9, saturate(length(color2.rgb)));\n"
+ " color2 = color1 * color2;\n"
+ " if (color2.a <= 0.4)\n"
+ " discard;\n"
+ " return color2;\n"
+ "}\n";
+
+#define LIGHTING_COMMON \
+ "cbuffer Lighting : register(b1)\n" \
+ "{\n" \
+ " float3 Ambient;\n" \
+ " float3 LightPos[8];\n" \
+ " float4 LightColor[8];\n" \
+ " float LightCount;\n" \
+ "};\n" \
+ "struct Varyings\n" \
+ "{\n" \
+ " float4 Position : SV_Position;\n" \
+ " float4 Color : COLOR0;\n" \
+ " float2 TexCoord : TEXCOORD0;\n" \
+ " float3 Normal : NORMAL;\n" \
+ " float3 VPos : TEXCOORD4;\n" \
+ "};\n" \
+ "float4 DoLight(Varyings v)\n" \
+ "{\n" \
+ " float3 norm = normalize(v.Normal);\n" \
+ " float3 light = Ambient;\n" \
+ " for (uint i = 0; i < LightCount; i++)\n"\
+ " {\n" \
+ " float3 ltp = (LightPos[i] - v.VPos);\n" \
+ " float ldist = dot(ltp,ltp);\n" \
+ " ltp = normalize(ltp);\n" \
+ " light += saturate(LightColor[i] * v.Color.rgb * dot(norm, ltp) / sqrt(ldist));\n"\
+ " }\n" \
+ " return float4(light, v.Color.a);\n" \
+ "}\n"
+
+static const char* LitSolidPixelShaderSrc =
+ LIGHTING_COMMON
+ "float4 main(in Varyings ov) : SV_Target\n"
+ "{\n"
+ " return DoLight(ov) * ov.Color;\n"
+ "}\n";
+
+static const char* LitTexturePixelShaderSrc =
+ "Texture2D Texture : register(t0);\n"
+ "SamplerState Linear : register(s0);\n"
+ LIGHTING_COMMON
+ "float4 main(in Varyings ov) : SV_Target\n"
+ "{\n"
+ " return DoLight(ov) * Texture.Sample(Linear, ov.TexCoord);\n"
+ "}\n";
+
+static const char* AlphaTexturePixelShaderSrc =
+ "Texture2D Texture : register(t0);\n"
+ "SamplerState Linear : register(s0);\n"
+ "struct Varyings\n"
+ "{\n"
+ " float4 Position : SV_Position;\n"
+ " float4 Color : COLOR0;\n"
+ " float2 TexCoord : TEXCOORD0;\n"
+ "};\n"
+ "float4 main(in Varyings ov) : SV_Target\n"
+ "{\n"
+ " return ov.Color * float4(1,1,1,Texture.Sample(Linear, ov.TexCoord).r);\n"
+ "}\n";
+
+
+// ***** PostProcess Shader
+
+static const char* PostProcessVertexShaderSrc =
+ "float4x4 View : register(c4);\n"
+ "float4x4 Texm : register(c8);\n"
+ "void main(in float4 Position : POSITION, in float4 Color : COLOR0, in float2 TexCoord : TEXCOORD0, in float2 TexCoord1 : TEXCOORD1,\n"
+ " out float4 oPosition : SV_Position, out float4 oColor : COLOR, out float2 oTexCoord : TEXCOORD0)\n"
+ "{\n"
+ " oPosition = mul(View, Position);\n"
+ " oTexCoord = mul(Texm, float4(TexCoord,0,1));\n"
+ " oColor = Color;\n"
+ "}\n";
+
+// Shader with just lens distortion correction.
+static const char* PostProcessPixelShaderSrc =
+ "Texture2D Texture : register(t0);\n"
+ "SamplerState Linear : register(s0);\n"
+ "float2 LensCenter;\n"
+ "float2 ScreenCenter;\n"
+ "float2 Scale;\n"
+ "float2 ScaleIn;\n"
+ "float4 HmdWarpParam;\n"
+ "\n"
+
+ // Scales input texture coordinates for distortion.
+ // ScaleIn maps texture coordinates to Scales to ([-1, 1]), although top/bottom will be
+ // larger due to aspect ratio.
+ "float2 HmdWarp(float2 in01)\n"
+ "{\n"
+ " float2 theta = (in01 - LensCenter) * ScaleIn;\n" // Scales to [-1, 1]
+ " float rSq = theta.x * theta.x + theta.y * theta.y;\n"
+ " float2 theta1 = theta * (HmdWarpParam.x + HmdWarpParam.y * rSq + "
+ " HmdWarpParam.z * rSq * rSq + HmdWarpParam.w * rSq * rSq * rSq);\n"
+ " return LensCenter + Scale * theta1;\n"
+ "}\n"
+
+ "float4 main(in float4 oPosition : SV_Position, in float4 oColor : COLOR,\n"
+ " in float2 oTexCoord : TEXCOORD0) : SV_Target\n"
+ "{\n"
+ " float2 tc = HmdWarp(oTexCoord);\n"
+ " if (any(clamp(tc, ScreenCenter-float2(0.25,0.5), ScreenCenter+float2(0.25, 0.5)) - tc))\n"
+ " return 0;\n"
+ " return Texture.Sample(Linear, tc);\n"
+ "}\n";
+
+// Shader with lens distortion and chromatic aberration correction.
+static const char* PostProcessPixelShaderWithChromAbSrc =
+ "Texture2D Texture : register(t0);\n"
+ "SamplerState Linear : register(s0);\n"
+ "float2 LensCenter;\n"
+ "float2 ScreenCenter;\n"
+ "float2 Scale;\n"
+ "float2 ScaleIn;\n"
+ "float4 HmdWarpParam;\n"
+ "float4 ChromAbParam;\n"
+ "\n"
+
+ // Scales input texture coordinates for distortion.
+ // ScaleIn maps texture coordinates to Scales to ([-1, 1]), although top/bottom will be
+ // larger due to aspect ratio.
+ "float4 main(in float4 oPosition : SV_Position, in float4 oColor : COLOR,\n"
+ " in float2 oTexCoord : TEXCOORD0) : SV_Target\n"
+ "{\n"
+ " float2 theta = (oTexCoord - LensCenter) * ScaleIn;\n" // Scales to [-1, 1]
+ " float rSq = theta.x * theta.x + theta.y * theta.y;\n"
+ " float2 theta1 = theta * (HmdWarpParam.x + HmdWarpParam.y * rSq + "
+ " HmdWarpParam.z * rSq * rSq + HmdWarpParam.w * rSq * rSq * rSq);\n"
+ " \n"
+ " // Detect whether blue texture coordinates are out of range since these will scaled out the furthest.\n"
+ " float2 thetaBlue = theta1 * (ChromAbParam.z + ChromAbParam.w * rSq);\n"
+ " float2 tcBlue = LensCenter + Scale * thetaBlue;\n"
+ " if (any(clamp(tcBlue, ScreenCenter-float2(0.25,0.5), ScreenCenter+float2(0.25, 0.5)) - tcBlue))\n"
+ " return 0;\n"
+ " \n"
+ " // Now do blue texture lookup.\n"
+ " float blue = Texture.Sample(Linear, tcBlue).b;\n"
+ " \n"
+ " // Do green lookup (no scaling).\n"
+ " float2 tcGreen = LensCenter + Scale * theta1;\n"
+ " float4 greenColor = Texture.Sample(Linear, tcGreen);\n"
+ " float green = greenColor.g;\n"
+ " float alpha = greenColor.a;\n"
+ " \n"
+ " // Do red scale and lookup.\n"
+ " float2 thetaRed = theta1 * (ChromAbParam.x + ChromAbParam.y * rSq);\n"
+ " float2 tcRed = LensCenter + Scale * thetaRed;\n"
+ " float red = Texture.Sample(Linear, tcRed).r;\n"
+ " \n"
+ " return float4(red, green, blue, alpha);\n"
+ "}\n";
+
+
+static const char* VShaderSrcs[VShader_Count] =
+{
+ DirectVertexShaderSrc,
+ StdVertexShaderSrc,
+ PostProcessVertexShaderSrc
+};
+static const char* FShaderSrcs[FShader_Count] =
+{
+ SolidPixelShaderSrc,
+ GouraudPixelShaderSrc,
+ TexturePixelShaderSrc,
+ AlphaTexturePixelShaderSrc,
+ PostProcessPixelShaderSrc,
+ PostProcessPixelShaderWithChromAbSrc,
+ LitSolidPixelShaderSrc,
+ LitTexturePixelShaderSrc,
+ MultiTexturePixelShaderSrc
+};
+
+RenderDevice::RenderDevice(const RendererParams& p, HWND window)
+{
+ RECT rc;
+ GetClientRect(window, &rc);
+ UINT width = rc.right - rc.left;
+ UINT height = rc.bottom - rc.top;
+ WindowWidth = width;
+ WindowHeight = height;
+ Window = window;
+
+ Params = p;
+ HRESULT hr = CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)(&DXGIFactory.GetRawRef()));
+ if (FAILED(hr))
+ return;
+
+ // Find the adapter & output (monitor) to use for fullscreen, based on the reported name of the HMD's monitor.
+ if (Params.Display.MonitorName.GetLength() > 0)
+ {
+ for(UINT AdapterIndex = 0; ; AdapterIndex++)
+ {
+ HRESULT hr = DXGIFactory->EnumAdapters(AdapterIndex, &Adapter.GetRawRef());
+ if (hr == DXGI_ERROR_NOT_FOUND)
+ break;
+
+ DXGI_ADAPTER_DESC Desc;
+ Adapter->GetDesc(&Desc);
+
+ UpdateMonitorOutputs();
+
+ if (FullscreenOutput)
+ break;
+ }
+
+ if (!FullscreenOutput)
+ Adapter = NULL;
+ }
+
+ if (!Adapter)
+ {
+ DXGIFactory->EnumAdapters(0, &Adapter.GetRawRef());
+ UpdateMonitorOutputs();
+ }
+
+ int flags = 0;
+
+#if (OVR_D3D_VERSION == 10)
+ hr = D3D10CreateDevice(Adapter, D3D10_DRIVER_TYPE_HARDWARE, NULL, flags, D3D1x_(SDK_VERSION),
+ &Device.GetRawRef());
+ Context = Device;
+ Context->AddRef();
+#else //11
+ hr = D3D11CreateDevice(Adapter, Adapter ? D3D_DRIVER_TYPE_UNKNOWN : D3D_DRIVER_TYPE_HARDWARE,
+ NULL, flags, NULL, 0, D3D1x_(SDK_VERSION),
+ &Device.GetRawRef(), NULL, &Context.GetRawRef());
+#endif
+ if (FAILED(hr))
+ return;
+
+ if (!RecreateSwapChain())
+ return;
+
+ if (Params.Fullscreen)
+ SwapChain->SetFullscreenState(1, FullscreenOutput);
+
+ CurRenderTarget = NULL;
+ for(int i = 0; i < Shader_Count; i++)
+ {
+ UniformBuffers[i] = *CreateBuffer();
+ MaxTextureSet[i] = 0;
+ }
+
+ ID3D10Blob* vsData = CompileShader("vs_4_0", DirectVertexShaderSrc);
+ VertexShaders[VShader_MV] = *new VertexShader(this, vsData);
+ for(int i = 1; i < VShader_Count; i++)
+ {
+ VertexShaders[i] = *new VertexShader(this, CompileShader("vs_4_0", VShaderSrcs[i]));
+ }
+
+ for(int i = 0; i < FShader_Count; i++)
+ {
+ PixelShaders[i] = *new PixelShader(this, CompileShader("ps_4_0", FShaderSrcs[i]));
+ }
+
+ SPInt bufferSize = vsData->GetBufferSize();
+ const void* buffer = vsData->GetBufferPointer();
+ ID3D1xInputLayout** objRef = &ModelVertexIL.GetRawRef();
+
+ HRESULT validate = Device->CreateInputLayout(ModelVertexDesc, 5, buffer, bufferSize, objRef);
+ OVR_UNUSED(validate);
+
+ Ptr<ShaderSet> gouraudShaders = *new ShaderSet();
+ gouraudShaders->SetShader(VertexShaders[VShader_MVP]);
+ gouraudShaders->SetShader(PixelShaders[FShader_Gouraud]);
+ DefaultFill = *new ShaderFill(gouraudShaders);
+
+#if (OVR_D3D_VERSION == 10)
+ D3D1x_(BLEND_DESC) bm;
+ memset(&bm, 0, sizeof(bm));
+ bm.BlendEnable[0] = true;
+ bm.BlendOp = bm.BlendOpAlpha = D3D1x_(BLEND_OP_ADD);
+ bm.SrcBlend = bm.SrcBlendAlpha = D3D1x_(BLEND_SRC_ALPHA);
+ bm.DestBlend = bm.DestBlendAlpha = D3D1x_(BLEND_INV_SRC_ALPHA);
+ bm.RenderTargetWriteMask[0] = D3D1x_(COLOR_WRITE_ENABLE_ALL);
+ Device->CreateBlendState(&bm, &BlendState.GetRawRef());
+#else
+ D3D1x_(BLEND_DESC) bm;
+ memset(&bm, 0, sizeof(bm));
+ bm.RenderTarget[0].BlendEnable = true;
+ bm.RenderTarget[0].BlendOp = bm.RenderTarget[0].BlendOpAlpha = D3D1x_(BLEND_OP_ADD);
+ bm.RenderTarget[0].SrcBlend = bm.RenderTarget[0].SrcBlendAlpha = D3D1x_(BLEND_SRC_ALPHA);
+ bm.RenderTarget[0].DestBlend = bm.RenderTarget[0].DestBlendAlpha = D3D1x_(BLEND_INV_SRC_ALPHA);
+ bm.RenderTarget[0].RenderTargetWriteMask = D3D1x_(COLOR_WRITE_ENABLE_ALL);
+ Device->CreateBlendState(&bm, &BlendState.GetRawRef());
+#endif
+
+ D3D1x_(RASTERIZER_DESC) rs;
+ memset(&rs, 0, sizeof(rs));
+ rs.AntialiasedLineEnable = true;
+ rs.CullMode = D3D1x_(CULL_BACK);
+ // rs.CullMode = D3D1x_(CULL_NONE);
+ rs.DepthClipEnable = true;
+ rs.FillMode = D3D1x_(FILL_SOLID);
+ Device->CreateRasterizerState(&rs, &Rasterizer.GetRawRef());
+
+ QuadVertexBuffer = *CreateBuffer();
+ const Render::Vertex QuadVertices[] =
+ { Vertex(Vector3f(0, 1, 0)), Vertex(Vector3f(1, 1, 0)),
+ Vertex(Vector3f(0, 0, 0)), Vertex(Vector3f(1, 0, 0)) };
+ QuadVertexBuffer->Data(Buffer_Vertex, QuadVertices, sizeof(QuadVertices));
+
+ SetDepthMode(0, 0);
+}
+
+RenderDevice::~RenderDevice()
+{
+ if (SwapChain && Params.Fullscreen)
+ {
+ SwapChain->SetFullscreenState(false, NULL);
+ }
+}
+
+
+// Implement static initializer function to create this class.
+Render::RenderDevice* RenderDevice::CreateDevice(const RendererParams& rp, void* oswnd)
+{
+ return new RenderDevice(rp, (HWND)oswnd);
+}
+
+
+// Fallback monitor enumeration in case newly plugged in monitor wasn't detected.
+// Added originally for the FactoryTest app.
+// New Outputs don't seem to be detected unless adapter is re-created, but that would also
+// require us to re-initialize D3D10 (recreating objects, etc). This bypasses that for "fake"
+// fullscreen modes.
+BOOL CALLBACK MonitorEnumFunc(HMONITOR hMonitor, HDC, LPRECT, LPARAM dwData)
+{
+ RenderDevice* renderer = (RenderDevice*)dwData;
+
+ MONITORINFOEX monitor;
+ monitor.cbSize = sizeof(monitor);
+
+ if (::GetMonitorInfo(hMonitor, &monitor) && monitor.szDevice[0])
+ {
+ DISPLAY_DEVICE dispDev;
+ memset(&dispDev, 0, sizeof(dispDev));
+ dispDev.cb = sizeof(dispDev);
+
+ if (::EnumDisplayDevices(monitor.szDevice, 0, &dispDev, 0))
+ {
+ if (strstr(String(dispDev.DeviceName).ToCStr(), renderer->GetParams().Display.MonitorName.ToCStr()))
+ {
+ renderer->FSDesktopX = monitor.rcMonitor.left;
+ renderer->FSDesktopY = monitor.rcMonitor.top;
+ return FALSE;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+
+void RenderDevice::UpdateMonitorOutputs(bool needRecreate)
+{
+ HRESULT hr;
+
+ if (needRecreate)
+ {
+ // need to recreate DXGIFactory and Adapter in order
+ // to get latest info about monitors.
+ if (SwapChain)
+ {
+ SwapChain->SetFullscreenState(FALSE, NULL);
+ SwapChain->Release();
+ SwapChain = NULL;
+ }
+
+ DXGIFactory = NULL;
+ Adapter = NULL;
+ hr = CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)(&DXGIFactory.GetRawRef()));
+ if (FAILED(hr))
+ return;
+ DXGIFactory->EnumAdapters(0, &Adapter.GetRawRef());
+ }
+
+ bool deviceNameFound = false;
+
+ for(UINT OutputIndex = 0; ; OutputIndex++)
+ {
+ Ptr<IDXGIOutput> Output;
+ hr = Adapter->EnumOutputs(OutputIndex, &Output.GetRawRef());
+ if (hr == DXGI_ERROR_NOT_FOUND)
+ {
+ break;
+ }
+
+ DXGI_OUTPUT_DESC OutDesc;
+ Output->GetDesc(&OutDesc);
+
+ MONITORINFOEX monitor;
+ monitor.cbSize = sizeof(monitor);
+ if (::GetMonitorInfo(OutDesc.Monitor, &monitor) && monitor.szDevice[0])
+ {
+ DISPLAY_DEVICE dispDev;
+ memset(&dispDev, 0, sizeof(dispDev));
+ dispDev.cb = sizeof(dispDev);
+
+ if (::EnumDisplayDevices(monitor.szDevice, 0, &dispDev, 0))
+ {
+ if (strstr(String(dispDev.DeviceName).ToCStr(), Params.Display.MonitorName.ToCStr()))
+ {
+ deviceNameFound = true;
+ FullscreenOutput = Output;
+ FSDesktopX = monitor.rcMonitor.left;
+ FSDesktopY = monitor.rcMonitor.top;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!deviceNameFound && !Params.Display.MonitorName.IsEmpty())
+ {
+ EnumDisplayMonitors(0, 0, MonitorEnumFunc, (LPARAM)this);
+ }
+}
+
+bool RenderDevice::RecreateSwapChain()
+{
+ DXGI_SWAP_CHAIN_DESC scDesc;
+ memset(&scDesc, 0, sizeof(scDesc));
+ scDesc.BufferCount = 1;
+ scDesc.BufferDesc.Width = WindowWidth;
+ scDesc.BufferDesc.Height = WindowHeight;
+ scDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+ scDesc.BufferDesc.RefreshRate.Numerator = 60;
+ scDesc.BufferDesc.RefreshRate.Denominator = 1;
+ scDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+ scDesc.OutputWindow = Window;
+ scDesc.SampleDesc.Count = Params.Multisample;
+ scDesc.SampleDesc.Quality = 0;
+ scDesc.Windowed = Params.Fullscreen != Display_Fullscreen;
+ scDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
+
+ if (SwapChain)
+ {
+ SwapChain->SetFullscreenState(FALSE, NULL);
+ SwapChain->Release();
+ SwapChain = NULL;
+ }
+
+ Ptr<IDXGISwapChain> newSC;
+ if (FAILED(DXGIFactory->CreateSwapChain(Device, &scDesc, &newSC.GetRawRef())))
+ return false;
+ SwapChain = newSC;
+
+ BackBuffer = NULL;
+ BackBufferRT = NULL;
+ HRESULT hr = SwapChain->GetBuffer(0, __uuidof(ID3D1xTexture2D), (void**)&BackBuffer.GetRawRef());
+ if (FAILED(hr))
+ return false;
+
+ hr = Device->CreateRenderTargetView(BackBuffer, NULL, &BackBufferRT.GetRawRef());
+ if (FAILED(hr))
+ return false;
+
+ Texture* depthBuffer = GetDepthBuffer(WindowWidth, WindowHeight, Params.Multisample);
+ CurDepthBuffer = depthBuffer;
+ if (CurRenderTarget == NULL)
+ {
+ Context->OMSetRenderTargets(1, &BackBufferRT.GetRawRef(), depthBuffer->TexDsv);
+ }
+ return true;
+}
+
+bool RenderDevice::SetParams(const RendererParams& newParams)
+{
+ String oldMonitor = Params.Display.MonitorName;
+
+ Params = newParams;
+ if (newParams.Display.MonitorName != oldMonitor)
+ {
+ UpdateMonitorOutputs(true);
+ }
+
+ // Cause this to be recreated with the new multisample mode.
+ pSceneColorTex = NULL;
+ return RecreateSwapChain();
+}
+
+void RenderDevice::SetWindowSize(int w, int h)
+{
+ if (w == WindowWidth && h == WindowHeight)
+ return;
+
+ WindowWidth = w;
+ WindowHeight = h;
+ Context->OMSetRenderTargets(0, NULL, NULL);
+ BackBuffer = NULL;
+ BackBufferRT = NULL;
+ if (SwapChain)
+ {
+ SwapChain->ResizeBuffers(2, WindowWidth, WindowHeight, DXGI_FORMAT_R8G8B8A8_UNORM, 0);
+ SwapChain->GetBuffer(0, __uuidof(ID3D1xTexture2D), (void**)&BackBuffer.GetRawRef());
+ }
+ Device->CreateRenderTargetView(BackBuffer, NULL, &BackBufferRT.GetRawRef());
+}
+
+bool RenderDevice::SetFullscreen(DisplayMode fullscreen)
+{
+ if (fullscreen == Params.Fullscreen)
+ {
+ return true;
+ }
+
+ if (Params.Fullscreen == Display_FakeFullscreen)
+ {
+ SetWindowLong(Window, GWL_STYLE, WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPSIBLINGS);
+ SetWindowPos(Window, NULL, PreFullscreenX, PreFullscreenY,
+ PreFullscreenW, PreFullscreenH, SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
+ }
+
+ if (fullscreen == Display_FakeFullscreen)
+ {
+ // Get WINDOWPLACEMENT before changing style to get OVERLAPPED coordinates,
+ // which we will restore.
+ WINDOWPLACEMENT wp;
+ wp.length = sizeof(wp);
+ GetWindowPlacement(Window, &wp);
+ PreFullscreenW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
+ PreFullscreenH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
+ PreFullscreenX = wp.rcNormalPosition.left;
+ PreFullscreenY = wp.rcNormalPosition.top;
+ // Warning: SetWindowLong sends message computed based on old size (incorrect).
+ // A proper work-around would be to mask that message out during window frame change in Platform.
+ SetWindowLong(Window, GWL_STYLE, WS_OVERLAPPED | WS_VISIBLE | WS_CLIPSIBLINGS);
+ SetWindowPos(Window, NULL, FSDesktopX, FSDesktopY, 0, 0, SWP_NOSIZE | SWP_NOACTIVATE | SWP_FRAMECHANGED);
+
+ // Relocate cursor into the window to avoid losing focus on first click.
+ POINT oldCursor;
+ if (GetCursorPos(&oldCursor) &&
+ ((oldCursor.x < FSDesktopX) || (oldCursor.x > (FSDesktopX + PreFullscreenW)) ||
+ (oldCursor.y < FSDesktopY) || (oldCursor.x > (FSDesktopY + PreFullscreenH))))
+ {
+ // TBD: FullScreen window logic should really be in platform; it causes world rotation
+ // in relative mouse mode.
+ ::SetCursorPos(FSDesktopX, FSDesktopY);
+ }
+ }
+ else
+ {
+ HRESULT hr = SwapChain->SetFullscreenState(fullscreen, fullscreen ? FullscreenOutput : NULL);
+ if (FAILED(hr))
+ {
+ return false;
+ }
+ }
+
+ Params.Fullscreen = fullscreen;
+ return true;
+}
+
+void RenderDevice::SetMultipleViewports(int n, const Viewport* vps)
+{
+ if (n > 2)
+ {
+ n = 2;
+ }
+ for(int i = 0; i < n; i++)
+ {
+#if (OVR_D3D_VERSION == 10)
+ Viewports[i].Width = vps[i].w;
+ Viewports[i].Height = vps[i].h;
+ Viewports[i].MinDepth = 0;
+ Viewports[i].MaxDepth = 1;
+ Viewports[i].TopLeftX = vps[i].x;
+ Viewports[i].TopLeftY = vps[i].y;
+#else
+ Viewports[i].Width = (float)vps[i].w;
+ Viewports[i].Height = (float)vps[i].h;
+ Viewports[i].MinDepth = 0;
+ Viewports[i].MaxDepth = 1;
+ Viewports[i].TopLeftX = (float)vps[i].x;
+ Viewports[i].TopLeftY = (float)vps[i].y;
+#endif
+ }
+ NumViewports = n;
+ Context->RSSetViewports(n, Viewports);
+}
+
+static int GetDepthStateIndex(bool enable, bool write, RenderDevice::CompareFunc func)
+{
+ if (!enable)
+ {
+ return 0;
+ }
+ return 1 + int(func) * 2 + write;
+}
+
+void RenderDevice::SetDepthMode(bool enable, bool write, CompareFunc func)
+{
+ int index = GetDepthStateIndex(enable, write, func);
+ if (DepthStates[index])
+ {
+ CurDepthState = DepthStates[index];
+ Context->OMSetDepthStencilState(DepthStates[index], 0);
+ return;
+ }
+
+ D3D1x_(DEPTH_STENCIL_DESC) dss;
+ memset(&dss, 0, sizeof(dss));
+ dss.DepthEnable = enable;
+ switch(func)
+ {
+ case Compare_Always: dss.DepthFunc = D3D1x_(COMPARISON_ALWAYS); break;
+ case Compare_Less: dss.DepthFunc = D3D1x_(COMPARISON_LESS); break;
+ case Compare_Greater: dss.DepthFunc = D3D1x_(COMPARISON_GREATER); break;
+ default:
+ assert(0);
+ }
+ dss.DepthWriteMask = write ? D3D1x_(DEPTH_WRITE_MASK_ALL) : D3D1x_(DEPTH_WRITE_MASK_ZERO);
+ Device->CreateDepthStencilState(&dss, &DepthStates[index].GetRawRef());
+ Context->OMSetDepthStencilState(DepthStates[index], 0);
+ CurDepthState = DepthStates[index];
+}
+
+Texture* RenderDevice::GetDepthBuffer(int w, int h, int ms)
+{
+ for(unsigned i = 0; i < DepthBuffers.GetSize(); i++)
+ {
+ if (w == DepthBuffers[i]->Width && h == DepthBuffers[i]->Height &&
+ ms == DepthBuffers[i]->Samples)
+ return DepthBuffers[i];
+ }
+
+ Ptr<Texture> newDepth = *CreateTexture(Texture_Depth | Texture_RenderTarget | ms, w, h, NULL);
+ if (newDepth == NULL)
+ {
+ OVR_DEBUG_LOG(("Failed to get depth buffer."));
+ return NULL;
+ }
+
+ DepthBuffers.PushBack(newDepth);
+ return newDepth.GetPtr();
+}
+
+void RenderDevice::Clear(float r, float g, float b, float a, float depth)
+{
+ const float color[] = {r, g, b, a};
+
+ // save state that is affected by clearing this way
+ ID3D1xDepthStencilState* oldDepthState = CurDepthState;
+ StandardUniformData clearUniforms;
+
+ SetDepthMode(true, true, Compare_Always);
+
+ Context->IASetInputLayout(ModelVertexIL);
+#if (OVR_D3D_VERSION == 10)
+ Context->GSSetShader(NULL);
+#else
+ Context->GSSetShader(NULL, NULL, 0);
+#endif
+ //Shader<Shader_Geometry,ID3D1xGeometryShader> NullGS(this,(ID3D1xGeometryShader*)NULL);
+ //NullGS.Set(Prim_TriangleStrip);
+
+ ID3D1xShaderResourceView* sv[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+ if (MaxTextureSet[Shader_Fragment])
+ {
+ Context->PSSetShaderResources(0, MaxTextureSet[Shader_Fragment], sv);
+ }
+
+ ID3D1xBuffer* vertexBuffer = QuadVertexBuffer->GetBuffer();
+ UINT vertexStride = sizeof(Vertex);
+ UINT vertexOffset = 0;
+ Context->IASetVertexBuffers(0, 1, &vertexBuffer, &vertexStride, &vertexOffset);
+
+ clearUniforms.View = Matrix4f(2, 0, 0, 0,
+ 0, 2, 0, 0,
+ 0, 0, 0, 0,
+ -1, -1, depth, 1);
+ UniformBuffers[Shader_Vertex]->Data(Buffer_Uniform, &clearUniforms, sizeof(clearUniforms));
+
+ ID3D1xBuffer* vertexConstants = UniformBuffers[Shader_Vertex]->GetBuffer();
+ Context->VSSetConstantBuffers(0, 1, &vertexConstants);
+ Context->IASetPrimitiveTopology(D3D1x_(PRIMITIVE_TOPOLOGY_TRIANGLESTRIP));
+ VertexShaders[VShader_MV]->Set(Prim_TriangleStrip);
+ PixelShaders[FShader_Solid]->Set(Prim_TriangleStrip);
+
+ UniformBuffers[Shader_Pixel]->Data(Buffer_Uniform, color, sizeof(color));
+ PixelShaders[FShader_Solid]->SetUniformBuffer(UniformBuffers[Shader_Pixel]);
+
+ if (NumViewports > 1)
+ {
+ for(int i = 0; i < NumViewports; i++)
+ {
+ Context->RSSetViewports(1, &Viewports[i]);
+ Context->OMSetBlendState(NULL, NULL, 0xffffffff);
+ Context->Draw(4, 0);
+ }
+ Context->RSSetViewports(NumViewports, Viewports);
+ }
+ else
+ {
+ Context->OMSetBlendState(NULL, NULL, 0xffffffff);
+ Context->Draw(4, 0);
+ }
+
+ // reset
+ CurDepthState = oldDepthState;
+ Context->OMSetDepthStencilState(CurDepthState, 0);
+}
+
+// Buffers
+
+Buffer* RenderDevice::CreateBuffer()
+{
+ return new Buffer(this);
+}
+
+Buffer::~Buffer()
+{
+}
+
+bool Buffer::Data(int use, const void *buffer, size_t size)
+{
+ if (D3DBuffer && Size >= size)
+ {
+ if (Dynamic)
+ {
+ if (!buffer)
+ return true;
+
+ void* v = Map(0, size, Map_Discard);
+ if (v)
+ {
+ memcpy(v, buffer, size);
+ Unmap(v);
+ return true;
+ }
+ }
+ else
+ {
+ Ren->Context->UpdateSubresource(D3DBuffer, 0, NULL, buffer, 0, 0);
+ return true;
+ }
+ }
+ if (D3DBuffer)
+ {
+ D3DBuffer = NULL;
+ Size = 0;
+ Use = 0;
+ Dynamic = 0;
+ }
+
+ D3D1x_(BUFFER_DESC) desc;
+ memset(&desc, 0, sizeof(desc));
+ if (use & Buffer_ReadOnly)
+ {
+ desc.Usage = D3D1x_(USAGE_IMMUTABLE);
+ desc.CPUAccessFlags = 0;
+ }
+ else
+ {
+ desc.Usage = D3D1x_(USAGE_DYNAMIC);
+ desc.CPUAccessFlags = D3D1x_(CPU_ACCESS_WRITE);
+ Dynamic = 1;
+ }
+
+ switch(use & Buffer_TypeMask)
+ {
+ case Buffer_Vertex: desc.BindFlags = D3D1x_(BIND_VERTEX_BUFFER); break;
+ case Buffer_Index: desc.BindFlags = D3D1x_(BIND_INDEX_BUFFER); break;
+ case Buffer_Uniform:
+ desc.BindFlags = D3D1x_(BIND_CONSTANT_BUFFER);
+ size += ((size + 15) & ~15) - size;
+ break;
+ case Buffer_Feedback:
+ desc.BindFlags = D3D1x_(BIND_STREAM_OUTPUT);
+ desc.Usage = D3D1x_(USAGE_DEFAULT);
+ desc.CPUAccessFlags = 0;
+ size += ((size + 15) & ~15) - size;
+ break;
+ }
+
+ desc.ByteWidth = (unsigned)size;
+
+ D3D1x_(SUBRESOURCE_DATA) sr;
+ sr.pSysMem = buffer;
+ sr.SysMemPitch = 0;
+ sr.SysMemSlicePitch = 0;
+
+ HRESULT hr = Ren->Device->CreateBuffer(&desc, buffer ? &sr : NULL, &D3DBuffer.GetRawRef());
+ if (SUCCEEDED(hr))
+ {
+ Use = use;
+ Size = desc.ByteWidth;
+ return 1;
+ }
+ return 0;
+}
+
+void* Buffer::Map(size_t start, size_t size, int flags)
+{
+ OVR_UNUSED(size);
+
+ D3D1x_(MAP) mapFlags = D3D1x_(MAP_WRITE);
+ if (flags & Map_Discard)
+ mapFlags = D3D1x_(MAP_WRITE_DISCARD);
+ if (flags & Map_Unsynchronized)
+ mapFlags = D3D1x_(MAP_WRITE_NO_OVERWRITE);
+
+#if (OVR_D3D_VERSION == 10)
+ void* map;
+ if (SUCCEEDED(D3DBuffer->Map(mapFlags, 0, &map)))
+ return ((char*)map) + start;
+ else
+ return NULL;
+#else
+ D3D1x_(MAPPED_SUBRESOURCE) map;
+ if (SUCCEEDED(Ren->Context->Map(D3DBuffer, 0, mapFlags, 0, &map)))
+ return ((char*)map.pData) + start;
+ else
+ return NULL;
+#endif
+}
+
+bool Buffer::Unmap(void *m)
+{
+ OVR_UNUSED(m);
+
+#if (OVR_D3D_VERSION == 10)
+ D3DBuffer->Unmap();
+#else
+ Ren->Context->Unmap(D3DBuffer, 0);
+#endif
+ return true;
+}
+
+
+// Shaders
+
+#if (OVR_D3D_VERSION == 10)
+template<> bool Shader<Render::Shader_Vertex, ID3D10VertexShader>::Load(void* shader, size_t size)
+{
+ return SUCCEEDED(Ren->Device->CreateVertexShader(shader, size, &D3DShader));
+}
+template<> bool Shader<Render::Shader_Pixel, ID3D10PixelShader>::Load(void* shader, size_t size)
+{
+ return SUCCEEDED(Ren->Device->CreatePixelShader(shader, size, &D3DShader));
+}
+template<> bool Shader<Render::Shader_Geometry, ID3D10GeometryShader>::Load(void* shader, size_t size)
+{
+ return SUCCEEDED(Ren->Device->CreateGeometryShader(shader, size, &D3DShader));
+}
+
+template<> void Shader<Render::Shader_Vertex, ID3D10VertexShader>::Set(PrimitiveType) const
+{
+ Ren->Context->VSSetShader(D3DShader);
+}
+template<> void Shader<Render::Shader_Pixel, ID3D10PixelShader>::Set(PrimitiveType) const
+{
+ Ren->Context->PSSetShader(D3DShader);
+}
+template<> void Shader<Render::Shader_Geometry, ID3D10GeometryShader>::Set(PrimitiveType) const
+{
+ Ren->Context->GSSetShader(D3DShader);
+}
+
+#else // 11
+template<> bool Shader<Render::Shader_Vertex, ID3D11VertexShader>::Load(void* shader, size_t size)
+{
+ return SUCCEEDED(Ren->Device->CreateVertexShader(shader, size, NULL, &D3DShader));
+}
+template<> bool Shader<Render::Shader_Pixel, ID3D11PixelShader>::Load(void* shader, size_t size)
+{
+ return SUCCEEDED(Ren->Device->CreatePixelShader(shader, size, NULL, &D3DShader));
+}
+template<> bool Shader<Render::Shader_Geometry, ID3D11GeometryShader>::Load(void* shader, size_t size)
+{
+ return SUCCEEDED(Ren->Device->CreateGeometryShader(shader, size, NULL, &D3DShader));
+}
+
+template<> void Shader<Render::Shader_Vertex, ID3D11VertexShader>::Set(PrimitiveType) const
+{
+ Ren->Context->VSSetShader(D3DShader, NULL, 0);
+}
+template<> void Shader<Render::Shader_Pixel, ID3D11PixelShader>::Set(PrimitiveType) const
+{
+ Ren->Context->PSSetShader(D3DShader, NULL, 0);
+}
+template<> void Shader<Render::Shader_Geometry, ID3D11GeometryShader>::Set(PrimitiveType) const
+{
+ Ren->Context->GSSetShader(D3DShader, NULL, 0);
+}
+#endif
+
+template<> void Shader<Render::Shader_Vertex, ID3D1xVertexShader>::SetUniformBuffer(Render::Buffer* buffer, int i)
+{
+ Ren->Context->VSSetConstantBuffers(i, 1, &((Buffer*)buffer)->D3DBuffer.GetRawRef());
+}
+template<> void Shader<Render::Shader_Pixel, ID3D1xPixelShader>::SetUniformBuffer(Render::Buffer* buffer, int i)
+{
+ Ren->Context->PSSetConstantBuffers(i, 1, &((Buffer*)buffer)->D3DBuffer.GetRawRef());
+}
+template<> void Shader<Render::Shader_Geometry, ID3D1xGeometryShader>::SetUniformBuffer(Render::Buffer* buffer, int i)
+{
+ Ren->Context->GSSetConstantBuffers(i, 1, &((Buffer*)buffer)->D3DBuffer.GetRawRef());
+}
+
+ID3D10Blob* RenderDevice::CompileShader(const char* profile, const char* src, const char* mainName)
+{
+ ID3D10Blob* shader;
+ ID3D10Blob* errors;
+ HRESULT hr = D3DCompile(src, strlen(src), NULL, NULL, NULL, mainName, profile,
+ 0, 0, &shader, &errors);
+ if (FAILED(hr))
+ {
+ OVR_DEBUG_LOG(("Compiling D3D shader for %s failed\n%s\n\n%s",
+ profile, src, errors->GetBufferPointer()));
+ OutputDebugStringA((char*)errors->GetBufferPointer());
+ return NULL;
+ }
+ if (errors)
+ {
+ errors->Release();
+ }
+ return shader;
+}
+
+
+ShaderBase::ShaderBase(RenderDevice* r, ShaderStage stage)
+ : Render::Shader(stage), Ren(r), UniformData(0)
+{
+}
+ShaderBase::~ShaderBase()
+{
+ if (UniformData)
+ {
+ OVR_FREE(UniformData);
+ }
+}
+
+bool ShaderBase::SetUniform(const char* name, int n, const float* v)
+{
+ for(unsigned i = 0; i < UniformInfo.GetSize(); i++)
+ if (!strcmp(UniformInfo[i].Name.ToCStr(), name))
+ {
+ memcpy(UniformData + UniformInfo[i].Offset, v, n * sizeof(float));
+ return 1;
+ }
+ return 0;
+}
+
+void ShaderBase::InitUniforms(ID3D10Blob* s)
+{
+ ID3D10ShaderReflection* ref = NULL;
+ D3D10ReflectShader(s->GetBufferPointer(), s->GetBufferSize(), &ref);
+ ID3D10ShaderReflectionConstantBuffer* buf = ref->GetConstantBufferByIndex(0);
+ D3D10_SHADER_BUFFER_DESC bufd;
+ if (FAILED(buf->GetDesc(&bufd)))
+ {
+ UniformsSize = 0;
+ if (UniformData)
+ {
+ OVR_FREE(UniformData);
+ UniformData = 0;
+ }
+ return;
+ }
+
+ for(unsigned i = 0; i < bufd.Variables; i++)
+ {
+ ID3D10ShaderReflectionVariable* var = buf->GetVariableByIndex(i);
+ if (var)
+ {
+ D3D10_SHADER_VARIABLE_DESC vd;
+ if (SUCCEEDED(var->GetDesc(&vd)))
+ {
+ Uniform u;
+ u.Name = vd.Name;
+ u.Offset = vd.StartOffset;
+ u.Size = vd.Size;
+ UniformInfo.PushBack(u);
+ }
+ }
+ }
+
+ UniformsSize = bufd.Size;
+ UniformData = (unsigned char*)OVR_ALLOC(bufd.Size);
+}
+
+void ShaderBase::UpdateBuffer(Buffer* buf)
+{
+ if (UniformsSize)
+ {
+ buf->Data(Buffer_Uniform, UniformData, UniformsSize);
+ }
+}
+
+void RenderDevice::SetCommonUniformBuffer(int i, Render::Buffer* buffer)
+{
+ CommonUniforms[i] = (Buffer*)buffer;
+
+ Context->PSSetConstantBuffers(1, 1, &CommonUniforms[1]->D3DBuffer.GetRawRef());
+ Context->VSSetConstantBuffers(1, 1, &CommonUniforms[1]->D3DBuffer.GetRawRef());
+}
+
+Render::Shader *RenderDevice::LoadBuiltinShader(ShaderStage stage, int shader)
+{
+ switch(stage)
+ {
+ case Shader_Vertex:
+ return VertexShaders[shader];
+ case Shader_Pixel:
+ return PixelShaders[shader];
+ default:
+ return NULL;
+ }
+}
+
+ShaderBase* RenderDevice::CreateStereoShader(PrimitiveType prim, Render::Shader* vs)
+{
+ if (pStereoShaders[prim])
+ {
+ return pStereoShaders[prim];
+ }
+
+ OVR_UNUSED(vs);
+ const char* varyings =
+ " float4 Position : SV_Position;\n"
+ " float4 Color : COLOR0;\n"
+ " float2 TexCoord : TEXCOORD0;\n"
+ " float3 Normal : NORMAL;\n";
+ const char* copyVaryings =
+ " o.Color = iv[i].Color;\n"
+ " o.Normal = iv[i].Normal;\n"
+ " o.TexCoord = iv[i].TexCoord;\n";
+
+ StringBuffer src =
+ "float4x4 Proj[2] : register(c0);\n"
+ "float4 ViewOffset : register(c8);\n"
+ "struct Varyings\n"
+ "{\n";
+ src += varyings;
+ src += "};\n"
+ "struct OutVaryings\n"
+ "{\n";
+ src += varyings;
+ src +=
+ " float3 VPos : TEXCOORD4;\n"
+ " uint Viewport : SV_ViewportArrayIndex;\n"
+ "};\n";
+
+ if (prim == Prim_Lines)
+ src +=
+ "[maxvertexcount(4)]\n"
+ "void main(line Varyings iv[2], inout LineStream<OutVaryings> v)\n";
+ else
+ src +=
+ "[maxvertexcount(6)]\n"
+ "void main(triangle Varyings iv[3], inout TriangleStream<OutVaryings> v)\n";
+
+ char ivsize[6];
+ OVR_sprintf(ivsize, 6, "%d", (prim == Prim_Lines) ? 2 : 3);
+
+ src +=
+ "{\n"
+ " OutVaryings o;\n"
+ " for (uint i = 0; i < ";
+ src += ivsize;
+ src += "; i++)\n"
+ " {\n"
+ " o.Position = mul(Proj[0], iv[i].Position - ViewOffset);\n"
+ " o.VPos = iv[i].Position;\n"
+ " o.Viewport = 0;\n";
+ src += copyVaryings;
+ src +=
+ " v.Append(o);\n"
+ " }\n"
+ " v.RestartStrip();\n"
+ " for (uint i = 0; i < ";
+ src += ivsize;
+ src += "; i++)\n"
+ " {\n"
+ " o.Position = mul(Proj[1], iv[i].Position + ViewOffset);\n"
+ " o.VPos = iv[i].Position;\n"
+ " o.Viewport = 1;\n";
+ src += copyVaryings;
+ src +=
+ " v.Append(o);\n"
+ " }\n"
+ " v.RestartStrip();\n"
+ "}\n";
+
+ pStereoShaders[prim] = *new GeomShader(this, CompileShader("gs_4_0", src.ToCStr()));
+ return pStereoShaders[prim];
+}
+
+Fill* RenderDevice::CreateSimpleFill(int flags)
+{
+ OVR_UNUSED(flags);
+ return DefaultFill;
+}
+
+// Textures
+
+ID3D1xSamplerState* RenderDevice::GetSamplerState(int sm)
+{
+ if (SamplerStates[sm])
+ return SamplerStates[sm];
+
+ D3D1x_(SAMPLER_DESC) ss;
+ memset(&ss, 0, sizeof(ss));
+ if (sm & Sample_Clamp)
+ ss.AddressU = ss.AddressV = ss.AddressW = D3D1x_(TEXTURE_ADDRESS_CLAMP);
+ else if (sm & Sample_ClampBorder)
+ ss.AddressU = ss.AddressV = ss.AddressW = D3D1x_(TEXTURE_ADDRESS_BORDER);
+ else
+ ss.AddressU = ss.AddressV = ss.AddressW = D3D1x_(TEXTURE_ADDRESS_WRAP);
+
+ if (sm & Sample_Nearest)
+ {
+ ss.Filter = D3D1x_(FILTER_MIN_MAG_MIP_POINT);
+ }
+ else if (sm & Sample_Anisotropic)
+ {
+ ss.Filter = D3D1x_(FILTER_ANISOTROPIC);
+ ss.MaxAnisotropy = 8;
+ }
+ else
+ {
+ ss.Filter = D3D1x_(FILTER_MIN_MAG_MIP_LINEAR);
+ }
+ ss.MaxLOD = 15;
+ Device->CreateSamplerState(&ss, &SamplerStates[sm].GetRawRef());
+ return SamplerStates[sm];
+}
+
+Texture::Texture(RenderDevice* ren, int fmt, int w, int h) : Ren(ren), Tex(NULL), TexSv(NULL), TexRtv(NULL), TexDsv(NULL), Width(w), Height(h)
+{
+ OVR_UNUSED(fmt);
+ Sampler = Ren->GetSamplerState(0);
+}
+
+Texture::~Texture()
+{
+}
+
+void Texture::Set(int slot, Render::ShaderStage stage) const
+{
+ Ren->SetTexture(stage, slot, this);
+}
+
+void Texture::SetSampleMode(int sm)
+{
+ Sampler = Ren->GetSamplerState(sm);
+}
+
+void RenderDevice::SetTexture(Render::ShaderStage stage, int slot, const Texture* t)
+{
+ if (MaxTextureSet[stage] <= slot)
+ MaxTextureSet[stage] = slot + 1;
+
+ ID3D1xShaderResourceView* sv = t ? t->TexSv : NULL;
+ switch(stage)
+ {
+ case Shader_Fragment:
+ Context->PSSetShaderResources(slot, 1, &sv);
+ if (t)
+ {
+ Context->PSSetSamplers(slot, 1, &t->Sampler.GetRawRef());
+ }
+ break;
+
+ case Shader_Vertex:
+ Context->VSSetShaderResources(slot, 1, &sv);
+ break;
+ }
+}
+
+void RenderDevice::GenerateSubresourceData(
+ unsigned imageWidth, unsigned imageHeight, int format, unsigned imageDimUpperLimit,
+ const void* rawBytes, D3D1x_(SUBRESOURCE_DATA)* subresData,
+ unsigned& largestMipWidth, unsigned& largestMipHeight, unsigned& byteSize, unsigned& effectiveMipCount)
+{
+ largestMipWidth = 0;
+ largestMipHeight = 0;
+
+ unsigned sliceLen = 0;
+ unsigned rowLen = 0;
+ unsigned numRows = 0;
+ const byte* mipBytes = static_cast<const byte*>(rawBytes);
+
+ unsigned index = 0;
+ unsigned subresWidth = imageWidth;
+ unsigned subresHeight = imageHeight;
+ unsigned numMips = effectiveMipCount;
+
+ for(unsigned i = 0; i < numMips; i++)
+ {
+ unsigned bytesPerBlock = 0;
+ if (format == DXGI_FORMAT_BC1_UNORM)
+ {
+ bytesPerBlock = 8;
+ }
+ else if (format == DXGI_FORMAT_BC3_UNORM)
+ {
+ bytesPerBlock = 16;
+ }
+
+ unsigned blockWidth = 0;
+ blockWidth = (subresWidth + 3) / 4;
+ if (blockWidth < 1)
+ {
+ blockWidth = 1;
+ }
+
+ unsigned blockHeight = 0;
+ blockHeight = (subresHeight + 3) / 4;
+ if (blockHeight < 1)
+ {
+ blockHeight = 1;
+ }
+
+ rowLen = blockWidth * bytesPerBlock;
+ numRows = blockHeight;
+ sliceLen = rowLen * numRows;
+
+ if (imageDimUpperLimit == 0 || (effectiveMipCount == 1) ||
+ (subresWidth <= imageDimUpperLimit && subresHeight <= imageDimUpperLimit))
+ {
+ if(!largestMipWidth)
+ {
+ largestMipWidth = subresWidth;
+ largestMipHeight = subresHeight;
+ }
+
+ subresData[index].pSysMem = (const void*)mipBytes;
+ subresData[index].SysMemPitch = static_cast<UINT>(rowLen);
+ subresData[index].SysMemSlicePitch = static_cast<UINT>(sliceLen);
+ byteSize += sliceLen;
+ ++index;
+ }
+ else
+ {
+ effectiveMipCount--;
+ }
+
+ mipBytes += sliceLen;
+
+ subresWidth = subresWidth >> 1;
+ subresHeight = subresHeight >> 1;
+ if (subresWidth <= 0)
+ {
+ subresWidth = 1;
+ }
+ if (subresHeight <= 0)
+ {
+ subresHeight = 1;
+ }
+ }
+}
+
+#define _256Megabytes 268435456
+#define _512Megabytes 536870912
+
+Texture* RenderDevice::CreateTexture(int format, int width, int height, const void* data, int mipcount)
+{
+ UPInt gpuMemorySize = 0;
+ {
+ IDXGIDevice* pDXGIDevice;
+ Device->QueryInterface(__uuidof(IDXGIDevice), (void **)&pDXGIDevice);
+ IDXGIAdapter * pDXGIAdapter;
+ pDXGIDevice->GetAdapter(&pDXGIAdapter);
+ DXGI_ADAPTER_DESC adapterDesc;
+ pDXGIAdapter->GetDesc(&adapterDesc);
+ gpuMemorySize = adapterDesc.DedicatedVideoMemory;
+ pDXGIAdapter->Release();
+ pDXGIDevice->Release();
+ }
+
+ unsigned imageDimUpperLimit = 0;
+ if (gpuMemorySize <= _256Megabytes)
+ {
+ imageDimUpperLimit = 512;
+ }
+ else if (gpuMemorySize <= _512Megabytes)
+ {
+ imageDimUpperLimit = 1024;
+ }
+
+ if (format == Texture_DXT1 || format == Texture_DXT5)
+ {
+ int convertedFormat = (format == Texture_DXT1) ? DXGI_FORMAT_BC1_UNORM : DXGI_FORMAT_BC3_UNORM;
+ unsigned largestMipWidth = 0;
+ unsigned largestMipHeight = 0;
+ unsigned effectiveMipCount = mipcount;
+ unsigned textureSize = 0;
+
+#ifdef OVR_DEFINE_NEW
+#undef new
+#endif
+
+ D3D1x_(SUBRESOURCE_DATA)* subresData = (D3D1x_(SUBRESOURCE_DATA)*)
+ OVR_ALLOC(sizeof(D3D1x_(SUBRESOURCE_DATA)) * mipcount);
+ GenerateSubresourceData(width, height, convertedFormat, imageDimUpperLimit, data, subresData, largestMipWidth,
+ largestMipHeight, textureSize, effectiveMipCount);
+ TotalTextureMemoryUsage += textureSize;
+
+#ifdef OVR_DEFINE_NEW
+#define new OVR_DEFINE_NEW
+#endif
+
+ if (!Device || !subresData)
+ {
+ return NULL;
+ }
+ int samples = (Texture_RGBA & Texture_SamplesMask);
+ if (samples < 1)
+ {
+ samples = 1;
+ }
+
+ Texture* NewTex = new Texture(this, format, largestMipWidth, largestMipHeight);
+ NewTex->Samples = samples;
+
+ D3D1x_(TEXTURE2D_DESC) desc;
+ desc.Width = largestMipWidth;
+ desc.Height = largestMipHeight;
+ desc.MipLevels = effectiveMipCount;
+ desc.ArraySize = 1;
+ desc.Format = static_cast<DXGI_FORMAT>(convertedFormat);
+ desc.SampleDesc.Count = samples;
+ desc.SampleDesc.Quality = 0;
+ desc.Usage = D3D1x_(USAGE_DEFAULT);
+ desc.BindFlags = D3D1x_(BIND_SHADER_RESOURCE);
+ desc.CPUAccessFlags = 0;
+ desc.MiscFlags = 0;
+
+ HRESULT hr = Device->CreateTexture2D(&desc, static_cast<D3D1x_(SUBRESOURCE_DATA)*>(subresData),
+ &NewTex->Tex.GetRawRef());
+ OVR_FREE(subresData);
+
+ if (SUCCEEDED(hr) && NewTex != 0)
+ {
+ D3D1x_(SHADER_RESOURCE_VIEW_DESC) SRVDesc;
+ memset(&SRVDesc, 0, sizeof(SRVDesc));
+ SRVDesc.Format = static_cast<DXGI_FORMAT>(format);
+ SRVDesc.ViewDimension = D3D_SRV_DIMENSION_TEXTURE2D;
+ SRVDesc.Texture2D.MipLevels = desc.MipLevels;
+
+ hr = Device->CreateShaderResourceView(NewTex->Tex, NULL, &NewTex->TexSv.GetRawRef());
+
+ if (FAILED(hr))
+ {
+ NewTex->Release();
+ return NULL;
+ }
+ return NewTex;
+ }
+
+ return NULL;
+ }
+ else
+ {
+ DXGI_FORMAT d3dformat;
+ int bpp;
+ switch(format & Texture_TypeMask)
+ {
+ case Texture_RGBA:
+ bpp = 4;
+ d3dformat = DXGI_FORMAT_R8G8B8A8_UNORM;
+ break;
+ case Texture_R:
+ bpp = 1;
+ d3dformat = DXGI_FORMAT_R8_UNORM;
+ break;
+ case Texture_Depth:
+ bpp = 0;
+ d3dformat = DXGI_FORMAT_D32_FLOAT;
+ break;
+ default:
+ return NULL;
+ }
+
+ int samples = (format & Texture_SamplesMask);
+ if (samples < 1)
+ {
+ samples = 1;
+ }
+
+ Texture* NewTex = new Texture(this, format, width, height);
+ NewTex->Samples = samples;
+
+ D3D1x_(TEXTURE2D_DESC) dsDesc;
+ dsDesc.Width = width;
+ dsDesc.Height = height;
+ dsDesc.MipLevels = (format == (Texture_RGBA | Texture_GenMipmaps) && data) ? GetNumMipLevels(width, height) : 1;
+ dsDesc.ArraySize = 1;
+ dsDesc.Format = d3dformat;
+ dsDesc.SampleDesc.Count = samples;
+ dsDesc.SampleDesc.Quality = 0;
+ dsDesc.Usage = D3D1x_(USAGE_DEFAULT);
+ dsDesc.BindFlags = D3D1x_(BIND_SHADER_RESOURCE);
+ dsDesc.CPUAccessFlags = 0;
+ dsDesc.MiscFlags = 0;
+
+ if (format & Texture_RenderTarget)
+ {
+ if ((format & Texture_TypeMask) == Texture_Depth)
+ // We don't use depth textures, and creating them in d3d10 requires different options.
+ {
+ dsDesc.BindFlags = D3D1x_(BIND_DEPTH_STENCIL);
+ }
+ else
+ {
+ dsDesc.BindFlags |= D3D1x_(BIND_RENDER_TARGET);
+ }
+ }
+
+ HRESULT hr = Device->CreateTexture2D(&dsDesc, NULL, &NewTex->Tex.GetRawRef());
+ if (FAILED(hr))
+ {
+ OVR_DEBUG_LOG_TEXT(("Failed to create 2D D3D texture."));
+ NewTex->Release();
+ return NULL;
+ }
+ if (dsDesc.BindFlags & D3D1x_(BIND_SHADER_RESOURCE))
+ {
+ Device->CreateShaderResourceView(NewTex->Tex, NULL, &NewTex->TexSv.GetRawRef());
+ }
+
+ if (data)
+ {
+ Context->UpdateSubresource(NewTex->Tex, 0, NULL, data, width * bpp, width * height * bpp);
+ if (format == (Texture_RGBA | Texture_GenMipmaps))
+ {
+ int srcw = width, srch = height;
+ int level = 0;
+ UByte* mipmaps = NULL;
+ do
+ {
+ level++;
+ int mipw = srcw >> 1;
+ if (mipw < 1)
+ {
+ mipw = 1;
+ }
+ int miph = srch >> 1;
+ if (miph < 1)
+ {
+ miph = 1;
+ }
+ if (mipmaps == NULL)
+ {
+ mipmaps = (UByte*)OVR_ALLOC(mipw * miph * 4);
+ }
+ FilterRgba2x2(level == 1 ? (const UByte*)data : mipmaps, srcw, srch, mipmaps);
+ Context->UpdateSubresource(NewTex->Tex, level, NULL, mipmaps, mipw * bpp, miph * bpp);
+ srcw = mipw;
+ srch = miph;
+ }
+ while(srcw > 1 || srch > 1);
+
+ if (mipmaps != NULL)
+ {
+ OVR_FREE(mipmaps);
+ }
+ }
+ }
+
+ if (format & Texture_RenderTarget)
+ {
+ if ((format & Texture_TypeMask) == Texture_Depth)
+ {
+ Device->CreateDepthStencilView(NewTex->Tex, NULL, &NewTex->TexDsv.GetRawRef());
+ }
+ else
+ {
+ Device->CreateRenderTargetView(NewTex->Tex, NULL, &NewTex->TexRtv.GetRawRef());
+ }
+ }
+
+ return NewTex;
+ }
+}
+
+// Rendering
+
+void RenderDevice::BeginRendering()
+{
+ Context->RSSetState(Rasterizer);
+}
+
+void RenderDevice::SetRenderTarget(Render::Texture* color, Render::Texture* depth, Render::Texture* stencil)
+{
+ OVR_UNUSED(stencil);
+
+ CurRenderTarget = (Texture*)color;
+ if (color == NULL)
+ {
+ Texture* newDepthBuffer = GetDepthBuffer(WindowWidth, WindowHeight, Params.Multisample);
+ if (newDepthBuffer == NULL)
+ {
+ OVR_DEBUG_LOG(("New depth buffer creation failed."));
+ }
+ if (newDepthBuffer != NULL)
+ {
+ CurDepthBuffer = GetDepthBuffer(WindowWidth, WindowHeight, Params.Multisample);
+ Context->OMSetRenderTargets(1, &BackBufferRT.GetRawRef(), CurDepthBuffer->TexDsv);
+ }
+ return;
+ }
+ if (depth == NULL)
+ {
+ depth = GetDepthBuffer(color->GetWidth(), color->GetHeight(), CurRenderTarget->Samples);
+ }
+
+ ID3D1xShaderResourceView* sv[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+ if (MaxTextureSet[Shader_Fragment])
+ {
+ Context->PSSetShaderResources(0, MaxTextureSet[Shader_Fragment], sv);
+ }
+ memset(MaxTextureSet, 0, sizeof(MaxTextureSet));
+
+ CurDepthBuffer = (Texture*)depth;
+ Context->OMSetRenderTargets(1, &((Texture*)color)->TexRtv.GetRawRef(), ((Texture*)depth)->TexDsv);
+}
+
+
+void RenderDevice::SetWorldUniforms(const Matrix4f& proj)
+{
+ StdUniforms.Proj = proj.Transposed();
+ // Shader constant buffers cannot be partially updated.
+}
+
+
+void RenderDevice::Render(const Matrix4f& matrix, Model* model)
+{
+ // Store data in buffers if not already
+ if (!model->VertexBuffer)
+ {
+ Ptr<Buffer> vb = *CreateBuffer();
+ vb->Data(Buffer_Vertex, &model->Vertices[0], model->Vertices.GetSize() * sizeof(Vertex));
+ model->VertexBuffer = vb;
+ }
+ if (!model->IndexBuffer)
+ {
+ Ptr<Buffer> ib = *CreateBuffer();
+ ib->Data(Buffer_Index, &model->Indices[0], model->Indices.GetSize() * 2);
+ model->IndexBuffer = ib;
+ }
+
+ Render(model->Fill ? model->Fill : DefaultFill,
+ model->VertexBuffer, model->IndexBuffer,
+ matrix, 0, (unsigned)model->Indices.GetSize(), model->GetPrimType());
+}
+
+void RenderDevice::RenderWithAlpha( const Fill* fill, Render::Buffer* vertices, Render::Buffer* indices,
+ const Matrix4f& matrix, int offset, int count, PrimitiveType rprim)
+{
+ Context->OMSetBlendState(BlendState, NULL, 0xffffffff);
+ Render(fill, vertices, indices, matrix, offset, count, rprim);
+ Context->OMSetBlendState(NULL, NULL, 0xffffffff);
+}
+
+void RenderDevice::Render(const Fill* fill, Render::Buffer* vertices, Render::Buffer* indices,
+ const Matrix4f& matrix, int offset, int count, PrimitiveType rprim)
+{
+ Context->IASetInputLayout(ModelVertexIL);
+ if (indices)
+ {
+ Context->IASetIndexBuffer(((Buffer*)indices)->GetBuffer(), DXGI_FORMAT_R16_UINT, 0);
+ }
+
+ ID3D1xBuffer* vertexBuffer = ((Buffer*)vertices)->GetBuffer();
+ UINT vertexStride = sizeof(Vertex);
+ UINT vertexOffset = offset;
+ Context->IASetVertexBuffers(0, 1, &vertexBuffer, &vertexStride, &vertexOffset);
+
+ ShaderSet* shaders = ((ShaderFill*)fill)->GetShaders();
+
+ ShaderBase* vshader = ((ShaderBase*)shaders->GetShader(Shader_Vertex));
+ unsigned char* vertexData = vshader->UniformData;
+ if (vertexData)
+ {
+ StandardUniformData* stdUniforms = (StandardUniformData*) vertexData;
+ stdUniforms->View = matrix.Transposed();
+ stdUniforms->Proj = StdUniforms.Proj;
+ UniformBuffers[Shader_Vertex]->Data(Buffer_Uniform, vertexData, vshader->UniformsSize);
+ vshader->SetUniformBuffer(UniformBuffers[Shader_Vertex]);
+ }
+
+ for(int i = Shader_Vertex + 1; i < Shader_Count; i++)
+ if (shaders->GetShader(i))
+ {
+ ((ShaderBase*)shaders->GetShader(i))->UpdateBuffer(UniformBuffers[i]);
+ ((ShaderBase*)shaders->GetShader(i))->SetUniformBuffer(UniformBuffers[i]);
+ }
+
+ D3D1x_(PRIMITIVE_TOPOLOGY) prim;
+ switch(rprim)
+ {
+ case Prim_Triangles:
+ prim = D3D1x_(PRIMITIVE_TOPOLOGY_TRIANGLELIST);
+ break;
+ case Prim_Lines:
+ prim = D3D1x_(PRIMITIVE_TOPOLOGY_LINELIST);
+ break;
+ case Prim_TriangleStrip:
+ prim = D3D1x_(PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
+ break;
+ default:
+ assert(0);
+ return;
+ }
+ Context->IASetPrimitiveTopology(prim);
+
+ fill->Set(rprim);
+ if (ExtraShaders)
+ {
+ ExtraShaders->Set(rprim);
+ }
+
+ if (indices)
+ {
+ Context->DrawIndexed(count, 0, 0);
+ }
+ else
+ {
+ Context->Draw(count, 0);
+ }
+}
+
+UPInt RenderDevice::QueryGPUMemorySize()
+{
+ IDXGIDevice* pDXGIDevice;
+ Device->QueryInterface(__uuidof(IDXGIDevice), (void **)&pDXGIDevice);
+ IDXGIAdapter * pDXGIAdapter;
+ pDXGIDevice->GetAdapter(&pDXGIAdapter);
+ DXGI_ADAPTER_DESC adapterDesc;
+ pDXGIAdapter->GetDesc(&adapterDesc);
+ return adapterDesc.DedicatedVideoMemory;
+}
+
+
+void RenderDevice::Present()
+{
+ SwapChain->Present(0, 0);
+}
+
+void RenderDevice::ForceFlushGPU()
+{
+ D3D1x_QUERY_DESC queryDesc = { D3D1x_(QUERY_EVENT), 0 };
+ Ptr<ID3D1xQuery> query;
+ BOOL done = FALSE;
+
+ if (Device->CreateQuery(&queryDesc, &query.GetRawRef()) == S_OK)
+ {
+
+#if (OVR_D3D_VERSION == 10)
+ // Begin() not used for EVENT query.
+ query->End();
+ // GetData will returns S_OK for both done == TRUE or FALSE.
+ // Exit on failure to avoid infinite loop.
+ do { }
+ while(!done && !FAILED(query->GetData(&done, sizeof(BOOL), 0)));
+#else
+ Context->End(query);
+ do { }
+ while(!done && !FAILED(Context->GetData(query, &done, sizeof(BOOL), 0)));
+#endif
+
+ }
+}
+
+
+void RenderDevice::FillRect(float left, float top, float right, float bottom, Color c)
+{
+ Context->OMSetBlendState(BlendState, NULL, 0xffffffff);
+ OVR::Render::RenderDevice::FillRect(left, top, right, bottom, c);
+ Context->OMSetBlendState(NULL, NULL, 0xffffffff);
+}
+
+void RenderDevice::RenderText(const struct Font* font, const char* str, float x, float y, float size, Color c)
+{
+ Context->OMSetBlendState(BlendState, NULL, 0xffffffff);
+ OVR::Render::RenderDevice::RenderText(font, str, x, y, size, c);
+ Context->OMSetBlendState(NULL, NULL, 0xffffffff);
+}
+
+}}}
+
diff --git a/Samples/CommonSrc/Render/Render_D3D1X_Device.h b/Samples/CommonSrc/Render/Render_D3D1X_Device.h
new file mode 100644
index 0000000..c0edc28
--- /dev/null
+++ b/Samples/CommonSrc/Render/Render_D3D1X_Device.h
@@ -0,0 +1,360 @@
+/************************************************************************************
+
+Filename : Render_D3D1X_Device.h
+Content : RenderDevice implementation header for D3DX10/11.
+Created : September 10, 2012
+Authors : Andrew Reisse
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+// ***** IMPORTANT:
+// This file can be included twice, once with OVR_D3D_VERSION=10 and
+// once with OVR_D3D_VERSION=11.
+
+
+#ifndef OVR_D3D_VERSION
+#error define OVR_D3D_VERSION to 10 or 11
+#endif
+
+#include "Kernel/OVR_String.h"
+#include "Kernel/OVR_Array.h"
+
+#if (OVR_D3D_VERSION == 10 && !defined(_OVR_RENDERER_D3D10)) || \
+ (OVR_D3D_VERSION == 11 && !defined(_OVR_RENDERER_D3D11))
+
+#include "../Render/Render_Device.h"
+
+#include <Windows.h>
+
+#if (OVR_D3D_VERSION == 10)
+#define _OVR_RENDERER_D3D10
+#include <d3d10.h>
+
+namespace OVR { namespace Render { namespace D3D10 {
+
+#else // 11
+
+#define _OVR_RENDERER_D3D11
+#include <d3d11.h>
+
+namespace OVR { namespace Render { namespace D3D11 {
+#endif
+
+class RenderDevice;
+
+#ifdef D3D1x_
+#undef D3D1x_
+#endif
+#ifdef ID3D1x
+#undef ID3D1x
+#endif
+
+#if (OVR_D3D_VERSION == 10)
+typedef ID3D10Device ID3D1xDevice;
+typedef ID3D10Device ID3D1xDeviceContext;
+typedef ID3D10RenderTargetView ID3D1xRenderTargetView;
+typedef ID3D10Texture2D ID3D1xTexture2D;
+typedef ID3D10ShaderResourceView ID3D1xShaderResourceView;
+typedef ID3D10DepthStencilView ID3D1xDepthStencilView;
+typedef ID3D10DepthStencilState ID3D1xDepthStencilState;
+typedef ID3D10InputLayout ID3D1xInputLayout;
+typedef ID3D10Buffer ID3D1xBuffer;
+typedef ID3D10VertexShader ID3D1xVertexShader;
+typedef ID3D10PixelShader ID3D1xPixelShader;
+typedef ID3D10GeometryShader ID3D1xGeometryShader;
+typedef ID3D10BlendState ID3D1xBlendState;
+typedef ID3D10RasterizerState ID3D1xRasterizerState;
+typedef ID3D10SamplerState ID3D1xSamplerState;
+typedef ID3D10Query ID3D1xQuery;
+typedef ID3D10Blob ID3D1xBlob;
+typedef D3D10_VIEWPORT D3D1x_VIEWPORT;
+typedef D3D10_QUERY_DESC D3D1x_QUERY_DESC;
+#define D3D1x_(x) D3D10_##x
+#define ID3D1x(x) ID3D10##x
+
+#else // D3D 11
+typedef ID3D11Device ID3D1xDevice;
+typedef ID3D11DeviceContext ID3D1xDeviceContext;
+typedef ID3D11RenderTargetView ID3D1xRenderTargetView;
+typedef ID3D11Texture2D ID3D1xTexture2D;
+typedef ID3D11ShaderResourceView ID3D1xShaderResourceView;
+typedef ID3D11DepthStencilView ID3D1xDepthStencilView;
+typedef ID3D11DepthStencilState ID3D1xDepthStencilState;
+typedef ID3D11InputLayout ID3D1xInputLayout;
+typedef ID3D11Buffer ID3D1xBuffer;
+typedef ID3D10Blob ID3D1xBlob;
+typedef ID3D11VertexShader ID3D1xVertexShader;
+typedef ID3D11PixelShader ID3D1xPixelShader;
+typedef ID3D11GeometryShader ID3D1xGeometryShader;
+typedef ID3D11BlendState ID3D1xBlendState;
+typedef ID3D11RasterizerState ID3D1xRasterizerState;
+typedef ID3D11SamplerState ID3D1xSamplerState;
+typedef ID3D11Query ID3D1xQuery;
+typedef D3D11_VIEWPORT D3D1x_VIEWPORT;
+typedef D3D11_QUERY_DESC D3D1x_QUERY_DESC;
+#define D3D1x_(x) D3D11_##x
+#define ID3D1x(x) ID3D11##x
+#endif
+
+class Buffer;
+
+class ShaderBase : public Render::Shader
+{
+public:
+ RenderDevice* Ren;
+ unsigned char* UniformData;
+ int UniformsSize;
+
+ struct Uniform
+ {
+ String Name;
+ int Offset, Size;
+ };
+ Array<Uniform> UniformInfo;
+
+ ShaderBase(RenderDevice* r, ShaderStage stage);
+ ~ShaderBase();
+
+ void InitUniforms(ID3D10Blob* s);
+ bool SetUniform(const char* name, int n, const float* v);
+ //virtual bool UseTransposeMatrix() const { return 1; }
+
+ void UpdateBuffer(Buffer* b);
+};
+
+template<Render::ShaderStage SStage, class D3DShaderType>
+class Shader : public ShaderBase
+{
+public:
+ D3DShaderType* D3DShader;
+
+ Shader(RenderDevice* r, D3DShaderType* s) : ShaderBase(r, SStage), D3DShader(s) {}
+ Shader(RenderDevice* r, ID3D1xBlob* s) : ShaderBase(r, SStage)
+ {
+ Load(s);
+ InitUniforms(s);
+ }
+ ~Shader()
+ {
+ if(D3DShader)
+ {
+ D3DShader->Release();
+ }
+ }
+ bool Load(ID3D1xBlob* shader)
+ {
+ return Load(shader->GetBufferPointer(), shader->GetBufferSize());
+ }
+
+ // These functions have specializations.
+ bool Load(void* shader, size_t size);
+ void Set(PrimitiveType prim) const;
+ void SetUniformBuffer(Render::Buffer* buffers, int i = 0);
+};
+
+typedef Shader<Render::Shader_Vertex, ID3D1xVertexShader> VertexShader;
+typedef Shader<Render::Shader_Geometry, ID3D1xGeometryShader> GeomShader;
+typedef Shader<Render::Shader_Fragment, ID3D1xPixelShader> PixelShader;
+
+
+class Buffer : public Render::Buffer
+{
+public:
+ RenderDevice* Ren;
+ Ptr<ID3D1xBuffer> D3DBuffer;
+ size_t Size;
+ int Use;
+ bool Dynamic;
+
+public:
+ Buffer(RenderDevice* r) : Ren(r), Size(0), Use(0) {}
+ ~Buffer();
+
+ ID3D1xBuffer* GetBuffer()
+ {
+ return D3DBuffer;
+ }
+
+ virtual size_t GetSize()
+ {
+ return Size;
+ }
+ virtual void* Map(size_t start, size_t size, int flags = 0);
+ virtual bool Unmap(void *m);
+ virtual bool Data(int use, const void* buffer, size_t size);
+};
+
+class Texture : public Render::Texture
+{
+public:
+ RenderDevice* Ren;
+ Ptr<ID3D1xTexture2D> Tex;
+ Ptr<ID3D1xShaderResourceView> TexSv;
+ Ptr<ID3D1xRenderTargetView> TexRtv;
+ Ptr<ID3D1xDepthStencilView> TexDsv;
+ mutable Ptr<ID3D1xSamplerState> Sampler;
+ int Width, Height;
+ int Samples;
+
+ Texture(RenderDevice* r, int fmt, int w, int h);
+ ~Texture();
+
+ virtual int GetWidth() const
+ {
+ return Width;
+ }
+ virtual int GetHeight() const
+ {
+ return Height;
+ }
+ virtual int GetSamples() const
+ {
+ return Samples;
+ }
+
+ virtual void SetSampleMode(int sm);
+
+ virtual void Set(int slot, Render::ShaderStage stage = Render::Shader_Fragment) const;
+};
+
+class RenderDevice : public Render::RenderDevice
+{
+public:
+ Ptr<IDXGIFactory> DXGIFactory;
+ HWND Window;
+
+ Ptr<ID3D1xDevice> Device;
+ Ptr<ID3D1xDeviceContext> Context;
+ Ptr<IDXGISwapChain> SwapChain;
+ Ptr<IDXGIAdapter> Adapter;
+ Ptr<IDXGIOutput> FullscreenOutput;
+ int FSDesktopX, FSDesktopY;
+ int PreFullscreenX, PreFullscreenY, PreFullscreenW, PreFullscreenH;
+
+ Ptr<ID3D1xTexture2D> BackBuffer;
+ Ptr<ID3D1xRenderTargetView> BackBufferRT;
+ Ptr<Texture> CurRenderTarget;
+ Ptr<Texture> CurDepthBuffer;
+ Ptr<ID3D1xRasterizerState> Rasterizer;
+ Ptr<ID3D1xBlendState> BlendState;
+ int NumViewports;
+ D3D1x_VIEWPORT Viewports[2];
+
+ Ptr<ID3D1xDepthStencilState> DepthStates[1 + 2 * Compare_Count];
+ Ptr<ID3D1xDepthStencilState> CurDepthState;
+ Ptr<ID3D1xInputLayout> ModelVertexIL;
+
+ Ptr<ID3D1xSamplerState> SamplerStates[Sample_Count];
+
+ struct StandardUniformData
+ {
+ Matrix4f Proj;
+ Matrix4f View;
+ } StdUniforms;
+ Ptr<Buffer> UniformBuffers[Shader_Count];
+ int MaxTextureSet[Shader_Count];
+
+ Ptr<VertexShader> VertexShaders[VShader_Count];
+ Ptr<PixelShader> PixelShaders[FShader_Count];
+ Ptr<GeomShader> pStereoShaders[Prim_Count];
+ Ptr<Buffer> CommonUniforms[8];
+ Ptr<ShaderSet> ExtraShaders;
+ Ptr<ShaderFill> DefaultFill;
+
+ Ptr<Buffer> QuadVertexBuffer;
+
+ Array<Ptr<Texture> > DepthBuffers;
+
+public:
+ RenderDevice(const RendererParams& p, HWND window);
+ ~RenderDevice();
+
+ // Implement static initializer function to create this class.
+ static Render::RenderDevice* CreateDevice(const RendererParams& rp, void* oswnd);
+
+ // if needRecreate == true it will recreate DXGIFactory and Adapter
+ // to get the latest info about monitors (including just connected/
+ // disconnected ones). Note, SwapChain will be released in this case
+ // and it should be recreated.
+ void UpdateMonitorOutputs(bool needRecreate = false);
+
+ virtual void SetMultipleViewports(int n, const Viewport* vps);
+ virtual void SetWindowSize(int w, int h);
+ virtual bool SetParams(const RendererParams& newParams);
+ //virtual void SetScissor(int x, int y, int w, int h);
+
+ virtual void Present();
+ virtual void ForceFlushGPU();
+
+ virtual bool SetFullscreen(DisplayMode fullscreen);
+ virtual UPInt QueryGPUMemorySize();
+
+ virtual void Clear(float r = 0, float g = 0, float b = 0, float a = 1, float depth = 1);
+ virtual void Rect(float left, float top, float right, float bottom)
+ {
+ OVR_UNUSED4(left, top, right, bottom);
+ }
+
+ virtual Buffer* CreateBuffer();
+ virtual Texture* CreateTexture(int format, int width, int height, const void* data, int mipcount=1);
+
+ static void GenerateSubresourceData(
+ unsigned imageWidth, unsigned imageHeight, int format, unsigned imageDimUpperLimit,
+ const void* rawBytes,
+ D3D1x_(SUBRESOURCE_DATA)* subresData,
+ unsigned& largestMipWidth, unsigned& largestMipHeight, unsigned& byteSize, unsigned& effectiveMipCount);
+
+ Texture* GetDepthBuffer(int w, int h, int ms);
+
+ virtual void BeginRendering();
+ virtual void SetRenderTarget(Render::Texture* color,
+ Render::Texture* depth = NULL, Render::Texture* stencil = NULL);
+ virtual void SetDepthMode(bool enable, bool write, CompareFunc func = Compare_Less);
+ virtual void SetWorldUniforms(const Matrix4f& proj);
+ virtual void SetCommonUniformBuffer(int i, Render::Buffer* buffer);
+ virtual void SetExtraShaders(ShaderSet* s)
+ {
+ ExtraShaders = s;
+ }
+
+
+ // Overrident to apply proper blend state.
+ virtual void FillRect(float left, float top, float right, float bottom, Color c);
+ virtual void RenderText(const struct Font* font, const char* str, float x, float y, float size, Color c);
+
+ virtual void Render(const Matrix4f& matrix, Model* model);
+ virtual void Render(const Fill* fill, Render::Buffer* vertices, Render::Buffer* indices,
+ const Matrix4f& matrix, int offset, int count, PrimitiveType prim = Prim_Triangles);
+ virtual void RenderWithAlpha( const Fill* fill, Render::Buffer* vertices, Render::Buffer* indices,
+ const Matrix4f& matrix, int offset, int count, PrimitiveType prim = Prim_Triangles);
+
+ virtual Fill *CreateSimpleFill(int flags = Fill::F_Solid);
+
+ virtual Render::Shader *LoadBuiltinShader(ShaderStage stage, int shader);
+
+ bool RecreateSwapChain();
+ virtual ID3D10Blob* CompileShader(const char* profile, const char* src, const char* mainName = "main");
+ virtual ShaderBase* CreateStereoShader(PrimitiveType prim, Render::Shader* vs);
+
+ ID3D1xSamplerState* GetSamplerState(int sm);
+
+ void SetTexture(Render::ShaderStage stage, int slot, const Texture* t);
+};
+
+}}}
+
+#endif
diff --git a/Samples/CommonSrc/Render/Render_Device.cpp b/Samples/CommonSrc/Render/Render_Device.cpp
new file mode 100644
index 0000000..d1687d5
--- /dev/null
+++ b/Samples/CommonSrc/Render/Render_Device.cpp
@@ -0,0 +1,1033 @@
+/************************************************************************************
+
+Filename : Render_Device.cpp
+Content : Platform renderer for simple scene graph - implementation
+Created : September 6, 2012
+Authors : Andrew Reisse
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#include "../Render/Render_Device.h"
+#include "../Render/Render_Font.h"
+
+#include "Kernel/OVR_Log.h"
+
+namespace OVR { namespace Render {
+
+void Model::Render(const Matrix4f& ltw, RenderDevice* ren)
+{
+ if(Visible)
+ {
+ Matrix4f m = ltw * GetMatrix();
+ ren->Render(m, this);
+ }
+}
+
+void Container::Render(const Matrix4f& ltw, RenderDevice* ren)
+{
+ Matrix4f m = ltw * GetMatrix();
+ for(unsigned i = 0; i < Nodes.GetSize(); i++)
+ {
+ Nodes[i]->Render(m, ren);
+ }
+}
+
+Matrix4f SceneView::GetViewMatrix() const
+{
+ Matrix4f view = Matrix4f(GetOrientation().Conj()) * Matrix4f::Translation(GetPosition());
+ return view;
+}
+
+void LightingParams::Update(const Matrix4f& view, const Vector4f* SceneLightPos)
+{
+ Version++;
+ for (int i = 0; i < LightCount; i++)
+ {
+ LightPos[i] = view.Transform(SceneLightPos[i]);
+ }
+}
+
+void Scene::Render(RenderDevice* ren, const Matrix4f& view)
+{
+ Lighting.Update(view, LightPos);
+
+ ren->SetLighting(&Lighting);
+
+ World.Render(view, ren);
+}
+
+
+
+UInt16 CubeIndices[] =
+{
+ 0, 1, 3,
+ 3, 1, 2,
+
+ 5, 4, 6,
+ 6, 4, 7,
+
+ 8, 9, 11,
+ 11, 9, 10,
+
+ 13, 12, 14,
+ 14, 12, 15,
+
+ 16, 17, 19,
+ 19, 17, 18,
+
+ 21, 20, 22,
+ 22, 20, 23
+};
+
+// Colors are specified for planes perpendicular to the axis
+// For example, "xColor" is the color of the y-z plane
+Model* Model::CreateAxisFaceColorBox(float x1, float x2, Color xcolor,
+ float y1, float y2, Color ycolor,
+ float z1, float z2, Color zcolor)
+{
+ float t;
+
+ if(x1 > x2)
+ {
+ t = x1;
+ x1 = x2;
+ x2 = t;
+ }
+ if(y1 > y2)
+ {
+ t = y1;
+ y1 = y2;
+ y2 = t;
+ }
+ if(z1 > z2)
+ {
+ t = z1;
+ z1 = z2;
+ z2 = t;
+ }
+
+ Model* box = new Model();
+
+ UInt16 startIndex = 0;
+ // Cube
+ startIndex =
+ box->AddVertex(Vector3f(x1, y2, z1), ycolor);
+ box->AddVertex(Vector3f(x2, y2, z1), ycolor);
+ box->AddVertex(Vector3f(x2, y2, z2), ycolor);
+ box->AddVertex(Vector3f(x1, y2, z2), ycolor);
+
+ box->AddVertex(Vector3f(x1, y1, z1), ycolor);
+ box->AddVertex(Vector3f(x2, y1, z1), ycolor);
+ box->AddVertex(Vector3f(x2, y1, z2), ycolor);
+ box->AddVertex(Vector3f(x1, y1, z2), ycolor);
+
+ box->AddVertex(Vector3f(x1, y1, z2), xcolor);
+ box->AddVertex(Vector3f(x1, y1, z1), xcolor);
+ box->AddVertex(Vector3f(x1, y2, z1), xcolor);
+ box->AddVertex(Vector3f(x1, y2, z2), xcolor);
+
+ box->AddVertex(Vector3f(x2, y1, z2), xcolor);
+ box->AddVertex(Vector3f(x2, y1, z1), xcolor);
+ box->AddVertex(Vector3f(x2, y2, z1), xcolor);
+ box->AddVertex(Vector3f(x2, y2, z2), xcolor);
+
+ box->AddVertex(Vector3f(x1, y1, z1), zcolor);
+ box->AddVertex(Vector3f(x2, y1, z1), zcolor);
+ box->AddVertex(Vector3f(x2, y2, z1), zcolor);
+ box->AddVertex(Vector3f(x1, y2, z1), zcolor);
+
+ box->AddVertex(Vector3f(x1, y1, z2), zcolor);
+ box->AddVertex(Vector3f(x2, y1, z2), zcolor);
+ box->AddVertex(Vector3f(x2, y2, z2), zcolor);
+ box->AddVertex(Vector3f(x1, y2, z2), zcolor);
+
+
+ enum
+ {
+ // CubeVertexCount = sizeof(CubeVertices)/sizeof(CubeVertices[0]),
+ CubeIndexCount = sizeof(CubeIndices) / sizeof(CubeIndices[0])
+ };
+
+ // Renumber indices
+ for(int i = 0; i < CubeIndexCount / 3; i++)
+ {
+ box->AddTriangle(CubeIndices[i * 3] + startIndex,
+ CubeIndices[i * 3 + 1] + startIndex,
+ CubeIndices[i * 3 + 2] + startIndex);
+ }
+
+ return box;
+}
+
+void Model::AddSolidColorBox(float x1, float y1, float z1,
+ float x2, float y2, float z2,
+ Color c)
+{
+ float t;
+
+ if(x1 > x2)
+ {
+ t = x1;
+ x1 = x2;
+ x2 = t;
+ }
+ if(y1 > y2)
+ {
+ t = y1;
+ y1 = y2;
+ y2 = t;
+ }
+ if(z1 > z2)
+ {
+ t = z1;
+ z1 = z2;
+ z2 = t;
+ }
+
+ // Cube vertices and their normals.
+ Vector3f CubeVertices[][3] =
+ {
+ Vector3f(x1, y2, z1), Vector3f(z1, x1), Vector3f(0.0f, 1.0f, 0.0f),
+ Vector3f(x2, y2, z1), Vector3f(z1, x2), Vector3f(0.0f, 1.0f, 0.0f),
+ Vector3f(x2, y2, z2), Vector3f(z2, x2), Vector3f(0.0f, 1.0f, 0.0f),
+ Vector3f(x1, y2, z2), Vector3f(z2, x1), Vector3f(0.0f, 1.0f, 0.0f),
+
+ Vector3f(x1, y1, z1), Vector3f(z1, x1), Vector3f(0.0f, -1.0f, 0.0f),
+ Vector3f(x2, y1, z1), Vector3f(z1, x2), Vector3f(0.0f, -1.0f, 0.0f),
+ Vector3f(x2, y1, z2), Vector3f(z2, x2), Vector3f(0.0f, -1.0f, 0.0f),
+ Vector3f(x1, y1, z2), Vector3f(z2, x1), Vector3f(0.0f, -1.0f, 0.0f),
+
+ Vector3f(x1, y1, z2), Vector3f(z2, y1), Vector3f(-1.0f, 0.0f, 0.0f),
+ Vector3f(x1, y1, z1), Vector3f(z1, y1), Vector3f(-1.0f, 0.0f, 0.0f),
+ Vector3f(x1, y2, z1), Vector3f(z1, y2), Vector3f(-1.0f, 0.0f, 0.0f),
+ Vector3f(x1, y2, z2), Vector3f(z2, y2), Vector3f(-1.0f, 0.0f, 0.0f),
+
+ Vector3f(x2, y1, z2), Vector3f(z2, y1), Vector3f(1.0f, 0.0f, 0.0f),
+ Vector3f(x2, y1, z1), Vector3f(z1, y1), Vector3f(1.0f, 0.0f, 0.0f),
+ Vector3f(x2, y2, z1), Vector3f(z1, y2), Vector3f(1.0f, 0.0f, 0.0f),
+ Vector3f(x2, y2, z2), Vector3f(z2, y2), Vector3f(1.0f, 0.0f, 0.0f),
+
+ Vector3f(x1, y1, z1), Vector3f(x1, y1), Vector3f(0.0f, 0.0f, -1.0f),
+ Vector3f(x2, y1, z1), Vector3f(x2, y1), Vector3f(0.0f, 0.0f, -1.0f),
+ Vector3f(x2, y2, z1), Vector3f(x2, y2), Vector3f(0.0f, 0.0f, -1.0f),
+ Vector3f(x1, y2, z1), Vector3f(x1, y2), Vector3f(0.0f, 0.0f, -1.0f),
+
+ Vector3f(x1, y1, z2), Vector3f(x1, y1), Vector3f(0.0f, 0.0f, 1.0f),
+ Vector3f(x2, y1, z2), Vector3f(x2, y1), Vector3f(0.0f, 0.0f, 1.0f),
+ Vector3f(x2, y2, z2), Vector3f(x2, y2), Vector3f(0.0f, 0.0f, 1.0f),
+ Vector3f(x1, y2, z2), Vector3f(x1, y2), Vector3f(0.0f, 0.0f, 1.0f)
+ };
+
+
+ UInt16 startIndex = GetNextVertexIndex();
+
+ enum
+ {
+ CubeVertexCount = sizeof(CubeVertices) / sizeof(CubeVertices[0]),
+ CubeIndexCount = sizeof(CubeIndices) / sizeof(CubeIndices[0])
+ };
+
+ for(int v = 0; v < CubeVertexCount; v++)
+ {
+ AddVertex(Vertex(CubeVertices[v][0], c, CubeVertices[v][1].x, CubeVertices[v][1].y, CubeVertices[v][2]));
+ }
+
+ // Renumber indices
+ for(int i = 0; i < CubeIndexCount / 3; i++)
+ {
+ AddTriangle(CubeIndices[i * 3] + startIndex,
+ CubeIndices[i * 3 + 1] + startIndex,
+ CubeIndices[i * 3 + 2] + startIndex);
+ }
+}
+
+
+
+Model* Model::CreateBox(Color c, Vector3f origin, Vector3f size)
+{
+ Model *box = new Model();
+ Vector3f s = size * 0.5f;
+
+ box->AddVertex(-s.x, s.y, -s.z, c, 0, 1, 0, 0, -1);
+ box->AddVertex(s.x, s.y, -s.z, c, 1, 1, 0, 0, -1);
+ box->AddVertex(s.x, -s.y, -s.z, c, 1, 0, 0, 0, -1);
+ box->AddVertex(-s.x, -s.y, -s.z, c, 0, 0, 0, 0, -1);
+ box->AddTriangle(2, 1, 0);
+ box->AddTriangle(0, 3, 2);
+
+ box->AddVertex(s.x, s.y, s.z, c, 1, 1, 0, 0, 1);
+ box->AddVertex(-s.x, s.y, s.z, c, 0, 1, 0, 0, 1);
+ box->AddVertex(-s.x, -s.y, s.z, c, 0, 0, 0, 0, 1);
+ box->AddVertex(s.x, -s.y, s.z, c, 1, 0, 0, 0, 1);
+ box->AddTriangle(6, 5, 4);
+ box->AddTriangle(4, 7, 6);
+
+ box->AddVertex(-s.x, s.y, -s.z, c, 1, 0, -1, 0, 0);
+ box->AddVertex(-s.x, s.y, s.z, c, 1, 1, -1, 0, 0);
+ box->AddVertex(-s.x, -s.y, s.z, c, 0, 1, -1, 0, 0);
+ box->AddVertex(-s.x, -s.y, -s.z, c, 0, 0, -1, 0, 0);
+ box->AddTriangle(10, 11, 8);
+ box->AddTriangle(8, 9, 10);
+
+ box->AddVertex(s.x, s.y, -s.z, c, 1, 0, 1, 0, 0);
+ box->AddVertex(s.x, -s.y, -s.z, c, 0, 0, 1, 0, 0);
+ box->AddVertex(s.x, -s.y, s.z, c, 0, 1, 1, 0, 0);
+ box->AddVertex(s.x, s.y, s.z, c, 1, 1, 1, 0, 0);
+ box->AddTriangle(14, 15, 12);
+ box->AddTriangle(12, 13, 14);
+
+ box->AddVertex(-s.x, -s.y, s.z, c, 0, 1, 0, -1, 0);
+ box->AddVertex(s.x, -s.y, s.z, c, 1, 1, 0, -1, 0);
+ box->AddVertex(s.x, -s.y, -s.z, c, 1, 0, 0, -1, 0);
+ box->AddVertex(-s.x, -s.y, -s.z, c, 0, 0, 0, -1, 0);
+ box->AddTriangle(18, 19, 16);
+ box->AddTriangle(16, 17, 18);
+
+ box->AddVertex(-s.x, s.y, -s.z, c, 0, 0, 0, 1, 0);
+ box->AddVertex(s.x, s.y, -s.z, c, 1, 0, 0, 1, 0);
+ box->AddVertex(s.x, s.y, s.z, c, 1, 1, 0, 1, 0);
+ box->AddVertex(-s.x, s.y, s.z, c, 0, 1, 0, 1, 0);
+ box->AddTriangle(20, 21, 22);
+ box->AddTriangle(22, 23, 20);
+
+ box->SetPosition(origin);
+ return box;
+}
+
+// Triangulation of a cylinder centered at the origin
+Model* Model::CreateCylinder(Color color, Vector3f origin, float height, float radius, int sides)
+{
+ Model *cyl = new Model();
+ float halfht = height * 0.5f;
+ for(UInt16 i = 0; i < sides; i++)
+ {
+ float x = cosf(Math<float>::TwoPi * i / float(sides));
+ float y = sinf(Math<float>::TwoPi * i / float(sides));
+
+ cyl->AddVertex(radius * x, radius * y, halfht, color, x + 1, y, 0, 0, 1);
+ cyl->AddVertex(radius * x, radius * y, -1.0f*halfht, color, x, y, 0, 0, -1);
+
+ UInt16 j = 0;
+ if(i < sides - 1)
+ {
+ j = i + 1;
+ cyl->AddTriangle(0, i * 4 + 4, i * 4);
+ cyl->AddTriangle(1, i * 4 + 1, i * 4 + 5);
+ }
+
+ float nx = cosf(Math<float>::Pi * (0.5f + 2.0f * i / float(sides)));
+ float ny = sinf(Math<float>::Pi * (0.5f + 2.0f * i / float(sides)));
+ cyl->AddVertex(radius * x, radius * y, halfht, color, x + 1, y, nx, ny, 0);
+ cyl->AddVertex(radius * x, radius * y, -1.0f*halfht, color, x, y, nx, ny, 0);
+
+ cyl->AddTriangle(i * 4 + 2, j * 4 + 2, i * 4 + 3);
+ cyl->AddTriangle(i * 4 + 3, j * 4 + 2, j * 4 + 3);
+ }
+ cyl->SetPosition(origin);
+ return cyl;
+};
+
+//Triangulation of a cone centered at the origin
+Model* Model::CreateCone(Color color, Vector3f origin, float height, float radius, int sides)
+{
+ Model *cone = new Model();
+ float halfht = height * 0.5f;
+ cone->AddVertex(0.0f, 0.0f, -1.0f*halfht, color, 0, 0, 0, 0, -1);
+
+ for(UInt16 i = 0; i < sides; i++)
+ {
+ float x = cosf(Math<float>::TwoPi * i / float(sides));
+ float y = sinf(Math<float>::TwoPi * i / float(sides));
+
+ cone->AddVertex(radius * x, radius * y, -1.0f*halfht, color, 0, 0, 0, 0, -1);
+
+ UInt16 j = 1;
+ if(i < sides - 1)
+ {
+ j = i + 1;
+ }
+
+ float next_x = cosf(Math<float>::TwoPi * j / float(sides));
+ float next_y = sinf(Math<float>::TwoPi * j / float(sides));
+
+ Vector3f normal = Vector3f(x, y, -halfht).Cross(Vector3f(next_x, next_y, -halfht));
+
+ cone->AddVertex(0.0f, 0.0f, halfht, color, 1, 0, normal.x, normal.y, normal.z);
+ cone->AddVertex(radius * x, radius * y, -1.0f*halfht, color, 0, 0, normal.x, normal.y, normal.z);
+
+ cone->AddTriangle(0, 3*i + 1, 3*j + 1);
+ cone->AddTriangle(3*i + 2, 3*j + 3, 3*i + 3);
+ }
+ cone->SetPosition(origin);
+ return cone;
+};
+
+//Triangulation of a sphere centered at the origin
+Model* Model::CreateSphere(Color color, Vector3f origin, float radius, int sides)
+{
+ Model *sphere = new Model();
+ UInt16 usides = (UInt16) sides;
+ UInt16 halfsides = usides/2;
+
+ for(UInt16 k = 0; k < halfsides; k++) {
+
+ float z = cosf(Math<float>::Pi * k / float(halfsides));
+ float z_r = sinf(Math<float>::Pi * k / float(halfsides)); // the radius of the cross circle with coordinate z
+
+ if (k == 0)
+ { // add north and south poles
+ sphere->AddVertex(0.0f, 0.0f, radius, color, 0, 0, 0, 0, 1);
+ sphere->AddVertex(0.0f, 0.0f, -radius, color, 1, 1, 0, 0, -1);
+ }
+ else
+ {
+ for(UInt16 i = 0; i < sides; i++)
+ {
+ float x = cosf(Math<float>::TwoPi * i / float(sides)) * z_r;
+ float y = sinf(Math<float>::TwoPi * i / float(sides)) * z_r;
+
+ UInt16 j = 0;
+ if(i < sides - 1)
+ {
+ j = i + 1;
+ }
+
+ sphere->AddVertex(radius * x, radius * y, radius * z, color, 0, 1, x, y, z);
+
+ UInt16 indi = 2 + (k -1)*usides + i;
+ UInt16 indj = 2 + (k -1)*usides + j;
+ if (k == 1) // NorthPole
+ sphere->AddTriangle(0, j + 2, i + 2);
+ else if (k == halfsides - 1) //SouthPole
+ {
+ sphere->AddTriangle(1, indi, indj);
+ sphere->AddTriangle(indi, indi - usides, indj);
+ sphere->AddTriangle(indi - usides, indj - usides, indj);
+ }
+ else
+ {
+ sphere->AddTriangle(indi, indi - usides, indj);
+ sphere->AddTriangle(indi - usides, indj - usides, indj);
+ }
+ }
+ } // end else
+ }
+ sphere->SetPosition(origin);
+ return sphere;
+};
+
+Model* Model::CreateGrid(Vector3f origin, Vector3f stepx, Vector3f stepy,
+ int halfx, int halfy, int nmajor, Color minor, Color major)
+{
+ Model* grid = new Model(Prim_Lines);
+ float halfxf = (float)halfx;
+ float halfyf = (float)halfy;
+
+ for(int jn = 0; jn <= halfy; jn++)
+ {
+ float j = (float)jn;
+
+ grid->AddLine(grid->AddVertex((stepx * -halfxf) + (stepy * j), (jn % nmajor) ? minor : major, 0, 0.5f),
+ grid->AddVertex((stepx * halfxf) + (stepy * j), (jn % nmajor) ? minor : major, 1, 0.5f));
+
+ if(j)
+ grid->AddLine(grid->AddVertex((stepx * -halfxf) + (stepy * -j), (jn % nmajor) ? minor : major, 0, 0.5f),
+ grid->AddVertex((stepx * halfxf) + (stepy * -j), (jn % nmajor) ? minor : major, 1, 0.5f));
+ }
+
+ for(int in = 0; in <= halfx; in++)
+ {
+ float i = (float)in;
+
+ grid->AddLine(grid->AddVertex((stepx * i) + (stepy * -halfyf), (in % nmajor) ? minor : major, 0, 0.5f),
+ grid->AddVertex((stepx * i) + (stepy * halfyf), (in % nmajor) ? minor : major, 1, 0.5f));
+
+ if(i)
+ grid->AddLine(grid->AddVertex((stepx * -i) + (stepy * -halfyf), (in % nmajor) ? minor : major, 0, 0.5f),
+ grid->AddVertex((stepx * -i) + (stepy * halfyf), (in % nmajor) ? minor : major, 1, 0.5f));
+ }
+
+ grid->SetPosition(origin);
+ return grid;
+}
+
+
+//-------------------------------------------------------------------------------------
+
+
+void ShaderFill::Set(PrimitiveType prim) const
+{
+ Shaders->Set(prim);
+ for(int i = 0; i < 8; i++)
+ if(Textures[i])
+ {
+ Textures[i]->Set(i);
+ }
+}
+
+
+
+//-------------------------------------------------------------------------------------
+// ***** Rendering
+
+
+RenderDevice::RenderDevice()
+ : CurPostProcess(PostProcess_None),
+ SceneColorTexW(0), SceneColorTexH(0),
+ SceneRenderScale(1),
+
+ Distortion(1.0f, 0.18f, 0.115f),
+ DistortionClearColor(0, 0, 0),
+ PostProcessShaderActive(PostProcessShader_DistortionAndChromAb),
+ TotalTextureMemoryUsage(0)
+{
+ PostProcessShaderRequested = PostProcessShaderActive;
+}
+
+Fill* RenderDevice::CreateTextureFill(Render::Texture* t, bool useAlpha)
+{
+ ShaderSet* shaders = CreateShaderSet();
+ shaders->SetShader(LoadBuiltinShader(Shader_Vertex, VShader_MVP));
+ shaders->SetShader(LoadBuiltinShader(Shader_Fragment, useAlpha ? FShader_AlphaTexture : FShader_Texture));
+ Fill* f = new ShaderFill(*shaders);
+ f->SetTexture(0, t);
+ return f;
+}
+
+void LightingParams::Set(ShaderSet* s) const
+{
+ s->SetUniform4fv("Ambient", 1, &Ambient);
+ s->SetUniform1f("LightCount", LightCount);
+ s->SetUniform4fv("LightPos", (int)LightCount, LightPos);
+ s->SetUniform4fv("LightColor", (int)LightCount, LightColor);
+}
+
+void RenderDevice::SetLighting(const LightingParams* lt)
+{
+ if (!LightingBuffer)
+ LightingBuffer = *CreateBuffer();
+
+ LightingBuffer->Data(Buffer_Uniform, lt, sizeof(LightingParams));
+ SetCommonUniformBuffer(1, LightingBuffer);
+}
+
+float RenderDevice::MeasureText(const Font* font, const char* str, float size, float* strsize)
+{
+ UPInt length = strlen(str);
+ float w = 0;
+ float xp = 0;
+ float yp = 0;
+
+ for (UPInt i = 0; i < length; i++)
+ {
+ if(str[i] == '\n')
+ {
+ yp += font->lineheight;
+ if(xp > w)
+ {
+ w = xp;
+ }
+ xp = 0;
+ continue;
+ }
+
+ // Tab followed by a numbers sets position to specified offset.
+ if(str[i] == '\t')
+ {
+ char *p = 0;
+ float tabPixels = (float)OVR_strtoq(str + i + 1, &p, 10);
+ i += p - (str + i + 1);
+ xp = tabPixels;
+ }
+ else
+ {
+ const Font::Char* ch = &font->chars[str[i]];
+ xp += ch->advance;
+ }
+ }
+
+ if(xp > w)
+ {
+ w = xp;
+ }
+
+ if(strsize)
+ {
+ strsize[0] = (size / font->lineheight) * w;
+ strsize[1] = (size / font->lineheight) * (yp + font->lineheight);
+ }
+ return (size / font->lineheight) * w;
+}
+
+void RenderDevice::RenderText(const Font* font, const char* str,
+ float x, float y, float size, Color c)
+{
+ if(!pTextVertexBuffer)
+ {
+ pTextVertexBuffer = *CreateBuffer();
+ if(!pTextVertexBuffer)
+ {
+ return;
+ }
+ }
+
+ if(!font->fill)
+ {
+ font->fill = CreateTextureFill(Ptr<Texture>(
+ *CreateTexture(Texture_R, font->twidth, font->theight, font->tex)), true);
+ }
+
+ UPInt length = strlen(str);
+
+ pTextVertexBuffer->Data(Buffer_Vertex, NULL, length * 6 * sizeof(Vertex));
+ Vertex* vertices = (Vertex*)pTextVertexBuffer->Map(0, length * 6 * sizeof(Vertex), Map_Discard);
+ if(!vertices)
+ {
+ return;
+ }
+
+ Matrix4f m = Matrix4f(size / font->lineheight, 0, 0, 0,
+ 0, size / font->lineheight, 0, 0,
+ 0, 0, 0, 0,
+ x, y, 0, 1).Transposed();
+
+ float xp = 0, yp = (float)font->ascent;
+ int ivertex = 0;
+
+ for (UPInt i = 0; i < length; i++)
+ {
+ if(str[i] == '\n')
+ {
+ yp += font->lineheight;
+ xp = 0;
+ continue;
+ }
+ // Tab followed by a numbers sets position to specified offset.
+ if(str[i] == '\t')
+ {
+ char *p = 0;
+ float tabPixels = (float)OVR_strtoq(str + i + 1, &p, 10);
+ i += p - (str + i + 1);
+ xp = tabPixels;
+ continue;
+ }
+
+ const Font::Char* ch = &font->chars[str[i]];
+ Vertex* chv = &vertices[ivertex];
+ for(int j = 0; j < 6; j++)
+ {
+ chv[j].C = c;
+ }
+ float x = xp + ch->x;
+ float y = yp - ch->y;
+ float cx = font->twidth * (ch->u2 - ch->u1);
+ float cy = font->theight * (ch->v2 - ch->v1);
+ chv[0] = Vertex(Vector3f(x, y, 0), c, ch->u1, ch->v1);
+ chv[1] = Vertex(Vector3f(x + cx, y, 0), c, ch->u2, ch->v1);
+ chv[2] = Vertex(Vector3f(x + cx, cy + y, 0), c, ch->u2, ch->v2);
+ chv[3] = Vertex(Vector3f(x, y, 0), c, ch->u1, ch->v1);
+ chv[4] = Vertex(Vector3f(x + cx, cy + y, 0), c, ch->u2, ch->v2);
+ chv[5] = Vertex(Vector3f(x, y + cy, 0), c, ch->u1, ch->v2);
+ ivertex += 6;
+
+ xp += ch->advance;
+ }
+
+ pTextVertexBuffer->Unmap(vertices);
+
+ Render(font->fill, pTextVertexBuffer, NULL, m, 0, ivertex, Prim_Triangles);
+}
+
+void RenderDevice::FillRect(float left, float top, float right, float bottom, Color c)
+{
+ if(!pTextVertexBuffer)
+ {
+ pTextVertexBuffer = *CreateBuffer();
+ if(!pTextVertexBuffer)
+ {
+ return;
+ }
+ }
+
+ // Get!!
+ Fill* fill = CreateSimpleFill();
+
+ pTextVertexBuffer->Data(Buffer_Vertex, NULL, 6 * sizeof(Vertex));
+ Vertex* vertices = (Vertex*)pTextVertexBuffer->Map(0, 6 * sizeof(Vertex), Map_Discard);
+ if(!vertices)
+ {
+ return;
+ }
+
+ vertices[0] = Vertex(Vector3f(left, top, 0.0f), c);
+ vertices[1] = Vertex(Vector3f(right, top, 0), c);
+ vertices[2] = Vertex(Vector3f(left, bottom, 0), c);
+ vertices[3] = Vertex(Vector3f(left, bottom, 0), c);
+ vertices[4] = Vertex(Vector3f(right, top, 0), c);
+ vertices[5] = Vertex(Vector3f(right, bottom, 0), c);
+
+ pTextVertexBuffer->Unmap(vertices);
+
+ Render(fill, pTextVertexBuffer, NULL, Matrix4f(), 0, 6, Prim_Triangles);
+}
+
+/*
+void RenderDevice::GetStereoViewports(const Viewport& in, Viewport* out) const
+{
+ out[0] = out[1] = in;
+ if (StereoBufferMode == Stereo_SplitH)
+ {
+ out[0].w = out[1].w = in.w / 2;
+ out[1].x += WindowWidth / 2;
+ }
+}
+*/
+
+void RenderDevice::SetSceneRenderScale(float ss)
+{
+ SceneRenderScale = ss;
+ pSceneColorTex = NULL;
+}
+
+void RenderDevice::SetViewport(const Viewport& vp)
+{
+ VP = vp;
+
+ if(CurPostProcess == PostProcess_Distortion)
+ {
+ Viewport svp = vp;
+ svp.w = (int)ceil(SceneRenderScale * vp.w);
+ svp.h = (int)ceil(SceneRenderScale * vp.h);
+ svp.x = (int)ceil(SceneRenderScale * vp.x);
+ svp.y = (int)ceil(SceneRenderScale * vp.y);
+ SetRealViewport(svp);
+ }
+ else
+ {
+ SetRealViewport(vp);
+ }
+}
+
+
+bool RenderDevice::initPostProcessSupport(PostProcessType pptype)
+{
+ if(pptype != PostProcess_Distortion)
+ {
+ return true;
+ }
+
+
+ if (PostProcessShaderRequested != PostProcessShaderActive)
+ {
+ pPostProcessShader.Clear();
+ PostProcessShaderActive = PostProcessShaderRequested;
+ }
+
+ if (!pPostProcessShader)
+ {
+ Shader *vs = LoadBuiltinShader(Shader_Vertex, VShader_PostProcess);
+
+ Shader *ppfs = NULL;
+
+ if (PostProcessShaderActive == PostProcessShader_Distortion)
+ {
+ ppfs = LoadBuiltinShader(Shader_Fragment, FShader_PostProcess);
+ }
+ else if (PostProcessShaderActive == PostProcessShader_DistortionAndChromAb)
+ {
+ ppfs = LoadBuiltinShader(Shader_Fragment, FShader_PostProcessWithChromAb);
+ }
+ else
+ OVR_ASSERT(false);
+
+ pPostProcessShader = *CreateShaderSet();
+ pPostProcessShader->SetShader(vs);
+ pPostProcessShader->SetShader(ppfs);
+ }
+
+
+ int texw = (int)ceil(SceneRenderScale * WindowWidth),
+ texh = (int)ceil(SceneRenderScale * WindowHeight);
+
+ // If pSceneColorTex is already created and is of correct size, we are done.
+ // It's important to check width/height in case window size changed.
+ if (pSceneColorTex && (texw == SceneColorTexW) && (texh == SceneColorTexH))
+ {
+ return true;
+ }
+
+ pSceneColorTex = *CreateTexture(Texture_RGBA | Texture_RenderTarget | Params.Multisample,
+ texw, texh, NULL);
+ if(!pSceneColorTex)
+ {
+ return false;
+ }
+ SceneColorTexW = texw;
+ SceneColorTexH = texh;
+ pSceneColorTex->SetSampleMode(Sample_ClampBorder | Sample_Linear);
+
+
+ if(!pFullScreenVertexBuffer)
+ {
+ pFullScreenVertexBuffer = *CreateBuffer();
+ const Render::Vertex QuadVertices[] =
+ {
+ Vertex(Vector3f(0, 1, 0), Color(1, 1, 1, 1), 0, 0),
+ Vertex(Vector3f(1, 1, 0), Color(1, 1, 1, 1), 1, 0),
+ Vertex(Vector3f(0, 0, 0), Color(1, 1, 1, 1), 0, 1),
+ Vertex(Vector3f(1, 0, 0), Color(1, 1, 1, 1), 1, 1)
+ };
+ pFullScreenVertexBuffer->Data(Buffer_Vertex, QuadVertices, sizeof(QuadVertices));
+ }
+ return true;
+}
+
+void RenderDevice::SetProjection(const Matrix4f& proj)
+{
+ Proj = proj;
+ SetWorldUniforms(proj);
+}
+
+void RenderDevice::BeginScene(PostProcessType pptype)
+{
+ BeginRendering();
+
+ if((pptype != PostProcess_None) && initPostProcessSupport(pptype))
+ {
+ CurPostProcess = pptype;
+ }
+ else
+ {
+ CurPostProcess = PostProcess_None;
+ }
+
+ if(CurPostProcess == PostProcess_Distortion)
+ {
+ SetRenderTarget(pSceneColorTex);
+ SetViewport(VP);
+ }
+ else
+ {
+ SetRenderTarget(0);
+ }
+
+ SetWorldUniforms(Proj);
+ SetExtraShaders(NULL);
+}
+
+void RenderDevice::FinishScene()
+{
+ SetExtraShaders(0);
+ if(CurPostProcess == PostProcess_None)
+ {
+ return;
+ }
+
+ SetRenderTarget(0);
+ SetRealViewport(VP);
+ FinishScene1();
+
+ CurPostProcess = PostProcess_None;
+}
+
+
+
+void RenderDevice::FinishScene1()
+{
+ float r, g, b, a;
+ DistortionClearColor.GetRGBA(&r, &g, &b, &a);
+ Clear(r, g, b, a);
+
+ float w = float(VP.w) / float(WindowWidth),
+ h = float(VP.h) / float(WindowHeight),
+ x = float(VP.x) / float(WindowWidth),
+ y = float(VP.y) / float(WindowHeight);
+
+ float as = float(VP.w) / float(VP.h);
+
+ // We are using 1/4 of DistortionCenter offset value here, since it is
+ // relative to [-1,1] range that gets mapped to [0, 0.5].
+ pPostProcessShader->SetUniform2f("LensCenter",
+ x + (w + Distortion.XCenterOffset * 0.5f)*0.5f, y + h*0.5f);
+ pPostProcessShader->SetUniform2f("ScreenCenter", x + w*0.5f, y + h*0.5f);
+
+ // MA: This is more correct but we would need higher-res texture vertically; we should adopt this
+ // once we have asymmetric input texture scale.
+ float scaleFactor = 1.0f / Distortion.Scale;
+
+ pPostProcessShader->SetUniform2f("Scale", (w/2) * scaleFactor, (h/2) * scaleFactor * as);
+ pPostProcessShader->SetUniform2f("ScaleIn", (2/w), (2/h) / as);
+
+ pPostProcessShader->SetUniform4f("HmdWarpParam",
+ Distortion.K[0], Distortion.K[1], Distortion.K[2], Distortion.K[3]);
+
+ if (PostProcessShaderRequested == PostProcessShader_DistortionAndChromAb)
+ {
+ pPostProcessShader->SetUniform4f("ChromAbParam",
+ Distortion.ChromaticAberration[0],
+ Distortion.ChromaticAberration[1],
+ Distortion.ChromaticAberration[2],
+ Distortion.ChromaticAberration[3]);
+ }
+
+ Matrix4f texm(w, 0, 0, x,
+ 0, h, 0, y,
+ 0, 0, 0, 0,
+ 0, 0, 0, 1);
+ pPostProcessShader->SetUniform4x4f("Texm", texm);
+
+ Matrix4f view(2, 0, 0, -1,
+ 0, 2, 0, -1,
+ 0, 0, 0, 0,
+ 0, 0, 0, 1);
+
+ ShaderFill fill(pPostProcessShader);
+ fill.SetTexture(0, pSceneColorTex);
+ RenderWithAlpha(&fill, pFullScreenVertexBuffer, NULL, view, 0, 4, Prim_TriangleStrip);
+}
+
+bool CollisionModel::TestPoint(const Vector3f& p) const
+{
+ for(unsigned i = 0; i < Planes.GetSize(); i++)
+ if(Planes[i].TestSide(p) > 0)
+ {
+ return 0;
+ }
+
+ return 1;
+}
+
+bool CollisionModel::TestRay(const Vector3f& origin, const Vector3f& norm, float& len, Planef* ph) const
+{
+ if(TestPoint(origin))
+ {
+ len = 0;
+ *ph = Planes[0];
+ return true;
+ }
+ Vector3f fullMove = origin + norm * len;
+
+ int crossing = -1;
+ float cdot1 = 0, cdot2 = 0;
+
+ for(unsigned i = 0; i < Planes.GetSize(); ++i)
+ {
+ float dot2 = Planes[i].TestSide(fullMove);
+ if(dot2 > 0)
+ {
+ return false;
+ }
+ float dot1 = Planes[i].TestSide(origin);
+ if(dot1 > 0)
+ {
+ if(dot2 <= 0)
+ {
+ //assert(crossing==-1);
+ if(crossing == -1)
+ {
+ crossing = i;
+ cdot2 = dot2;
+ cdot1 = dot1;
+ }
+ else
+ {
+ if(dot2 > cdot2)
+ {
+ crossing = i;
+ cdot2 = dot2;
+ cdot1 = dot1;
+ }
+ }
+ }
+ }
+ }
+
+ if(crossing < 0)
+ {
+ return false;
+ }
+
+ assert(TestPoint(origin + norm * len));
+
+ len = len * cdot1 / (cdot1 - cdot2) - 0.05f;
+ if(len < 0)
+ {
+ len = 0;
+ }
+ float tp = Planes[crossing].TestSide(origin + norm * len);
+ OVR_ASSERT(fabsf(tp) < 0.05f + Mathf::Tolerance);
+ OVR_UNUSED(tp);
+
+ if(ph)
+ {
+ *ph = Planes[crossing];
+ }
+ return true;
+}
+
+int GetNumMipLevels(int w, int h)
+{
+ int n = 1;
+ while(w > 1 || h > 1)
+ {
+ w >>= 1;
+ h >>= 1;
+ n++;
+ }
+ return n;
+}
+
+void FilterRgba2x2(const UByte* src, int w, int h, UByte* dest)
+{
+ for(int j = 0; j < (h & ~1); j += 2)
+ {
+ const UByte* psrc = src + (w * j * 4);
+ UByte* pdest = dest + ((w >> 1) * (j >> 1) * 4);
+
+ for(int i = 0; i < w >> 1; i++, psrc += 8, pdest += 4)
+ {
+ pdest[0] = (((int)psrc[0]) + psrc[4] + psrc[w * 4 + 0] + psrc[w * 4 + 4]) >> 2;
+ pdest[1] = (((int)psrc[1]) + psrc[5] + psrc[w * 4 + 1] + psrc[w * 4 + 5]) >> 2;
+ pdest[2] = (((int)psrc[2]) + psrc[6] + psrc[w * 4 + 2] + psrc[w * 4 + 6]) >> 2;
+ pdest[3] = (((int)psrc[3]) + psrc[7] + psrc[w * 4 + 3] + psrc[w * 4 + 7]) >> 2;
+ }
+ }
+}
+
+int GetTextureSize(int format, int w, int h)
+{
+ switch (format & Texture_TypeMask)
+ {
+ case Texture_R: return w*h;
+ case Texture_RGBA: return w*h*4;
+ case Texture_DXT1: {
+ int bw = (w+3)/4, bh = (h+3)/4;
+ return bw * bh * 8;
+ }
+ case Texture_DXT3:
+ case Texture_DXT5: {
+ int bw = (w+3)/4, bh = (h+3)/4;
+ return bw * bh * 16;
+ }
+
+ default:
+ OVR_ASSERT(0);
+ }
+ return 0;
+}
+
+}}
diff --git a/Samples/CommonSrc/Render/Render_Device.h b/Samples/CommonSrc/Render/Render_Device.h
new file mode 100644
index 0000000..448d0e6
--- /dev/null
+++ b/Samples/CommonSrc/Render/Render_Device.h
@@ -0,0 +1,897 @@
+/************************************************************************************
+
+Filename : Render_Device.h
+Content : Platform renderer for simple scene graph
+Created : September 6, 2012
+Authors : Andrew Reisse
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#ifndef OVR_Render_Device_h
+#define OVR_Render_Device_h
+
+#include "Kernel/OVR_Math.h"
+#include "Kernel/OVR_Array.h"
+#include "Kernel/OVR_RefCount.h"
+#include "Kernel/OVR_String.h"
+#include "Kernel/OVR_File.h"
+
+#include "Util/Util_Render_Stereo.h"
+
+namespace OVR { namespace Render {
+
+using namespace OVR::Util::Render;
+
+class RenderDevice;
+
+//-----------------------------------------------------------------------------------
+
+enum PrimitiveType
+{
+ Prim_Triangles,
+ Prim_Lines,
+ Prim_TriangleStrip,
+ Prim_Points,
+ Prim_Unknown,
+ Prim_Count
+};
+
+class Fill : public RefCountBase<Fill>
+{
+public:
+ enum Flags
+ {
+ F_Solid = 1,
+ F_Wireframe = 2,
+ };
+
+ virtual ~Fill() {}
+
+ virtual void Set(PrimitiveType prim = Prim_Unknown) const = 0;
+ virtual void Unset() const {}
+
+ virtual void SetTexture(int i, class Texture* tex) { OVR_UNUSED2(i,tex); }
+};
+
+enum ShaderStage
+{
+ Shader_Vertex = 0,
+ Shader_Geometry = 1,
+ Shader_Fragment = 2,
+ Shader_Pixel = 2,
+ Shader_Count = 3,
+};
+
+enum BuiltinShaders
+{
+ VShader_MV = 0,
+ VShader_MVP = 1,
+ VShader_PostProcess = 2,
+ VShader_Count = 3,
+
+ FShader_Solid = 0,
+ FShader_Gouraud = 1,
+ FShader_Texture = 2,
+ FShader_AlphaTexture = 3,
+ FShader_PostProcess = 4,
+ FShader_PostProcessWithChromAb = 5,
+ FShader_LitGouraud = 6,
+ FShader_LitTexture = 7,
+ FShader_MultiTexture = 8,
+ FShader_Count = 9,
+};
+
+
+enum MapFlags
+{
+ Map_Discard = 1,
+ Map_Read = 2, // do not use
+ Map_Unsynchronized = 4, // like D3D11_MAP_NO_OVERWRITE
+};
+
+enum BufferUsage
+{
+ Buffer_Unknown = 0,
+ Buffer_Vertex = 1,
+ Buffer_Index = 2,
+ Buffer_Uniform = 4,
+ Buffer_Feedback = 8,
+ Buffer_TypeMask = 0xff,
+ Buffer_ReadOnly = 0x100, // Buffer must be created with Data().
+};
+
+enum TextureFormat
+{
+ Texture_RGBA = 0x100,
+ Texture_R = 0x200,
+ Texture_DXT1 = 0x1100,
+ Texture_DXT3 = 0x1200,
+ Texture_DXT5 = 0x1300,
+ Texture_Depth = 0x8000,
+ Texture_TypeMask = 0xff00,
+ Texture_Compressed = 0x1000,
+ Texture_SamplesMask = 0x00ff,
+ Texture_RenderTarget = 0x10000,
+ Texture_GenMipmaps = 0x20000,
+};
+
+enum SampleMode
+{
+ Sample_Linear = 0,
+ Sample_Nearest = 1,
+ Sample_Anisotropic = 2,
+ Sample_FilterMask = 3,
+
+ Sample_Repeat = 0,
+ Sample_Clamp = 4,
+ Sample_ClampBorder = 8, // If unsupported Clamp is used instead.
+ Sample_AddressMask =12,
+
+ Sample_Count =13,
+};
+
+// A vector with a dummy w component for alignment in uniform buffers (and for float colors).
+// The w component is not used in any calculations.
+
+struct Vector4f : public Vector3f
+{
+ float w;
+
+ Vector4f() : w(1) {}
+ Vector4f(const Vector3f& v) : Vector3f(v), w(1) {}
+ Vector4f(float r, float g, float b, float a) : Vector3f(r,g,b), w(a) {}
+};
+
+
+class Shader : public RefCountBase<Shader>
+{
+ friend class ShaderSet;
+
+protected:
+ ShaderStage Stage;
+
+public:
+ Shader(ShaderStage s) : Stage(s) {}
+ virtual ~Shader() {}
+
+ ShaderStage GetStage() const { return Stage; }
+
+ virtual void Set(PrimitiveType) const { }
+ virtual void SetUniformBuffer(class Buffer* buffers, int i = 0) { OVR_UNUSED2(buffers, i); }
+ virtual bool UseTransposeMatrix() const { return 0; }
+
+protected:
+ virtual bool SetUniform(const char* name, int n, const float* v) { OVR_UNUSED3(name, n, v); return false; }
+};
+
+
+// A group of shaders, one per stage.
+// Some renderers subclass this, so CreateShaderSet must be used.
+
+class ShaderSet : public RefCountBase<ShaderSet>
+{
+ protected:
+ Ptr<Shader> Shaders[Shader_Count];
+
+public:
+ ShaderSet() { }
+ ~ShaderSet() { }
+
+ virtual void SetShader(Shader *s)
+ {
+ Shaders[s->GetStage()] = s;
+ }
+ virtual void UnsetShader(int stage)
+ {
+ Shaders[stage] = NULL;
+ }
+ Shader* GetShader(int stage) { return Shaders[stage]; }
+
+ virtual void Set(PrimitiveType prim) const
+ {
+ for (int i = 0; i < Shader_Count; i++)
+ if (Shaders[i])
+ Shaders[i]->Set(prim);
+ }
+
+ // Set a uniform (other than the standard matrices). It is undefined whether the
+ // uniforms from one shader occupy the same space as those in other shaders
+ // (unless a buffer is used, then each buffer is independent).
+ virtual bool SetUniform(const char* name, int n, const float* v)
+ {
+ bool result = 0;
+ for (int i = 0; i < Shader_Count; i++)
+ if (Shaders[i])
+ result |= Shaders[i]->SetUniform(name, n, v);
+
+ return result;
+ }
+ bool SetUniform1f(const char* name, float x)
+ {
+ const float v[] = {x};
+ return SetUniform(name, 1, v);
+ }
+ bool SetUniform2f(const char* name, float x, float y)
+ {
+ const float v[] = {x,y};
+ return SetUniform(name, 2, v);
+ }
+ bool SetUniform4f(const char* name, float x, float y, float z, float w = 1)
+ {
+ const float v[] = {x,y,z,w};
+ return SetUniform(name, 4, v);
+ }
+ bool SetUniformv(const char* name, const Vector3f& v)
+ {
+ const float a[] = {v.x,v.y,v.z,1};
+ return SetUniform(name, 4, a);
+ }
+ bool SetUniform4fv(const char* name, int n, const Vector4f* v)
+ {
+ return SetUniform(name, 4*n, &v[0].x);
+ }
+ virtual bool SetUniform4x4f(const char* name, const Matrix4f& m)
+ {
+ return SetUniform(name, 16, &m.M[0][0]);
+ }
+};
+
+class ShaderSetMatrixTranspose : public ShaderSet
+{
+public:
+ virtual bool SetUniform4x4f(const char* name, const Matrix4f& m)
+ {
+ Matrix4f mt = m.Transposed();
+ return SetUniform(name, 16, &mt.M[0][0]);
+ }
+};
+
+class ShaderFill : public Fill
+{
+ Ptr<ShaderSet> Shaders;
+ Ptr<Texture> Textures[8];
+
+public:
+ ShaderFill(ShaderSet* sh) : Shaders(sh) { }
+ ShaderFill(ShaderSet& sh) : Shaders(sh) { }
+ void Set(PrimitiveType prim) const;
+ ShaderSet* GetShaders() { return Shaders; }
+
+ virtual void SetTexture(int i, class Texture* tex) { if (i < 8) Textures[i] = tex; }
+};
+
+/* Buffer for vertex or index data. Some renderers require separate buffers, so that
+ is recommended. Some renderers cannot have high-performance buffers which are readable,
+ so reading in Map should not be relied on.
+
+ Constraints on buffers, such as ReadOnly, are not enforced by the api but may result in
+ rendering-system dependent undesirable behavior, such as terrible performance or unreported failure.
+
+ Use of a buffer inconsistent with usage is also not checked by the api, but it may result in bad
+ performance or even failure.
+
+ Use the Data() function to set buffer data the first time, if possible (it may be faster).
+*/
+
+class Buffer : public RefCountBase<Buffer>
+{
+public:
+ virtual ~Buffer() {}
+
+ virtual size_t GetSize() = 0;
+ virtual void* Map(size_t start, size_t size, int flags = 0) = 0;
+ virtual bool Unmap(void *m) = 0;
+
+ // Allocates a buffer, optionally filling it with data.
+ virtual bool Data(int use, const void* buffer, size_t size) = 0;
+};
+
+class Texture : public RefCountBase<Texture>
+{
+public:
+ virtual ~Texture() {}
+
+ virtual int GetWidth() const = 0;
+ virtual int GetHeight() const = 0;
+ virtual int GetSamples() const { return 1; }
+
+ virtual void SetSampleMode(int sm) = 0;
+ virtual void Set(int slot, ShaderStage stage = Shader_Fragment) const = 0;
+};
+
+
+
+//-----------------------------------------------------------------------------------
+
+class CollisionModel : public RefCountBase<CollisionModel>
+{
+public:
+ Array<Planef > Planes;
+
+ void Add(const Planef& p)
+ {
+ Planes.PushBack(p);
+ }
+
+ // Return whether p is inside this
+ bool TestPoint(const Vector3f& p) const;
+
+ // Assumes that the origin of the ray is outside this.
+ bool TestRay(const Vector3f& origin, const Vector3f& norm, float& len, Planef* ph = NULL) const;
+};
+
+class Node : public RefCountBase<Node>
+{
+ Vector3f Pos;
+ Quatf Rot;
+
+ mutable Matrix4f Mat;
+ mutable bool MatCurrent;
+
+public:
+ Node() : Pos(Vector3f(0)), MatCurrent(1) { }
+ virtual ~Node() { }
+
+ enum NodeType
+ {
+ Node_NonDisplay,
+ Node_Container,
+ Node_Model
+ };
+ virtual NodeType GetType() const { return Node_NonDisplay; }
+
+ virtual void ClearRenderer() { }
+
+ const Vector3f& GetPosition() const { return Pos; }
+ const Quatf& GetOrientation() const { return Rot; }
+ void SetPosition(Vector3f p) { Pos = p; MatCurrent = 0; }
+ void SetOrientation(Quatf q) { Rot = q; MatCurrent = 0; }
+
+ void Move(Vector3f p) { Pos += p; MatCurrent = 0; }
+ void Rotate(Quatf q) { Rot = q * Rot; MatCurrent = 0; }
+
+
+ // For testing only; causes Position an Orientation
+ void SetMatrix(const Matrix4f& m)
+ {
+ MatCurrent = true;
+ Mat = m;
+ }
+
+
+ const Matrix4f& GetMatrix() const
+ {
+ if (!MatCurrent)
+ {
+ Mat = Rot;
+ Mat = Matrix4f::Translation(Pos) * Mat;
+ MatCurrent = 1;
+ }
+ return Mat;
+ }
+
+ virtual void Render(const Matrix4f& ltw, RenderDevice* ren) { OVR_UNUSED2(ltw, ren); }
+};
+
+struct Vertex
+{
+ Vector3f Pos;
+ Color C;
+ float U, V;
+ float U2, V2;
+ Vector3f Norm;
+
+ Vertex (const Vector3f& p, const Color& c = Color(64,0,0,255),
+ float u = 0, float v = 0, Vector3f n = Vector3f(1,0,0))
+ : Pos(p), C(c), U(u), V(v), Norm(n), U2(u), V2(v) {}
+ Vertex(float x, float y, float z, const Color& c = Color(64,0,0,255),
+ float u = 0, float v = 0) : Pos(x,y,z), C(c), U(u), V(v), U2(u), V2(v) { }
+
+ // for multiple UV coords
+ Vertex(const Vector3f& p, const Color& c,
+ float u, float v, float u2, float v2, Vector3f n) : Pos(p), C(c), U(u), V(v), U2(u2), V2(v2), Norm(n) { }
+
+ bool operator==(const Vertex& b) const
+ {
+ return Pos == b.Pos && C == b.C && U == b.U && V == b.V;
+ }
+};
+
+// this is stored in a uniform buffer, don't change it without fixing all renderers
+struct LightingParams
+{
+ Vector4f Ambient;
+ Vector4f LightPos[8];
+ Vector4f LightColor[8];
+ float LightCount;
+ int Version;
+
+ LightingParams() : LightCount(0), Version(0) {}
+
+ void Update(const Matrix4f& view, const Vector4f* SceneLightPos);
+
+ void Set(ShaderSet* s) const;
+};
+
+//-----------------------------------------------------------------------------------
+
+class Model : public Node
+{
+public:
+ Array<Vertex> Vertices;
+ Array<UInt16> Indices;
+ PrimitiveType Type;
+ Ptr<class Fill> Fill;
+ bool Visible;
+ bool IsCollisionModel;
+
+ // Some renderers will create these if they didn't exist before rendering.
+ // Currently they are not updated, so vertex data should not be changed after rendering.
+ Ptr<Buffer> VertexBuffer;
+ Ptr<Buffer> IndexBuffer;
+
+ Model(PrimitiveType t = Prim_Triangles) : Type(t), Fill(NULL), Visible(true) { }
+ ~Model() { }
+
+ virtual NodeType GetType() const { return Node_Model; }
+
+ virtual void Render(const Matrix4f& ltw, RenderDevice* ren);
+
+ PrimitiveType GetPrimType() const { return Type; }
+
+ void SetVisible(bool visible) { Visible = visible; }
+ bool IsVisible() const { return Visible; }
+
+ void ClearRenderer()
+ {
+ VertexBuffer.Clear();
+ IndexBuffer.Clear();
+ }
+
+ // Returns the index next added vertex will have.
+ UInt16 GetNextVertexIndex() const
+ {
+ return (UInt16)Vertices.GetSize();
+ }
+
+ UInt16 AddVertex(const Vertex& v)
+ {
+ assert(!VertexBuffer && !IndexBuffer);
+ UInt16 index = (UInt16)Vertices.GetSize();
+ Vertices.PushBack(v);
+ return index;
+ }
+ UInt16 AddVertex(const Vector3f& v, const Color& c, float u_ = 0, float v_ = 0)
+ {
+ return AddVertex(Vertex(v,c,u_,v_));
+ }
+ UInt16 AddVertex(float x, float y, float z, const Color& c, float u, float v)
+ {
+ return AddVertex(Vertex(Vector3f(x,y,z),c, u,v));
+ }
+
+ void AddLine(UInt16 a, UInt16 b)
+ {
+ Indices.PushBack(a);
+ Indices.PushBack(b);
+ }
+
+ UInt16 AddVertex(float x, float y, float z, const Color& c,
+ float u, float v, float nx, float ny, float nz)
+ {
+ return AddVertex(Vertex(Vector3f(x,y,z),c, u,v, Vector3f(nx,ny,nz)));
+ }
+
+ UInt16 AddVertex(float x, float y, float z, const Color& c,
+ float u1, float v1, float u2, float v2, float nx, float ny, float nz)
+ {
+ return AddVertex(Vertex(Vector3f(x,y,z), c, u1, v1, u2, v2, Vector3f(nx,ny,nz)));
+ }
+
+ void AddLine(const Vertex& a, const Vertex& b)
+ {
+ AddLine(AddVertex(a), AddVertex(b));
+ }
+
+ void AddTriangle(UInt16 a, UInt16 b, UInt16 c)
+ {
+ Indices.PushBack(a);
+ Indices.PushBack(b);
+ Indices.PushBack(c);
+ }
+
+
+ // Uses texture coordinates for uniform world scaling (must use a repeat sampler).
+ void AddSolidColorBox(float x1, float y1, float z1,
+ float x2, float y2, float z2,
+ Color c);
+
+
+ static Model* CreateAxisFaceColorBox(float x1, float x2, Color xcolor,
+ float y1, float y2, Color ycolor,
+ float z1, float z2, Color zcolor);
+
+
+
+ // Uses texture coordinates for exactly covering each surface once.
+ static Model* CreateBox(Color c, Vector3f origin, Vector3f size);
+ static Model* CreateCylinder(Color c, Vector3f origin, float height, float radius, int sides = 20);
+ static Model* CreateCone(Color c, Vector3f origin, float height, float radius, int sides = 20);
+ static Model* CreateSphere(Color c, Vector3f origin, float radius, int sides = 20);
+
+ // Grid having halfx,halfy lines in each direction from the origin
+ static Model* CreateGrid(Vector3f origin, Vector3f stepx, Vector3f stepy,
+ int halfx, int halfy, int nmajor = 5,
+ Color minor = Color(64,64,64,192), Color major = Color(128,128,128,192));
+};
+
+class Container : public Node
+{
+public:
+ Array<Ptr<Node> > Nodes;
+
+ ~Container()
+ {
+
+ }
+
+ void ClearRenderer()
+ {
+ for (UPInt i=0; i< Nodes.GetSize(); i++)
+ Nodes[i]->ClearRenderer();
+ }
+
+ virtual NodeType GetType() const { return Node_Container; }
+
+ virtual void Render(const Matrix4f& ltw, RenderDevice* ren);
+
+ void Add(Node *n) { Nodes.PushBack(n); }
+ void Add(Model *n, class Fill *f) { n->Fill = f; Nodes.PushBack(n); }
+ void Clear() { Nodes.Clear(); }
+
+ bool CollideChildren;
+
+ Container() : CollideChildren(1) {}
+};
+
+class Scene
+{
+public:
+ Container World;
+ Vector4f LightPos[8];
+ LightingParams Lighting;
+ Array<Ptr<Model> > Models;
+
+public:
+ void Render(RenderDevice* ren, const Matrix4f& view);
+
+ void SetAmbient(Vector4f color)
+ {
+ Lighting.Ambient = color;
+ }
+ void AddLight(Vector3f pos, Vector4f color)
+ {
+ int n = (int)Lighting.LightCount;
+ OVR_ASSERT(n < 8);
+ LightPos[n] = pos;
+ Lighting.LightColor[n] = color;
+ Lighting.LightCount++;
+ }
+
+ void Clear()
+ {
+ World.Clear();
+ Models.Clear();
+ Lighting.Ambient = Vector4f(0.0f, 0.0f, 0.0f, 0.0f);
+ Lighting.LightCount = 0;
+ }
+
+ void ClearRenderer()
+ {
+ World.ClearRenderer();
+ }
+};
+
+class SceneView : public Node
+{
+public:
+ Matrix4f GetViewMatrix() const;
+};
+
+
+//-----------------------------------------------------------------------------------
+
+enum RenderCaps
+{
+ Cap_VertexBuffer = 1,
+};
+
+// Post-processing type to apply to scene after rendering. PostProcess_Distortion
+// applied distortion as described by DistortionConfig.
+enum PostProcessType
+{
+ PostProcess_None,
+ PostProcess_Distortion
+};
+
+enum DisplayMode
+{
+ Display_Window = 0,
+ Display_Fullscreen = 1,
+ Display_FakeFullscreen
+};
+
+struct DisplayId
+{
+ // Windows
+ String MonitorName; // Monitor name for fullscreen mode
+
+ // MacOS
+ long CgDisplayId; // CGDirectDisplayID
+
+ DisplayId() : CgDisplayId(0) {}
+ DisplayId(long id) : CgDisplayId(id) {}
+ DisplayId(String m, long id=0) : MonitorName(m), CgDisplayId(id) {}
+
+ operator bool () const
+ {
+ return MonitorName.GetLength() || CgDisplayId;
+ }
+
+ bool operator== (const DisplayId& b) const
+ {
+ return CgDisplayId == b.CgDisplayId &&
+ (strstr(MonitorName.ToCStr(), b.MonitorName.ToCStr()) ||
+ strstr(b.MonitorName.ToCStr(), MonitorName.ToCStr()));
+ }
+};
+
+struct RendererParams
+{
+ int Multisample;
+ int Fullscreen;
+ DisplayId Display;
+
+ RendererParams(int ms = 1) : Multisample(ms), Fullscreen(0) {}
+
+ bool IsDisplaySet() const
+ {
+ return Display;
+ }
+};
+
+
+
+//-----------------------------------------------------------------------------------
+// ***** RenderDevice
+
+class RenderDevice : public RefCountBase<RenderDevice>
+{
+ friend class StereoGeomShaders;
+protected:
+ int WindowWidth, WindowHeight;
+ RendererParams Params;
+ Viewport VP;
+
+ Matrix4f Proj;
+ Ptr<Buffer> pTextVertexBuffer;
+
+
+ // For rendering with lens warping
+ PostProcessType CurPostProcess;
+ Ptr<Texture> pSceneColorTex;
+ int SceneColorTexW;
+ int SceneColorTexH;
+ Ptr<ShaderSet> pPostProcessShader;
+ Ptr<Buffer> pFullScreenVertexBuffer;
+ float SceneRenderScale;
+ DistortionConfig Distortion;
+ Color DistortionClearColor;
+ UPInt TotalTextureMemoryUsage;
+
+ // For lighting on platforms with uniform buffers
+ Ptr<Buffer> LightingBuffer;
+
+ void FinishScene1();
+
+public:
+ enum CompareFunc
+ {
+ Compare_Always = 0,
+ Compare_Less = 1,
+ Compare_Greater = 2,
+ Compare_Count
+ };
+ RenderDevice();
+ virtual ~RenderDevice() { Shutdown(); }
+
+ // This static function is implemented in each derived class
+ // to support a specific renderer type.
+ //static RenderDevice* CreateDevice(const RendererParams& rp, void* oswnd);
+
+
+ virtual void Init() {}
+ virtual void Shutdown() {}
+ virtual bool SetParams(const RendererParams&) { return 0; }
+
+ const RendererParams& GetParams() const { return Params; }
+
+
+ // StereoParams apply Viewport, Projection and Distortion simultaneously,
+ // doing full configuration for one eye.
+ void ApplyStereoParams(const StereoEyeParams& params)
+ {
+ SetViewport(params.VP);
+ SetProjection(params.Projection);
+ if (params.pDistortion)
+ SetDistortionConfig(*params.pDistortion, params.Eye);
+ }
+
+ // Apply "orthographic" stereo parameters used for rendering 2D HUD overlays.
+ void ApplyStereoParams2D(const StereoEyeParams& params)
+ {
+ SetViewport(params.VP);
+ SetProjection(params.OrthoProjection);
+ if (params.pDistortion)
+ SetDistortionConfig(*params.pDistortion, params.Eye);
+ }
+
+
+ virtual void SetViewport(const Viewport& vp);
+ void SetViewport(int x, int y, int w, int h) { SetViewport(Viewport(x,y,w,h)); }
+ //virtual void SetScissor(int x, int y, int w, int h) = 0;
+
+ // Set viewport ignoring any adjustments used for the stereo mode.
+ virtual void SetRealViewport(const Viewport& vp) { SetMultipleViewports(1, &vp); }
+ virtual void SetMultipleViewports(int n, const Viewport* vps) { OVR_UNUSED2(n, vps); }
+
+ virtual void Clear(float r = 0, float g = 0, float b = 0, float a = 1, float depth = 1) = 0;
+ virtual void Rect(float left, float top, float right, float bottom) = 0;
+
+ inline void Clear(const Color &c, float depth = 1)
+ {
+ float r, g, b, a;
+ c.GetRGBA(&r, &g, &b, &a);
+ Clear(r, g, b, a, depth);
+ }
+
+ virtual bool IsFullscreen() const { return Params.Fullscreen != Display_Window; }
+ virtual void Present() = 0;
+ // Waits for rendering to complete; important for reducing latency.
+ virtual void ForceFlushGPU() { }
+
+ // Resources
+ virtual Buffer* CreateBuffer() { return NULL; }
+ virtual Texture* CreateTexture(int format, int width, int height, const void* data, int mipcount=1)
+ { OVR_UNUSED5(format,width,height,data, mipcount); return NULL; }
+
+ virtual bool GetSamplePositions(Render::Texture*, Vector3f* pos) { pos[0] = Vector3f(0); return 1; }
+
+ virtual ShaderSet* CreateShaderSet() { return new ShaderSetMatrixTranspose; }
+ virtual Shader* LoadBuiltinShader(ShaderStage stage, int shader) = 0;
+
+ // Rendering
+
+ // Begin drawing directly to the currently selected render target, no post-processing.
+ virtual void BeginRendering() {}
+ // Begin drawing the primary scene. This will have post-processing applied (if enabled)
+ // during FinishScene.
+ virtual void BeginScene(PostProcessType pp = PostProcess_None); //StereoDisplay disp = Stereo_Center);
+ // Postprocess the scene and return to the screen render target.
+ virtual void FinishScene();
+
+ // Texture must have been created with Texture_RenderTarget. Use NULL for the default render target.
+ // NULL depth buffer means use an internal, temporary one.
+ virtual void SetRenderTarget(Texture* color, Texture* depth = NULL, Texture* stencil = NULL)
+ { OVR_UNUSED3(color, depth, stencil); }
+ virtual void SetDepthMode(bool enable, bool write, CompareFunc func = Compare_Less) = 0;
+ virtual void SetProjection(const Matrix4f& proj);
+ virtual void SetWorldUniforms(const Matrix4f& proj) = 0;
+
+ // The data is not copied, it must remain valid until the end of the frame
+ virtual void SetLighting(const LightingParams* light);
+
+ // The index 0 is reserved for non-buffer uniforms, and so cannot be used with this function.
+ virtual void SetCommonUniformBuffer(int i, Buffer* buffer) { OVR_UNUSED2(i, buffer); }
+
+ virtual void SetExtraShaders(ShaderSet* s) { OVR_UNUSED(s); }
+ virtual Matrix4f GetProjection() const { return Proj; }
+
+ // This is a View matrix only, it will be combined with the projection matrix from SetProjection
+ virtual void Render(const Matrix4f& matrix, Model* model) = 0;
+ // offset is in bytes; indices can be null.
+ virtual void Render(const Fill* fill, Buffer* vertices, Buffer* indices,
+ const Matrix4f& matrix, int offset, int count, PrimitiveType prim = Prim_Triangles) = 0;
+ virtual void RenderWithAlpha(const Fill* fill, Render::Buffer* vertices, Render::Buffer* indices,
+ const Matrix4f& matrix, int offset, int count, PrimitiveType prim = Prim_Triangles) = 0;
+
+ // Returns width of text in same units as drawing. If strsize is not null, stores width and height.
+ float MeasureText(const struct Font* font, const char* str, float size, float* strsize = NULL);
+ virtual void RenderText(const struct Font* font, const char* str, float x, float y, float size, Color c);
+
+ virtual void FillRect(float left, float top, float right, float bottom, Color c);
+
+ virtual Fill *CreateSimpleFill(int flags = Fill::F_Solid) = 0;
+ Fill * CreateTextureFill(Texture* tex, bool useAlpha = false);
+
+ // PostProcess distortion
+ void SetSceneRenderScale(float ss);
+
+ void SetDistortionConfig(const DistortionConfig& config, StereoEye eye = StereoEye_Left)
+ {
+ Distortion = config;
+ if (eye == StereoEye_Right)
+ Distortion.XCenterOffset = -Distortion.XCenterOffset;
+ }
+
+ // Sets the color that is applied around distortion.
+ void SetDistortionClearColor(Color clearColor)
+ {
+ DistortionClearColor = clearColor;
+ }
+
+ // Don't call these directly, use App/Platform instead
+ virtual bool SetFullscreen(DisplayMode fullscreen) { OVR_UNUSED(fullscreen); return false; }
+ virtual void SetWindowSize(int w, int h) { WindowWidth = w; WindowHeight = h; }
+
+ UPInt GetTotalTextureMemoryUsage() const
+ {
+ return TotalTextureMemoryUsage;
+ }
+
+ enum PostProcessShader
+ {
+ PostProcessShader_Distortion = 0,
+ PostProcessShader_DistortionAndChromAb = 1,
+ PostProcessShader_Count
+ };
+
+ PostProcessShader GetPostProcessShader()
+ {
+ return PostProcessShaderActive;
+ }
+
+ void SetPostProcessShader(PostProcessShader newShader)
+ {
+ PostProcessShaderRequested = newShader;
+ }
+
+protected:
+ // Stereo & post-processing
+ virtual bool initPostProcessSupport(PostProcessType pptype);
+
+ virtual Shader* CreateStereoShader(PrimitiveType prim, Shader* vs)
+ { OVR_UNUSED2(prim, vs); return NULL; }
+
+private:
+ PostProcessShader PostProcessShaderRequested;
+ PostProcessShader PostProcessShaderActive;
+};
+
+int GetNumMipLevels(int w, int h);
+int GetTextureSize(int format, int w, int h);
+
+// Filter an rgba image with a 2x2 box filter, for mipmaps.
+// Image size must be a power of 2.
+void FilterRgba2x2(const UByte* src, int w, int h, UByte* dest);
+
+Texture* LoadTextureTga(RenderDevice* ren, File* f);
+Texture* LoadTextureDDS(RenderDevice* ren, File* f);
+
+}}
+
+#endif
diff --git a/Samples/CommonSrc/Render/Render_Font.h b/Samples/CommonSrc/Render/Render_Font.h
new file mode 100644
index 0000000..0a7acb5
--- /dev/null
+++ b/Samples/CommonSrc/Render/Render_Font.h
@@ -0,0 +1,51 @@
+/************************************************************************************
+
+Filename : Render_Font_h
+Content : Font data structure used by renderer
+Created : September, 2012
+Authors : Andrew Reisse
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#ifndef OVR_Render_Font_h
+#define OVR_Render_Font_h
+
+namespace OVR { namespace Render {
+
+class Fill;
+
+struct Font
+{
+ struct Char
+ {
+ short x, y; // offset
+ short advance;
+ float u1, v1, u2, v2;
+ };
+
+ int lineheight, ascent, descent;
+ const Char* chars;
+ const short** kerning;
+ int twidth, theight;
+ const
+ unsigned char* tex;
+ mutable Fill* fill;
+};
+
+}}
+
+#endif
diff --git a/Samples/CommonSrc/Render/Render_FontEmbed_DejaVu48.h b/Samples/CommonSrc/Render/Render_FontEmbed_DejaVu48.h
new file mode 100644
index 0000000..0558866
--- /dev/null
+++ b/Samples/CommonSrc/Render/Render_FontEmbed_DejaVu48.h
@@ -0,0 +1,9463 @@
+#include "Render_Font.h"
+
+namespace OVR { namespace Render {
+
+const Font::Char DejaVu_chars[] = {
+ /* 0 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f},
+ /* 1 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f},
+ /* 2 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f},
+ /* 3 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f},
+ /* 4 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f},
+ /* 5 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f},
+ /* 6 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f},
+ /* 7 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f},
+ /* 8 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f},
+ /* 9 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f},
+ /* 10 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f},
+ /* 11 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f},
+ /* 12 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f},
+ /* 13 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f},
+ /* 14 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f},
+ /* 15 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f},
+ /* 16 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f},
+ /* 17 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f},
+ /* 18 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f},
+ /* 19 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f},
+ /* 20 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f},
+ /* 21 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f},
+ /* 22 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f},
+ /* 23 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f},
+ /* 24 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f},
+ /* 25 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f},
+ /* 26 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f},
+ /* 27 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f},
+ /* 28 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f},
+ /* 29 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f},
+ /* 30 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f},
+ /* 31 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f},
+ /* 32 */ { 0, 0, 15, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f},
+ /* 33 ! */ { 7,35, 19, 0.0009842520f, 0.0000000000f, 0.0059055118f, 0.2482269555f},
+ /* 34 " */ { 5,35, 22, 0.0068897638f, 0.0000000000f, 0.0196850393f, 0.0921985805f},
+ /* 35 # */ { 4,35, 40, 0.0206692908f, 0.0000000000f, 0.0531496070f, 0.2482269555f},
+ /* 36 $ */ { 4,37, 31, 0.0541338585f, 0.0000000000f, 0.0757874027f, 0.3120567501f},
+ /* 37 % */ { 3,36, 46, 0.0767716542f, 0.0000000000f, 0.1161417291f, 0.2624113560f},
+ /* 38 & */ { 3,36, 37, 0.1171259806f, 0.0000000000f, 0.1496063024f, 0.2624113560f},
+ /* 39 ' */ { 5,35, 13, 0.1505905539f, 0.0000000000f, 0.1545275599f, 0.0921985805f},
+ /* 40 ( */ { 4,36, 19, 0.1555118114f, 0.0000000000f, 0.1663385779f, 0.3049645424f},
+ /* 41 ) */ { 4,36, 19, 0.1673228294f, 0.0000000000f, 0.1781496108f, 0.3049645424f},
+ /* 42 * */ { 2,36, 24, 0.1791338623f, 0.0000000000f, 0.1998031437f, 0.1560283750f},
+ /* 43 + */ { 5,30, 40, 0.2007873952f, 0.0000000000f, 0.2303149551f, 0.2127659619f},
+ /* 44 , */ { 4, 6, 15, 0.2312992066f, 0.0000000000f, 0.2372047305f, 0.0851063803f},
+ /* 45 - */ { 2,15, 17, 0.2381889820f, 0.0000000000f, 0.2509842515f, 0.0283687934f},
+ /* 46 . */ { 6, 6, 15, 0.2519685030f, 0.0000000000f, 0.2559055090f, 0.0425531901f},
+ /* 47 / */ { 0,35, 16, 0.2568897605f, 0.0000000000f, 0.2726377845f, 0.2765957415f},
+ /* 48 0 */ { 3,36, 31, 0.2736220360f, 0.0000000000f, 0.2972441018f, 0.2624113560f},
+ /* 49 1 */ { 6,35, 31, 0.2982283533f, 0.0000000000f, 0.3179133832f, 0.2482269555f},
+ /* 50 2 */ { 4,36, 31, 0.3188976347f, 0.0000000000f, 0.3415354192f, 0.2553191483f},
+ /* 51 3 */ { 4,36, 31, 0.3425196707f, 0.0000000000f, 0.3651574850f, 0.2624113560f},
+ /* 52 4 */ { 2,35, 31, 0.3661417365f, 0.0000000000f, 0.3917322755f, 0.2482269555f},
+ /* 53 5 */ { 4,35, 31, 0.3927165270f, 0.0000000000f, 0.4153543413f, 0.2553191483f},
+ /* 54 6 */ { 3,36, 31, 0.4163385928f, 0.0000000000f, 0.4399606287f, 0.2624113560f},
+ /* 55 7 */ { 4,35, 31, 0.4409448802f, 0.0000000000f, 0.4635826647f, 0.2482269555f},
+ /* 56 8 */ { 3,36, 31, 0.4645669162f, 0.0000000000f, 0.4881889820f, 0.2624113560f},
+ /* 57 9 */ { 3,36, 31, 0.4891732335f, 0.0000000000f, 0.5127952695f, 0.2624113560f},
+ /* 58 : */ { 6,25, 16, 0.5137795210f, 0.0000000000f, 0.5187007785f, 0.1773049682f},
+ /* 59 ; */ { 4,25, 16, 0.5196850300f, 0.0000000000f, 0.5255905390f, 0.2198581547f},
+ /* 60 < */ { 5,28, 40, 0.5265747905f, 0.0000000000f, 0.5561023355f, 0.1843971610f},
+ /* 61 = */ { 5,22, 40, 0.5570865870f, 0.0000000000f, 0.5866141915f, 0.0992907807f},
+ /* 62 > */ { 5,28, 40, 0.5875984430f, 0.0000000000f, 0.6171259880f, 0.1843971610f},
+ /* 63 ? */ { 3,36, 25, 0.6181102395f, 0.0000000000f, 0.6368110180f, 0.2553191483f},
+ /* 64 @ */ { 3,34, 48, 0.6377952695f, 0.0000000000f, 0.6791338325f, 0.2978723347f},
+ /* 65 A */ { 0,35, 33, 0.6801180840f, 0.0000000000f, 0.7116141915f, 0.2482269555f},
+ /* 66 B */ { 5,35, 33, 0.7125984430f, 0.0000000000f, 0.7372047305f, 0.2482269555f},
+ /* 67 C */ { 3,36, 34, 0.7381889820f, 0.0000000000f, 0.7657480240f, 0.2624113560f},
+ /* 68 D */ { 5,35, 37, 0.7667322755f, 0.0000000000f, 0.7952755690f, 0.2482269555f},
+ /* 69 E */ { 5,35, 30, 0.7962598205f, 0.0000000000f, 0.8188976645f, 0.2482269555f},
+ /* 70 F */ { 5,35, 28, 0.8198819160f, 0.0000000000f, 0.8395669460f, 0.2482269555f},
+ /* 71 G */ { 3,36, 37, 0.8405511975f, 0.0000000000f, 0.8710629940f, 0.2624113560f},
+ /* 72 H */ { 5,35, 36, 0.8720472455f, 0.0000000000f, 0.8986220360f, 0.2482269555f},
+ /* 73 I */ { 5,35, 14, 0.8996062875f, 0.0000000000f, 0.9035432935f, 0.2482269555f},
+ /* 74 J */ {-2,35, 14, 0.9045275450f, 0.0000000000f, 0.9153543115f, 0.3191489279f},
+ /* 75 K */ { 5,35, 31, 0.9163385630f, 0.0000000000f, 0.9438976645f, 0.2482269555f},
+ /* 76 L */ { 5,35, 27, 0.9448819160f, 0.0000000000f, 0.9665354490f, 0.2482269555f},
+ /* 77 M */ { 5,35, 41, 0.9675197005f, 0.0000000000f, 0.9990157485f, 0.2482269555f},
+ /* 78 N */ { 5,35, 36, 0.0000000000f, 0.3262411356f, 0.0255905520f, 0.5744680762f},
+ /* 79 O */ { 3,36, 38, 0.0265748035f, 0.3262411356f, 0.0580708645f, 0.5886524916f},
+ /* 80 P */ { 5,35, 29, 0.0590551198f, 0.3262411356f, 0.0816929117f, 0.5744680762f},
+ /* 81 Q */ { 3,36, 38, 0.0826771632f, 0.3262411356f, 0.1141732261f, 0.6241135001f},
+ /* 82 R */ { 5,35, 33, 0.1151574776f, 0.3262411356f, 0.1427165419f, 0.5744680762f},
+ /* 83 S */ { 3,36, 30, 0.1437007934f, 0.3262411356f, 0.1692913324f, 0.5886524916f},
+ /* 84 T */ { 0,35, 29, 0.1702755839f, 0.3262411356f, 0.1978346407f, 0.5744680762f},
+ /* 85 U */ { 5,35, 35, 0.1988188922f, 0.3262411356f, 0.2253936976f, 0.5815602541f},
+ /* 86 V */ { 0,35, 33, 0.2263779491f, 0.3262411356f, 0.2578740120f, 0.5744680762f},
+ /* 87 W */ { 2,35, 47, 0.2588582635f, 0.3262411356f, 0.3021653593f, 0.5744680762f},
+ /* 88 X */ { 1,35, 33, 0.3031496108f, 0.3262411356f, 0.3326771557f, 0.5744680762f},
+ /* 89 Y */ { 0,35, 29, 0.3336614072f, 0.3262411356f, 0.3612204790f, 0.5744680762f},
+ /* 90 Z */ { 2,35, 33, 0.3622047305f, 0.3262411356f, 0.3907480240f, 0.5744680762f},
+ /* 91 [ */ { 4,36, 19, 0.3917322755f, 0.3262411356f, 0.4015747905f, 0.6312056780f},
+ /* 92 \ */ { 0,35, 16, 0.4025590420f, 0.3262411356f, 0.4183070958f, 0.6028369069f},
+ /* 93 ] */ { 5,36, 19, 0.4192913473f, 0.3262411356f, 0.4291338623f, 0.6312056780f},
+ /* 94 ^ */ { 5,35, 40, 0.4301181138f, 0.3262411356f, 0.4596456587f, 0.4184397161f},
+ /* 95 _ */ { 0,-7, 24, 0.4606299102f, 0.3262411356f, 0.4842519760f, 0.3546099365f},
+ /* 96 ` */ { 4,38, 24, 0.4852362275f, 0.3262411356f, 0.4960629940f, 0.3900709152f},
+ /* 97 a */ { 3,27, 29, 0.4970472455f, 0.3262411356f, 0.5187007785f, 0.5248227119f},
+ /* 98 b */ { 4,36, 30, 0.5196850300f, 0.3262411356f, 0.5423228145f, 0.5886524916f},
+ /* 99 c */ { 3,27, 26, 0.5433070660f, 0.3262411356f, 0.5639764071f, 0.5248227119f},
+ /* 100 d */ { 3,36, 30, 0.5649606586f, 0.3262411356f, 0.5875984430f, 0.5886524916f},
+ /* 101 e */ { 3,27, 30, 0.5885826945f, 0.3262411356f, 0.6122047305f, 0.5248227119f},
+ /* 102 f */ { 1,36, 17, 0.6131889820f, 0.3262411356f, 0.6289370060f, 0.5815602541f},
+ /* 103 g */ { 3,27, 30, 0.6299212575f, 0.3262411356f, 0.6525590420f, 0.5886524916f},
+ /* 104 h */ { 4,36, 30, 0.6535432935f, 0.3262411356f, 0.6751968265f, 0.5815602541f},
+ /* 105 i */ { 4,36, 13, 0.6761810780f, 0.3262411356f, 0.6801180840f, 0.5815602541f},
+ /* 106 j */ {-1,36, 13, 0.6811023355f, 0.3262411356f, 0.6899606586f, 0.6524822712f},
+ /* 107 k */ { 4,36, 28, 0.6909449100f, 0.3262411356f, 0.7135826945f, 0.5815602541f},
+ /* 108 l */ { 4,36, 13, 0.7145669460f, 0.3262411356f, 0.7185039520f, 0.5815602541f},
+ /* 109 m */ { 4,27, 47, 0.7194882035f, 0.3262411356f, 0.7568897605f, 0.5177304745f},
+ /* 110 n */ { 4,27, 30, 0.7578740120f, 0.3262411356f, 0.7795275450f, 0.5177304745f},
+ /* 111 o */ { 3,27, 29, 0.7805117965f, 0.3262411356f, 0.8041338325f, 0.5248227119f},
+ /* 112 p */ { 4,27, 30, 0.8051180840f, 0.3262411356f, 0.8277559280f, 0.5886524916f},
+ /* 113 q */ { 3,27, 30, 0.8287401795f, 0.3262411356f, 0.8513779640f, 0.5886524916f},
+ /* 114 r */ { 4,27, 20, 0.8523622155f, 0.3262411356f, 0.8671259880f, 0.5177304745f},
+ /* 115 s */ { 3,27, 25, 0.8681102395f, 0.3262411356f, 0.8877952695f, 0.5248227119f},
+ /* 116 t */ { 1,33, 19, 0.8887795210f, 0.3262411356f, 0.9045275450f, 0.5602836609f},
+ /* 117 u */ { 4,27, 30, 0.9055117965f, 0.3262411356f, 0.9271653295f, 0.5248227119f},
+ /* 118 v */ { 1,26, 28, 0.9281495810f, 0.3262411356f, 0.9537401795f, 0.5106382966f},
+ /* 119 w */ { 2,26, 39, 0.9547244310f, 0.3262411356f, 0.9891732335f, 0.5106382966f},
+ /* 120 x */ { 1,26, 28, 0.0000000000f, 0.6595744491f, 0.0246062987f, 0.8439716101f},
+ /* 121 y */ { 1,26, 28, 0.0255905520f, 0.6595744491f, 0.0511811040f, 0.9148936272f},
+ /* 122 z */ { 2,26, 25, 0.0521653555f, 0.6595744491f, 0.0728346482f, 0.8439716101f},
+ /* 123 { */ { 6,36, 31, 0.0738188997f, 0.6595744491f, 0.0915354341f, 0.9716312289f},
+ /* 124 | */ { 6,36, 16, 0.0925196856f, 0.6595744491f, 0.0964566916f, 1.0000000000f},
+ /* 125 } */ { 6,36, 31, 0.0974409431f, 0.6595744491f, 0.1151574776f, 0.9716312289f},
+ /* 126 ~ */ { 5,20, 40, 0.1161417291f, 0.6595744491f, 0.1456692964f, 0.7234042287f},
+ /* 127 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f}}
+;
+
+const short DejaVu_kern45[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, -1, -2, 0, 0, 0, 0, 2, 0, 0, 3, 0, 0, 0, 0, 1,
+ 0, 2, 0, 0, -4, 0, -3, -2, -2, -6, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 0, 0, 0, -1, 0, 0, -1, 0, 0, 0, 0, 0, 0} ;
+const short DejaVu_kern65[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0,
+ 0, 1, 0, -1, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, -1,
+ 0, -1, 0, 0, -4, 0, -3, -3, 0, -4, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, -1, -1, -1, -2, 0, 0, 0, 0, 0, 0, 0, 0, -1,
+ 0, -1, 0, 0, -1, 0, -3, -2, 0, -3, 0, 0, 0, 0, 0, 0} ;
+const short DejaVu_kern66[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, -1,
+ 0, 0, 0, -1, 0, 0, -1, -2, 0, -3, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} ;
+const short DejaVu_kern67[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} ;
+const short DejaVu_kern68[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, -1, 0, 0, -3, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} ;
+const short DejaVu_kern70[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -8, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4, 0, 0, 0, 0, 0,
+ 0, -4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, -4, 0, 0, 0, -3, 0, 0, 0, -3, 0, 0, 0, 0, 0, -2,
+ 0, 0, -3, 0, 0, -3, 0, 0, 0, -4, 0, 0, 0, 0, 0, 0} ;
+const short DejaVu_kern71[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, -2, 0, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} ;
+const short DejaVu_kern72[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} ;
+const short DejaVu_kern74[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} ;
+const short DejaVu_kern75[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -5, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, -1, 0, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3,
+ 0, 0, 0, 0, -4, -1, 0, -2, 0, -2, 0, 0, 0, 0, 0, 0,
+ 0, -1, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2,
+ 0, 0, 0, 0, 0, -2, 0, 0, 0, -3, 0, 0, 0, 0, 0, 0} ;
+const short DejaVu_kern76[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2,
+ 0, 0, 0, 0, -7, -2, -5, -4, 0, -6, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1,
+ 0, 0, 0, 0, 0, -1, 0, 0, 0, -4, 0, 0, 0, 0, 0, 0} ;
+const short DejaVu_kern79[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -2, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0,
+ 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, -1, 0, -3, -3, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} ;
+const short DejaVu_kern80[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -7, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0,
+ 0, -2, 0, 0, 0, -2, 0, 0, 0, -1, 0, 0, 0, 0, -1, -2,
+ 0, 0, -1, -1, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} ;
+const short DejaVu_kern81[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} ;
+const short DejaVu_kern82[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -2, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0,
+ 0, -2, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, -3, 0, -3, -2, 0, -3, 0, 0, 0, 0, 0, 0,
+ 0, -1, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2,
+ 0, 0, 0, 0, 0, -2, 0, 0, 0, -3, 0, 0, 0, 0, 0, 0} ;
+const short DejaVu_kern83[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} ;
+const short DejaVu_kern84[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4, -6, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -5, 0, 0, 0, 0, 0,
+ 0, -4, 0, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, -8, 0, -8, 0, -8, 0, 0, 0, -1, 0, 0, 0, 0, 0, -8,
+ 0, 0, -7, -8, 0, -7, 0, -8, 0, -7, 0, 0, 0, 0, 0, 0} ;
+const short DejaVu_kern85[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} ;
+const short DejaVu_kern86[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, -6, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4, 0, 0, 0, 0, 0,
+ 0, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, -4, 0, 0, 0, -4, 0, 0, 0, -1, 0, 0, 0, 0, 0, -4,
+ 0, 0, 0, 0, 0, -3, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0} ;
+const short DejaVu_kern87[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -6, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 0, 0, 0, 0, 0,
+ 0, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, -3, 0, 0, 0, -3, 0, 0, 0, -1, 0, 0, 0, 0, 0, -3,
+ 0, 0, -2, 0, 0, -2, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0} ;
+const short DejaVu_kern88[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3,
+ 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} ;
+const short DejaVu_kern89[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -6, -10, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -6, 0, 0, 0, 0, 0,
+ 0, -4, 0, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, -7, 0, 0, 0, -6, 0, 0, 0, -2, 0, 0, 0, 0, 0, -6,
+ 0, 0, 0, 0, 0, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} ;
+const short DejaVu_kern90[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} ;
+const short DejaVu_kern101[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0} ;
+const short DejaVu_kern102[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, -3, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, -1, 0, 0, -1, 0, -1, 0, 0, 0, 0, 0, 0} ;
+const short DejaVu_kern107[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, -1, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2,
+ 0, 0, 0, 0, 0, -1, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0} ;
+const short DejaVu_kern111[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0} ;
+const short DejaVu_kern114[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, -4, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, -1, -1, -1, 0, -1, -1, 0, 0, 0, 0, -1, -1, -1,
+ 0, -1, -1, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0} ;
+const short DejaVu_kern118[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -4, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} ;
+const short DejaVu_kern119[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} ;
+const short DejaVu_kern120[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, -1, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} ;
+const short DejaVu_kern121[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -7, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} ;
+const short* DejaVu_kern[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, DejaVu_kern45, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, DejaVu_kern65, DejaVu_kern66, DejaVu_kern67, DejaVu_kern68, 0, DejaVu_kern70, DejaVu_kern71, DejaVu_kern72, 0, DejaVu_kern74, DejaVu_kern75, DejaVu_kern76, 0, 0, DejaVu_kern79,
+ DejaVu_kern80, DejaVu_kern81, DejaVu_kern82, DejaVu_kern83, DejaVu_kern84, DejaVu_kern85, DejaVu_kern86, DejaVu_kern87, DejaVu_kern88, DejaVu_kern89, DejaVu_kern90, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, DejaVu_kern101, DejaVu_kern102, 0, 0, 0, 0, DejaVu_kern107, 0, 0, 0, DejaVu_kern111,
+ 0, 0, DejaVu_kern114, 0, 0, 0, DejaVu_kern118, DejaVu_kern119, DejaVu_kern120, DejaVu_kern121, 0, 0, 0, 0, 0, 0};
+
+const unsigned char DejaVu_tex[] = {
+ 0, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 135, 255, 255, 255, 72, 0, 0, 0, 0, 0, 0, 208, 255, 255,
+ 241, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 59, 154, 218, 244, 243, 217, 152, 55, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 247, 255, 255, 100, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8,
+ 97, 166, 218, 239, 250, 236, 209, 160, 97, 18, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 138, 255, 255, 255, 177, 0, 178, 255, 255, 255, 137, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 87, 255, 255, 255, 217, 0, 0, 0, 0, 0, 0, 0, 0, 31, 122, 189,
+ 230, 249, 249, 230, 189, 122, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 44, 111, 178, 241, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 12, 69, 126, 169, 210, 233, 248, 246, 229,
+ 209, 159, 102, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 28, 94, 149, 191, 225, 241, 251, 238, 223, 187, 134, 67, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 109, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 29, 111, 175, 216, 241, 252, 247, 235, 209, 178, 135, 86, 28, 0, 0, 0,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 15, 100,
+ 159, 209, 231, 247, 247, 231, 209, 159, 101, 16, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 14, 89, 159, 210, 232, 250, 243, 218, 172,
+ 100, 13, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 0,
+ 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 24, 114, 208, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 0, 208, 114, 24, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 55, 123, 184, 220, 240, 250, 233, 207,
+ 151, 77, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 37, 99, 155, 192, 225, 240, 251, 246, 233,
+ 213, 174, 132, 68, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 219, 255, 255, 255, 255, 222, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 253, 241, 228, 200, 155, 100, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 6, 72, 134, 184, 217, 240, 251, 247,
+ 231, 215, 180, 130, 79, 12, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 254, 247, 240, 222, 200, 170, 130, 83, 23, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 41, 108, 162, 199, 229, 244, 252, 243, 229, 214, 185, 140, 94, 38, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 2, 145, 255, 255, 255, 255, 254, 114, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 210, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 211, 255, 255, 255, 255, 255, 255, 0,
+ 0, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 197, 255, 255, 252, 13, 0, 0, 0, 0, 0, 18, 254, 255, 255,
+ 183, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 7, 160, 255, 255, 255, 255, 255, 255, 255, 254, 156, 5, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 175, 255, 255, 202, 1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 94, 234,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 251, 172, 57, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 36, 249, 255, 255, 245, 31, 0, 32, 246, 255, 255, 248, 34,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 165, 255, 255, 255, 139, 0, 0, 0, 0, 0, 0, 15, 152, 251, 255, 255,
+ 255, 255, 255, 255, 255, 255, 251, 151, 15, 0, 0, 0, 0, 0, 0, 33,
+ 99, 166, 232, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 39, 118, 197, 251, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 249, 165, 34, 0, 0, 0, 0, 0, 0, 0, 43, 129, 214,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 221, 102, 1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 34, 245, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, 173,
+ 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 15, 148, 245, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 245, 149, 14, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 8, 118, 237, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 241, 118, 4, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 0,
+ 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9,
+ 88, 182, 252, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 0, 255, 255, 252, 182, 88, 8, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 6, 106, 212, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 219, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 24, 125, 213, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 246, 166, 65, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62,
+ 255, 255, 255, 255, 255, 255, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 250, 167, 31, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 31, 146, 239, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 245, 168, 73, 0, 0, 0, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 208, 123, 25,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 111,
+ 215, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 216,
+ 136, 45, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 7, 171, 255, 255, 255, 255, 249, 87, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 57, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58,
+ 255, 255, 255, 255, 255, 255, 255, 0,
+ 0, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 8, 249, 255, 255, 202, 0, 0, 0, 0, 0, 0, 79, 255, 255, 255,
+ 121, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3,
+ 183, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 179, 2, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 255, 255, 254, 54, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 176, 255, 255, 255, 120, 0, 0, 0, 122, 255, 255, 255, 173,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,
+ 238, 255, 255, 255, 61, 0, 0, 0, 0, 0, 35, 217, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 216, 34, 0, 0, 0, 0, 0, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 243, 101, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 194, 21,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 192, 255, 255, 254, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 115, 249, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 204, 0, 0, 0, 0, 51, 227, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 227, 51, 0, 0, 0,
+ 0, 0, 0, 0, 24, 201, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 191, 17, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 0,
+ 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 62, 156, 241,
+ 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 241, 155, 61, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 75, 218, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 254, 123, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 27, 155, 248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 205, 70, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 160,
+ 255, 255, 255, 255, 255, 255, 163, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 246, 88, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 7, 131, 249, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 199, 66, 0, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 249,
+ 155, 22, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 97, 232, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 180, 60, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 17, 194, 255, 255, 255, 255, 240, 64, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 159, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 160,
+ 255, 255, 255, 255, 255, 255, 255, 0,
+ 0, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 64, 255, 255, 255, 139, 0, 0, 0, 0, 0, 0, 143, 255, 255, 255,
+ 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120,
+ 255, 255, 255, 241, 98, 15, 17, 102, 243, 255, 255, 255, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 5, 216, 255, 255, 157, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 55, 255, 255, 255, 226, 7, 0, 0, 0, 9, 228, 255, 255, 255,
+ 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64,
+ 255, 255, 255, 236, 3, 0, 0, 0, 0, 24, 224, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 223, 23, 0, 0, 0, 0, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 114, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 213,
+ 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 109, 255, 255, 255, 148, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 2, 156, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 97, 0, 0, 0, 44, 237, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 237, 44, 0, 0,
+ 0, 0, 0, 23, 224, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 201, 10, 0, 0, 0, 0, 255, 255, 255, 255, 255, 0,
+ 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 129, 222, 255, 255, 255,
+ 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 222, 129, 35,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 2, 119, 244, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 176, 22, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 244,
+ 255, 255, 255, 255, 255, 255, 245, 14, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 86, 0, 0, 0, 0, 0,
+ 0, 0, 0, 28, 202, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 240, 85, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 15, 175, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 30, 213, 255, 255, 255, 255, 228, 45, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 245, 15,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 246,
+ 255, 255, 255, 255, 255, 255, 255, 0,
+ 0, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 125, 255, 255, 255, 77, 0, 0, 0, 0, 0, 0, 206, 255, 255, 246,
+ 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 243,
+ 255, 255, 254, 65, 0, 0, 0, 0, 72, 255, 255, 255, 241, 12, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 117, 255, 255, 240, 22, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 246, 255, 255, 255,
+ 255, 177, 76, 22, 5, 18, 47, 101, 168, 243, 255, 255, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 184, 255, 255, 255, 97, 0, 0, 0, 0, 0, 101, 255, 255, 255,
+ 180, 0, 0, 0, 0, 0, 12, 194, 66, 0, 0, 0, 0, 0, 0, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 67, 194, 12, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 255, 255, 255,
+ 223, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 141,
+ 255, 255, 255, 162, 0, 0, 0, 0, 0, 187, 255, 255, 255, 255, 228, 124,
+ 46, 15, 16, 47, 126, 229, 255, 255, 255, 255, 186, 0, 0, 0, 0, 255,
+ 255, 255, 255, 224, 160, 96, 32, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 255, 201, 140, 84, 41, 21, 5, 18,
+ 52, 117, 219, 255, 255, 255, 255, 255, 78, 0, 0, 0, 0, 255, 255, 255,
+ 224, 157, 103, 61, 27, 12, 4, 17, 45, 94, 176, 253, 255, 255, 255, 255,
+ 173, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34,
+ 244, 255, 255, 224, 12, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, 255, 255, 255, 255,
+ 254, 185, 106, 46, 23, 3, 9, 22, 48, 82, 132, 182, 231, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 93, 255, 255, 255, 237, 9, 0, 0, 2, 204, 255, 255, 255, 255, 240,
+ 146, 70, 26, 7, 8, 27, 72, 150, 243, 255, 255, 255, 255, 203, 2, 0,
+ 0, 0, 4, 192, 255, 255, 255, 255, 218, 108, 40, 11, 11, 40, 108, 219,
+ 255, 255, 255, 255, 255, 158, 0, 0, 0, 0, 255, 255, 255, 255, 255, 0,
+ 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 16, 103, 197, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 209, 115, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 115, 209, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 197, 103, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 218, 143, 76, 35, 11, 9, 35, 95,
+ 200, 255, 255, 255, 255, 241, 23, 0, 0, 0, 0, 0, 0, 0, 0, 27,
+ 193, 255, 255, 255, 255, 255, 255, 251, 187, 131, 77, 40, 23, 6, 9, 21,
+ 51, 89, 149, 217, 255, 255, 255, 255, 255, 255, 234, 66, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 99, 255,
+ 255, 255, 224, 225, 255, 255, 255, 101, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0,
+ 4, 15, 40, 80, 148, 238, 255, 255, 255, 255, 237, 22, 0, 0, 0, 0,
+ 0, 0, 33, 224, 255, 255, 255, 255, 255, 227, 156, 86, 47, 27, 7, 15,
+ 36, 57, 108, 174, 238, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 3, 10, 25, 48, 85, 130, 191, 251, 255, 255, 255,
+ 255, 255, 255, 124, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 206, 255, 255, 255, 255,
+ 255, 241, 179, 116, 60, 39, 20, 4, 10, 24, 53, 95, 147, 217, 255, 255,
+ 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 46, 229, 255, 255, 255, 255, 212, 28, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 225, 255, 255, 255, 108,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 109, 255,
+ 255, 255, 224, 255, 255, 255, 255, 0,
+ 0, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 187, 255, 255, 253, 16, 0, 0, 0, 0, 0, 16, 253, 255, 255, 191,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 104, 255,
+ 255, 255, 178, 0, 0, 0, 0, 0, 0, 183, 255, 255, 255, 103, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 25, 242, 255, 255, 110, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 252,
+ 93, 0, 0, 0, 0, 0, 0, 0, 0, 8, 92, 201, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 54, 255, 255, 255, 218, 3, 0, 0, 0, 0, 0, 3, 220, 255, 255,
+ 255, 51, 0, 0, 0, 0, 133, 255, 255, 160, 19, 0, 0, 0, 0, 255,
+ 255, 255, 0, 0, 0, 0, 19, 161, 255, 255, 132, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 255, 255, 255,
+ 126, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 218,
+ 255, 255, 255, 85, 0, 0, 0, 0, 91, 255, 255, 255, 255, 183, 15, 0,
+ 0, 0, 0, 0, 0, 16, 185, 255, 255, 255, 255, 89, 0, 0, 0, 224,
+ 160, 96, 32, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 255, 255, 216, 120, 32, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 124, 253, 255, 255, 255, 223, 9, 0, 0, 0, 212, 125, 39,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 214, 255, 255, 255,
+ 255, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 192,
+ 255, 255, 255, 71, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 88, 255, 255, 255, 255, 207,
+ 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 200, 255, 255, 255, 141, 0, 0, 0, 84, 255, 255, 255, 255, 191, 24,
+ 0, 0, 0, 0, 0, 0, 0, 0, 27, 195, 255, 255, 255, 255, 84, 0,
+ 0, 0, 108, 255, 255, 255, 255, 149, 5, 0, 0, 0, 0, 0, 0, 6,
+ 152, 255, 255, 255, 255, 255, 66, 0, 0, 0, 255, 255, 255, 255, 255, 0,
+ 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 4, 77, 171, 248, 255, 255, 255, 255, 255, 255, 255, 255,
+ 231, 142, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 48, 142, 231, 255, 255, 255, 255, 255, 255,
+ 255, 255, 248, 170, 76, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 255, 254, 173, 66, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 120, 255, 255, 255, 255, 124, 0, 0, 0, 0, 0, 0, 0, 45, 227,
+ 255, 255, 255, 255, 255, 220, 113, 22, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 54, 157, 251, 255, 255, 255, 255, 249, 91, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 196, 255,
+ 255, 255, 129, 130, 255, 255, 255, 199, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 14, 161, 255, 255, 255, 255, 118, 0, 0, 0, 0,
+ 0, 16, 218, 255, 255, 255, 255, 224, 95, 2, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 80, 194, 255, 255, 255, 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 112, 226, 255,
+ 255, 255, 255, 255, 109, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 204, 255, 255, 255, 255, 235,
+ 121, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 150,
+ 242, 255, 255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 65, 240, 255, 255, 255, 255, 193, 16, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 125, 255, 255, 255, 209,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 210, 255,
+ 255, 255, 124, 255, 255, 255, 255, 0,
+ 0, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 4, 244, 255, 255, 207, 0, 0, 0, 0, 0, 0, 78, 255, 255, 255, 129,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 101, 166, 207,
+ 239, 255, 255, 255, 243, 222, 200, 168, 120, 72, 24, 0, 0, 0, 170, 255,
+ 255, 255, 90, 0, 0, 0, 0, 0, 0, 93, 255, 255, 255, 168, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 164, 255, 255, 210, 4, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 255, 255, 255, 140,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 167, 255, 255, 255, 104, 0, 0, 0, 0, 0, 0, 0, 108, 255, 255,
+ 255, 162, 0, 0, 0, 0, 155, 254, 255, 255, 235, 93, 0, 0, 0, 255,
+ 255, 255, 0, 0, 0, 93, 235, 255, 255, 254, 155, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 152, 255, 255, 253,
+ 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 255,
+ 255, 255, 249, 13, 0, 0, 0, 2, 219, 255, 255, 255, 199, 4, 0, 0,
+ 0, 0, 0, 0, 0, 0, 4, 200, 255, 255, 255, 218, 2, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 197, 80, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 104, 255, 255, 255, 255, 89, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 230, 255, 255,
+ 255, 152, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 108, 255,
+ 255, 255, 159, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 233, 255, 255, 255, 190, 9,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 49, 255, 255, 255, 254, 36, 0, 0, 0, 168, 255, 255, 255, 217, 10, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 220, 255, 255, 255, 167, 0,
+ 0, 6, 226, 255, 255, 255, 163, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 166, 255, 255, 255, 255, 201, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 50, 144, 234, 255, 255, 255, 255, 255, 255, 255, 255, 247, 168, 74,
+ 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 74, 168, 247, 255, 255, 255,
+ 255, 255, 255, 255, 255, 233, 144, 50, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 187, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 156, 255, 255, 255, 198, 0, 0, 0, 0, 0, 0, 48, 240, 255,
+ 255, 255, 255, 236, 111, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 34, 180, 255, 255, 255, 255, 253, 85, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 255, 255,
+ 255, 254, 33, 34, 254, 255, 255, 255, 40, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 183, 255, 255, 255, 197, 0, 0, 0, 0,
+ 0, 176, 255, 255, 255, 255, 167, 7, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 73, 222, 255, 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 141,
+ 255, 255, 255, 255, 250, 53, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 162, 255, 255, 255, 255, 183, 15,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 12, 123, 246, 255, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 89, 249, 255, 255, 255, 255, 170, 7, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 26, 252, 255, 255, 255,
+ 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 57, 255, 255,
+ 255, 251, 26, 255, 255, 255, 255, 0,
+ 0, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 54, 255, 255, 255, 144, 0, 0, 0, 0, 0, 0, 141, 255, 255, 255, 67,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 167, 251, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 219, 255,
+ 255, 255, 39, 0, 0, 0, 0, 0, 0, 41, 255, 255, 255, 218, 0, 0,
+ 0, 0, 0, 0, 0, 0, 60, 255, 255, 255, 64, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 235, 255, 255, 255, 38,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 26, 250, 255, 255, 235, 9, 0, 0, 0, 0, 0, 0, 0, 11, 238, 255,
+ 255, 248, 22, 0, 0, 0, 0, 55, 198, 255, 255, 255, 186, 34, 0, 255,
+ 255, 255, 0, 35, 187, 255, 255, 255, 197, 54, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 217, 255, 255, 186,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 118, 255,
+ 255, 255, 186, 0, 0, 0, 0, 77, 255, 255, 255, 248, 35, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 35, 248, 255, 255, 255, 75, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 184, 255, 255, 255, 169, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 104, 255, 255,
+ 255, 214, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 244, 255,
+ 255, 231, 17, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 155, 255, 255, 255, 214, 10, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 156, 255, 255, 255, 184, 0, 0, 0, 0, 220, 255, 255, 255, 93, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 255, 255, 255, 220, 0,
+ 0, 77, 255, 255, 255, 239, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 16, 240, 255, 255, 255, 255, 61, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26,
+ 118, 212, 255, 255, 255, 255, 255, 255, 255, 255, 254, 195, 100, 14, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 100, 194, 254,
+ 255, 255, 255, 255, 255, 255, 255, 255, 212, 117, 26, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 46, 255, 255, 255, 233, 0, 0, 0, 0, 0, 34, 233, 255, 255,
+ 255, 255, 186, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 99, 249, 255, 255, 255, 248, 61, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 135, 255, 255,
+ 255, 190, 0, 0, 191, 255, 255, 255, 137, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 62, 255, 255, 255, 231, 0, 0, 0, 0,
+ 87, 255, 255, 255, 255, 145, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 11, 160, 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 101, 255, 255, 255, 255, 202, 1, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 77, 255, 255, 255, 255, 151, 3, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 32, 178, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 116,
+ 254, 255, 255, 255, 255, 144, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 177, 255, 255, 255,
+ 158, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 159, 255, 255,
+ 255, 176, 0, 255, 255, 255, 255, 0,
+ 0, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 116, 255, 255, 255, 81, 0, 0, 0, 0, 0, 0, 205, 255, 255, 250, 10,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 89, 243, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 240, 255,
+ 255, 255, 11, 0, 0, 0, 0, 0, 0, 12, 255, 255, 255, 238, 0, 0,
+ 0, 0, 0, 0, 0, 3, 207, 255, 255, 168, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252, 255, 255, 255, 7,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 124, 255, 255, 255, 138, 0, 0, 0, 0, 0, 0, 0, 0, 0, 143, 255,
+ 255, 255, 119, 0, 0, 0, 0, 0, 0, 87, 224, 255, 255, 246, 120, 255,
+ 255, 255, 120, 246, 255, 255, 223, 87, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 255, 255, 255, 87,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 195, 255,
+ 255, 255, 108, 0, 0, 0, 0, 168, 255, 255, 255, 159, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 160, 255, 255, 255, 166, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 78, 255, 255, 255, 217, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 255, 255,
+ 255, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 191, 255, 255,
+ 255, 81, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 22, 250, 255, 255, 255, 63, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16,
+ 245, 255, 255, 255, 78, 0, 0, 0, 0, 244, 255, 255, 255, 25, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 255, 255, 255, 244, 0,
+ 0, 144, 255, 255, 255, 142, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 144, 255, 255, 255, 255, 155, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 91, 186, 253,
+ 255, 255, 255, 255, 255, 255, 255, 255, 220, 127, 33, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33,
+ 127, 219, 255, 255, 255, 255, 255, 255, 255, 255, 253, 185, 91, 10, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 8, 255, 255, 255, 249, 0, 0, 0, 0, 7, 206, 255, 255, 255,
+ 255, 143, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 241, 255, 255, 255, 224, 13,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 229, 255, 255,
+ 255, 92, 0, 0, 94, 255, 255, 255, 230, 3, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 14, 255, 255, 255, 250, 0, 0, 0, 4,
+ 221, 255, 255, 255, 192, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 137, 255, 255, 255, 255, 79, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 3, 216, 255, 255, 255, 195, 3, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 1, 144, 255,
+ 255, 255, 255, 254, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 75, 255, 255, 255,
+ 245, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 246, 255, 255,
+ 255, 74, 0, 255, 255, 255, 255, 0,
+ 0, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 177, 255, 255, 254, 20, 0, 0, 0, 0, 0, 15, 253, 255, 255, 199, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 81, 252, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 252, 255,
+ 255, 255, 2, 0, 0, 0, 0, 0, 0, 3, 255, 255, 255, 251, 0, 0,
+ 0, 0, 0, 0, 0, 106, 255, 255, 244, 28, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 249, 255, 255, 255, 35,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 214, 255, 255, 255, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 255,
+ 255, 255, 210, 0, 0, 0, 0, 0, 0, 0, 8, 121, 242, 255, 255, 255,
+ 255, 255, 255, 255, 242, 121, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 92, 255, 255, 237, 7,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 252, 255,
+ 255, 255, 31, 0, 0, 0, 3, 241, 255, 255, 255, 52, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 53, 255, 255, 255, 239, 3, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 22, 255, 255, 255, 241, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 255, 255,
+ 255, 248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 255, 255, 255,
+ 170, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 117, 255, 255, 255, 180, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112,
+ 255, 255, 255, 224, 2, 0, 0, 0, 0, 248, 255, 255, 255, 5, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 255, 255, 255, 246, 0,
+ 0, 202, 255, 255, 255, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 74, 255, 255, 255, 255, 232, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 65, 159, 242, 255, 255, 255,
+ 255, 255, 255, 255, 255, 239, 153, 59, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 59, 153, 239, 255, 255, 255, 255, 255, 255, 255, 255, 242, 159, 65,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 33, 255, 255, 255, 228, 0, 0, 0, 0, 142, 255, 255, 255, 255,
+ 133, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, 247, 255, 255, 255, 155,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 74, 255, 255, 255,
+ 241, 9, 0, 0, 10, 242, 255, 255, 255, 76, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 13, 255, 255, 255, 239, 0, 0, 0, 90,
+ 255, 255, 255, 242, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 211, 255, 255, 255, 187, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 85, 255, 255, 255, 242, 30, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 7, 170, 255, 255,
+ 255, 255, 249, 89, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 2, 225, 255, 255,
+ 255, 107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 108, 255, 255, 255,
+ 225, 2, 0, 255, 255, 255, 255, 0,
+ 0, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 80, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 0, 16, 235, 255, 255, 255, 255, 217, 121, 56,
+ 14, 255, 255, 9, 28, 46, 65, 94, 141, 187, 233, 0, 0, 0, 240, 255,
+ 255, 255, 10, 0, 0, 0, 0, 0, 0, 12, 255, 255, 255, 238, 0, 0,
+ 0, 0, 0, 0, 19, 237, 255, 255, 121, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 212, 255, 255, 255, 125,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 48,
+ 255, 255, 255, 206, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 211,
+ 255, 255, 255, 44, 0, 0, 0, 0, 0, 0, 0, 0, 23, 154, 253, 255,
+ 255, 255, 253, 154, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 158, 255, 255, 147, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 94, 255, 255,
+ 255, 209, 0, 0, 0, 0, 53, 255, 255, 255, 233, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 234, 255, 255, 255, 52, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 8, 255, 255, 255, 246, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 255, 255,
+ 255, 227, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 244, 255, 255, 236,
+ 22, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 216, 255, 255, 255, 81, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 216,
+ 255, 255, 255, 121, 0, 0, 0, 0, 0, 224, 255, 255, 255, 25, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 255, 255, 255, 222, 0,
+ 0, 227, 255, 255, 255, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 27, 255, 255, 255, 255, 255, 45, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 39, 133, 225, 255, 255, 255, 255, 255, 255,
+ 255, 255, 251, 180, 86, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 7, 85, 179, 251, 255, 255, 255, 255, 255, 255, 255, 255,
+ 225, 133, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 125, 255, 255, 255, 184, 0, 0, 0, 47, 252, 255, 255, 255, 169,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 255, 255, 255, 254,
+ 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 255, 255, 255,
+ 153, 0, 0, 0, 0, 155, 255, 255, 255, 174, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 62, 255, 255, 255, 205, 0, 0, 0, 190,
+ 255, 255, 255, 144, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 89, 255, 255, 255, 252, 18, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 187, 255, 255, 255, 144, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 16, 193, 255, 255, 255,
+ 255, 241, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 126, 255, 255,
+ 255, 208, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 209, 255, 255, 255,
+ 125, 0, 0, 255, 255, 255, 255, 0,
+ 0, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 80, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 0, 117, 255, 255, 255, 254, 120, 3, 0, 0,
+ 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 219, 255,
+ 255, 255, 38, 0, 0, 0, 0, 0, 0, 41, 255, 255, 255, 218, 0, 0,
+ 0, 0, 0, 0, 153, 255, 255, 218, 6, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 139, 255, 255, 255, 241,
+ 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 127,
+ 255, 255, 255, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 132,
+ 255, 255, 255, 122, 0, 0, 0, 0, 0, 0, 0, 0, 22, 153, 252, 255,
+ 255, 255, 252, 153, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 223, 255, 255, 49, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 255, 255,
+ 255, 132, 0, 0, 0, 0, 105, 255, 255, 255, 170, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 171, 255, 255, 255, 103, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 39, 255, 255, 255, 224, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, 255, 255,
+ 255, 175, 0, 0, 0, 0, 0, 0, 0, 0, 1, 190, 255, 255, 255, 92,
+ 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 34, 255, 255, 255, 239, 5, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 255,
+ 255, 255, 249, 21, 0, 0, 0, 0, 0, 169, 255, 255, 255, 93, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 255, 255, 255, 167, 0,
+ 0, 246, 255, 255, 255, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 9, 255, 255, 255, 255, 255, 98, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 206, 112, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 21, 111, 205, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 45, 245, 255, 255, 255, 103, 0, 0, 0, 184, 255, 255, 255, 222, 10,
+ 0, 0, 0, 0, 0, 0, 0, 5, 94, 176, 224, 248, 244, 212, 146, 42,
+ 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 185, 255, 255, 255,
+ 157, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 249, 255, 255, 255,
+ 55, 0, 0, 0, 0, 58, 255, 255, 255, 250, 21, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 181, 255, 255, 255, 145, 0, 0, 19, 253,
+ 255, 255, 254, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 228, 255, 255, 255, 87, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 17, 252, 255, 255, 254, 37, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 29, 212, 255, 255, 255, 255,
+ 229, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 27, 252, 255,
+ 255, 255, 55, 0, 0, 0, 0, 0, 0, 0, 0, 56, 255, 255, 255, 252,
+ 26, 0, 0, 255, 255, 255, 255, 0,
+ 0, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 80, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 0, 193, 255, 255, 255, 139, 0, 0, 0, 0,
+ 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 255,
+ 255, 255, 88, 0, 0, 0, 0, 0, 0, 91, 255, 255, 255, 169, 0, 0,
+ 0, 0, 0, 50, 253, 255, 255, 74, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 252, 255, 255, 255,
+ 209, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 198,
+ 255, 255, 255, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 59,
+ 255, 255, 255, 193, 0, 0, 0, 0, 0, 0, 7, 119, 242, 255, 255, 255,
+ 255, 255, 255, 255, 241, 118, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 242, 255, 255,
+ 255, 54, 0, 0, 0, 0, 149, 255, 255, 255, 117, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 117, 255, 255, 255, 147, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 98, 255, 255, 255, 188, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 216, 255, 255,
+ 255, 89, 0, 0, 0, 0, 0, 0, 0, 0, 107, 255, 255, 255, 181, 0,
+ 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 106, 168, 216, 237, 250, 236, 219, 193, 138, 79, 12, 0,
+ 0, 0, 0, 0, 0, 0, 0, 87, 255, 255, 255, 183, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 174, 255,
+ 255, 255, 164, 0, 0, 0, 0, 0, 0, 79, 255, 255, 255, 217, 9, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 220, 255, 255, 255, 76, 0,
+ 0, 247, 255, 255, 255, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 9, 255, 255, 255, 255, 255, 144, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 229, 139, 44,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 137, 228, 255, 255, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 41, 227, 255, 255, 255, 235, 13, 0, 0, 50, 255, 255, 255, 255, 71, 0,
+ 0, 0, 0, 0, 0, 0, 56, 215, 255, 255, 255, 255, 255, 255, 255, 253,
+ 141, 4, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 56, 255, 255, 255,
+ 248, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 111, 255, 255, 255, 213,
+ 0, 0, 0, 0, 0, 0, 216, 255, 255, 255, 112, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 12, 157, 255, 255, 255, 253, 46, 0, 0, 86, 255,
+ 255, 255, 201, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 164, 255, 255, 255, 139, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 85, 255, 255, 255, 200, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 255, 255, 255, 255, 0, 0, 44, 228, 255, 255, 255, 255, 213,
+ 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 177, 255,
+ 255, 255, 158, 0, 0, 0, 0, 0, 0, 0, 0, 159, 255, 255, 255, 176,
+ 0, 0, 0, 255, 255, 255, 255, 0,
+ 0, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 80, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 0, 232, 255, 255, 255, 33, 0, 0, 0, 0,
+ 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 106, 255,
+ 255, 255, 172, 0, 0, 0, 0, 0, 0, 179, 255, 255, 255, 105, 0, 0,
+ 0, 0, 1, 198, 255, 255, 179, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 144, 255, 255, 255,
+ 255, 190, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 252,
+ 255, 255, 240, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3,
+ 242, 255, 255, 250, 11, 0, 0, 0, 0, 84, 222, 255, 255, 246, 121, 255,
+ 255, 255, 121, 246, 255, 255, 222, 84, 0, 0, 0, 0, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 255, 255, 255,
+ 231, 1, 0, 0, 0, 0, 183, 255, 255, 255, 84, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 85, 255, 255, 255, 180, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 199, 255, 255, 255, 119, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 194, 255, 255, 255,
+ 209, 4, 0, 0, 0, 0, 0, 0, 0, 32, 243, 255, 255, 241, 28, 0,
+ 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 240, 140,
+ 28, 0, 0, 0, 0, 0, 0, 140, 255, 255, 255, 127, 0, 0, 0, 67,
+ 155, 212, 243, 249, 231, 210, 160, 92, 16, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 251, 255,
+ 255, 255, 58, 0, 0, 0, 0, 0, 0, 2, 199, 255, 255, 255, 190, 24,
+ 0, 0, 0, 0, 0, 0, 0, 0, 27, 195, 255, 255, 255, 196, 1, 0,
+ 0, 228, 255, 255, 255, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 27, 255, 255, 255, 255, 255, 179, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 229, 138, 44,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 136, 228, 255, 255, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73,
+ 237, 255, 255, 255, 255, 97, 0, 0, 0, 155, 255, 255, 255, 190, 0, 0,
+ 0, 0, 0, 0, 0, 70, 245, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 176, 3, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 198, 255, 255,
+ 255, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 208, 255, 255, 255, 116,
+ 0, 0, 0, 0, 0, 0, 119, 255, 255, 255, 209, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0,
+ 4, 14, 38, 79, 146, 236, 255, 255, 255, 255, 141, 0, 0, 0, 139, 255,
+ 255, 255, 144, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 102, 255, 255, 255, 184, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 138, 255, 255, 255, 144, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 255, 255, 255, 255, 0, 64, 240, 255, 255, 255, 255, 195, 17,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 75, 255,
+ 255, 255, 245, 14, 0, 0, 0, 0, 0, 0, 15, 245, 255, 255, 255, 74,
+ 0, 0, 0, 255, 255, 255, 255, 0,
+ 0, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 223,
+ 255, 255, 237, 2, 0, 0, 0, 0, 0, 51, 255, 255, 255, 152, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 250, 255, 255, 255, 6, 0, 0, 0, 0,
+ 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 245,
+ 255, 255, 253, 60, 0, 0, 0, 0, 67, 255, 255, 255, 243, 14, 0, 0,
+ 0, 0, 95, 255, 255, 248, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 83, 255, 255, 255,
+ 255, 255, 183, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 255,
+ 255, 255, 189, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 193, 255, 255, 255, 60, 0, 0, 52, 195, 255, 255, 255, 187, 35, 0, 255,
+ 255, 255, 0, 35, 188, 255, 255, 255, 194, 51, 0, 0, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 149, 255, 255, 255,
+ 155, 0, 0, 0, 0, 0, 211, 255, 255, 255, 51, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 255, 255, 255, 209, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 74, 255, 255, 255, 254, 38, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 12, 36, 82, 158, 245, 255, 255, 255, 237,
+ 41, 0, 0, 0, 0, 0, 0, 0, 1, 190, 255, 255, 255, 103, 0, 0,
+ 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 241, 92, 0, 0, 0, 0, 0, 188, 255, 255, 255, 78, 0, 48, 201, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 240, 127, 11, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 130, 255, 255,
+ 255, 208, 0, 0, 0, 0, 0, 0, 0, 0, 33, 231, 255, 255, 255, 240,
+ 144, 69, 24, 6, 7, 25, 71, 148, 242, 255, 255, 255, 229, 30, 0, 0,
+ 0, 206, 255, 255, 255, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 74, 255, 255, 255, 255, 255, 208, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 205, 111, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 21, 110, 204, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 142, 254,
+ 255, 255, 255, 255, 171, 0, 0, 0, 7, 240, 255, 255, 255, 72, 0, 0,
+ 0, 0, 0, 0, 32, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 140, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 124, 255, 255,
+ 255, 151, 0, 0, 0, 0, 0, 0, 0, 0, 50, 255, 255, 255, 251, 23,
+ 0, 0, 0, 0, 0, 0, 26, 252, 255, 255, 255, 51, 0, 0, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 254, 143, 2, 0, 0, 0, 183, 255,
+ 255, 255, 89, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 53, 255, 255, 255, 213, 0, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0,
+ 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 0, 0, 0, 183, 255, 255, 255, 88, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 255, 255, 255, 255, 87, 249, 255, 255, 255, 255, 173, 8, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 2, 226,
+ 255, 255, 255, 106, 0, 0, 0, 0, 0, 0, 107, 255, 255, 255, 225, 2,
+ 0, 0, 0, 255, 255, 255, 255, 0,
+ 0, 254, 255, 255, 255, 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 255,
+ 255, 255, 168, 0, 0, 0, 0, 0, 0, 122, 255, 255, 255, 81, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 235, 255, 255, 255, 41, 0, 0, 0, 0,
+ 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 125,
+ 255, 255, 255, 238, 94, 15, 16, 98, 241, 255, 255, 255, 122, 0, 0, 0,
+ 0, 14, 231, 255, 255, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 102, 252, 255, 255, 255,
+ 255, 255, 255, 179, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 114, 255,
+ 255, 255, 138, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 141, 255, 255, 255, 111, 0, 152, 254, 255, 255, 235, 94, 0, 0, 0, 255,
+ 255, 255, 0, 0, 0, 94, 235, 255, 255, 254, 151, 0, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 225, 255, 255, 255,
+ 77, 0, 0, 0, 0, 0, 230, 255, 255, 255, 28, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 255, 255, 255, 228, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 17, 227, 255, 255, 255, 172, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 209, 40,
+ 0, 0, 0, 0, 0, 0, 0, 0, 106, 255, 255, 255, 191, 1, 0, 0,
+ 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 145, 0, 0, 0, 0, 209, 255, 255, 255, 51, 87, 247, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 210, 32, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 230, 255, 255,
+ 255, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 202, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 200, 31, 0, 0, 0,
+ 0, 152, 255, 255, 255, 141, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 143, 255, 255, 255, 255, 255, 227, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 39, 134, 225, 255, 255, 255, 255, 255, 255,
+ 255, 255, 251, 179, 85, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 7, 84, 178, 251, 255, 255, 255, 255, 255, 255, 255, 255,
+ 225, 134, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 206, 255, 255,
+ 255, 255, 255, 207, 12, 0, 0, 0, 67, 255, 255, 255, 231, 2, 0, 0,
+ 0, 0, 0, 0, 182, 255, 255, 255, 255, 226, 112, 40, 9, 11, 42, 116,
+ 230, 255, 254, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 60, 255, 255,
+ 255, 202, 0, 0, 0, 0, 0, 0, 0, 0, 147, 255, 255, 255, 177, 0,
+ 0, 0, 0, 0, 0, 0, 0, 181, 255, 255, 255, 148, 0, 0, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 247, 144, 43, 0, 0, 0, 0, 0, 213, 255,
+ 255, 255, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 35, 255, 255, 255, 236, 0, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0,
+ 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 0, 0, 0, 213, 255, 255, 255, 48, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 255, 255, 255, 255, 254, 255, 255, 255, 255, 146, 2, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 127,
+ 255, 255, 255, 208, 0, 0, 0, 0, 0, 0, 209, 255, 255, 255, 126, 0,
+ 0, 0, 0, 255, 255, 255, 255, 0,
+ 0, 241, 255, 255, 255, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 255,
+ 255, 255, 96, 0, 0, 0, 0, 0, 0, 192, 255, 255, 251, 14, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 197, 255, 255, 255, 157, 0, 0, 0, 0,
+ 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,
+ 189, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 185, 3, 0, 0, 0,
+ 0, 141, 255, 255, 226, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 103, 254, 255, 255, 245, 214,
+ 255, 255, 255, 255, 175, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 158, 255,
+ 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 98, 255, 255, 255, 156, 0, 134, 255, 255, 161, 19, 0, 0, 0, 0, 255,
+ 255, 255, 0, 0, 0, 0, 20, 162, 255, 255, 133, 0, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 255, 255, 255, 246,
+ 9, 0, 0, 0, 0, 0, 244, 255, 255, 255, 18, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 255, 255, 255, 243, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 181, 255, 255, 255, 246, 35, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 207, 91, 2, 0,
+ 0, 0, 0, 0, 0, 0, 0, 32, 243, 255, 255, 246, 35, 0, 0, 0,
+ 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 245, 179, 112, 67, 29, 13, 4, 19, 40, 91, 157, 244, 255, 255,
+ 255, 255, 255, 125, 0, 0, 0, 225, 255, 255, 255, 97, 251, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 234, 31, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 255, 255, 255,
+ 240, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 86, 196, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 196, 85, 1, 0, 0, 0, 0,
+ 0, 87, 255, 255, 255, 238, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 15, 240, 255, 255, 255, 255, 255, 242, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 66, 160, 243, 255, 255, 255,
+ 255, 255, 255, 255, 255, 239, 152, 58, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 58, 152, 238, 255, 255, 255, 255, 255, 255, 255, 255, 243, 160, 66,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 196, 255, 255, 255,
+ 255, 255, 209, 19, 0, 0, 0, 0, 130, 255, 255, 255, 153, 0, 0, 0,
+ 0, 0, 0, 44, 255, 255, 255, 255, 185, 14, 0, 0, 0, 0, 0, 0,
+ 18, 193, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 29, 255, 255,
+ 255, 225, 0, 0, 0, 0, 0, 0, 0, 7, 237, 255, 255, 255, 80, 0,
+ 0, 0, 0, 0, 0, 0, 0, 83, 255, 255, 255, 238, 7, 0, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 239, 135, 14, 0, 0, 0, 0, 237, 255,
+ 255, 255, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 18, 255, 255, 255, 246, 0, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0,
+ 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 0, 0, 0, 237, 255, 255, 255, 32, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 230, 18, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 28,
+ 252, 255, 255, 255, 55, 0, 0, 0, 0, 56, 255, 255, 255, 252, 27, 0,
+ 0, 0, 0, 255, 255, 255, 255, 0,
+ 0, 226, 255, 255, 255, 225, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 184, 255,
+ 255, 255, 25, 0, 0, 0, 0, 0, 12, 250, 255, 255, 195, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 119, 255, 255, 255, 255, 152, 16, 0, 0,
+ 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 9, 166, 255, 255, 255, 255, 255, 255, 255, 255, 162, 7, 0, 0, 0, 0,
+ 41, 250, 255, 255, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 252, 255, 255, 255, 114, 22,
+ 213, 255, 255, 255, 255, 170, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 22, 255, 255, 255, 236, 0, 0, 0, 0, 0, 0, 0, 0, 189, 255,
+ 255, 255, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 67, 255, 255, 255, 187, 0, 13, 195, 67, 0, 0, 0, 0, 0, 0, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 67, 195, 12, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 125, 255, 255, 255, 178,
+ 0, 0, 0, 0, 0, 0, 251, 255, 255, 255, 9, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 255, 255, 255, 249, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 143, 255, 255, 255, 255, 111, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 195, 78, 0,
+ 0, 0, 0, 0, 0, 0, 1, 189, 255, 255, 255, 114, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 202, 95, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 142, 253,
+ 255, 255, 255, 255, 57, 0, 0, 240, 255, 255, 255, 239, 255, 255, 255, 255,
+ 218, 107, 39, 11, 11, 40, 108, 218, 255, 255, 255, 255, 205, 8, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255,
+ 145, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 174, 248, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 248, 174, 51, 0, 0, 0, 0,
+ 0, 12, 237, 255, 255, 255, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 163, 255, 255, 255, 255, 255, 255, 249, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 92, 187, 253,
+ 255, 255, 255, 255, 255, 255, 255, 255, 219, 126, 33, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33,
+ 126, 219, 255, 255, 255, 255, 255, 255, 255, 255, 253, 186, 92, 10, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 131, 255, 255, 255, 255,
+ 255, 206, 20, 0, 0, 0, 0, 0, 178, 255, 255, 255, 95, 0, 0, 0,
+ 0, 0, 0, 131, 255, 255, 255, 225, 13, 0, 0, 0, 0, 0, 0, 0,
+ 0, 17, 229, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 8, 255, 255,
+ 255, 246, 0, 0, 0, 0, 0, 0, 0, 86, 255, 255, 255, 233, 4, 0,
+ 0, 0, 0, 0, 0, 0, 0, 6, 236, 255, 255, 255, 87, 0, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 219, 38, 0, 0, 0, 247, 255,
+ 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 4, 255, 255, 255, 253, 0, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0,
+ 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 0, 0, 0, 248, 255, 255, 255, 16, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 255, 255, 255, 255, 201, 255, 255, 255, 255, 208, 21, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 178, 255, 255, 255, 157, 0, 0, 0, 0, 158, 255, 255, 255, 177, 0, 0,
+ 0, 0, 0, 255, 255, 255, 255, 0,
+ 0, 210, 255, 255, 255, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 247, 255,
+ 255, 208, 0, 0, 0, 0, 0, 0, 77, 255, 255, 255, 124, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 14, 233, 255, 255, 255, 255, 244, 162, 90,
+ 37, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 62, 157, 219, 244, 244, 219, 155, 58, 0, 0, 0, 0, 0, 0,
+ 188, 255, 255, 189, 0, 0, 0, 0, 0, 0, 57, 153, 218, 243, 244, 218,
+ 154, 58, 0, 0, 0, 0, 0, 0, 17, 233, 255, 255, 255, 201, 2, 0,
+ 23, 214, 255, 255, 255, 255, 166, 2, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 67, 255, 255, 255, 196, 0, 0, 0, 0, 0, 0, 0, 0, 219, 255,
+ 255, 255, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 36, 255, 255, 255, 218, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 203, 255, 255, 255, 101,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 2, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 255, 255, 255, 254, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 117, 255, 255, 255, 255, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 174,
+ 14, 0, 0, 0, 0, 0, 105, 255, 255, 255, 200, 2, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 75,
+ 249, 255, 255, 255, 198, 0, 0, 253, 255, 255, 255, 255, 255, 255, 255, 146,
+ 5, 0, 0, 0, 0, 0, 0, 5, 150, 255, 255, 255, 255, 119, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 255, 255, 255, 254,
+ 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 140, 254, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 141, 4, 0, 0,
+ 0, 0, 127, 255, 255, 255, 255, 146, 5, 0, 0, 0, 0, 0, 0, 5,
+ 149, 255, 255, 255, 255, 255, 255, 255, 253, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27,
+ 119, 212, 255, 255, 255, 255, 255, 255, 255, 255, 254, 194, 99, 14, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 99, 193, 254,
+ 255, 255, 255, 255, 255, 255, 255, 255, 212, 118, 26, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 249, 255, 255, 255, 255,
+ 149, 7, 0, 0, 0, 0, 0, 0, 215, 255, 255, 255, 48, 0, 0, 0,
+ 0, 0, 0, 192, 255, 255, 255, 110, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 114, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 10, 255, 255,
+ 255, 242, 0, 0, 0, 0, 0, 0, 0, 184, 255, 255, 255, 141, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 145, 255, 255, 255, 185, 0, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 10, 24, 61, 113, 200, 255, 255, 255, 255, 223, 20, 0, 0, 254, 255,
+ 255, 255, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 19, 255, 255, 255, 246, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 254, 255, 255, 255, 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 255, 255, 255, 255, 14, 196, 255, 255, 255, 255, 213, 24, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 76, 255, 255, 255, 244, 14, 0, 0, 15, 245, 255, 255, 255, 75, 0, 0,
+ 0, 0, 0, 255, 255, 255, 255, 0,
+ 0, 194, 255, 255, 255, 195, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 255, 255,
+ 255, 136, 0, 0, 0, 0, 0, 0, 147, 255, 255, 255, 53, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 65, 248, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 120, 64, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84,
+ 255, 255, 251, 42, 0, 0, 0, 0, 6, 159, 255, 255, 255, 255, 255, 255,
+ 255, 255, 160, 6, 0, 0, 0, 0, 147, 255, 255, 255, 251, 47, 0, 0,
+ 0, 24, 216, 255, 255, 255, 255, 161, 2, 0, 0, 0, 0, 0, 0, 0,
+ 0, 113, 255, 255, 255, 157, 0, 0, 0, 0, 0, 0, 0, 0, 237, 255,
+ 255, 255, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 21, 255, 255, 255, 235, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 254, 255, 255, 254, 25,
+ 0, 0, 0, 0, 0, 0, 251, 255, 255, 255, 8, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 255, 255, 255, 250, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 106, 255, 255, 255, 255, 205, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 4, 19, 45, 95, 173, 250, 255, 255, 255, 255,
+ 194, 8, 0, 0, 0, 32, 243, 255, 255, 249, 43, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 97, 255, 255, 255, 255, 55, 0, 251, 255, 255, 255, 255, 255, 255, 160, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 164, 255, 255, 255, 233, 9, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, 255, 255, 255, 188,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 165, 255, 255, 255, 255, 237,
+ 140, 72, 26, 8, 9, 27, 73, 141, 238, 255, 255, 255, 255, 165, 1, 0,
+ 0, 0, 11, 212, 255, 255, 255, 255, 217, 107, 38, 11, 11, 39, 107, 218,
+ 255, 255, 255, 255, 236, 255, 255, 255, 240, 0, 255, 255, 255, 255, 255, 0,
+ 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 51, 145, 234, 255, 255, 255, 255, 255, 255, 255, 255, 246, 167, 73,
+ 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 73, 167, 246, 255, 255, 255,
+ 255, 255, 255, 255, 255, 234, 145, 51, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 126, 255, 255, 255, 255, 105,
+ 0, 0, 0, 0, 0, 0, 0, 0, 239, 255, 255, 255, 21, 0, 0, 0,
+ 0, 0, 0, 228, 255, 255, 255, 39, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 41, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 28, 255, 255,
+ 255, 220, 0, 0, 0, 0, 0, 0, 28, 253, 255, 255, 255, 43, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 47, 255, 255, 255, 253, 28, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 76, 237, 255, 255, 255, 162, 0, 0, 248, 255,
+ 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 36, 255, 255, 255, 236, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 248, 255, 255, 255, 16, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 255, 255, 255, 255, 0, 11, 190, 255, 255, 255, 255, 218, 28,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 3, 226, 255, 255, 255, 106, 0, 0, 107, 255, 255, 255, 226, 2, 0, 0,
+ 0, 0, 0, 255, 255, 255, 255, 0,
+ 0, 179, 255, 255, 255, 180, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 144, 255, 255,
+ 255, 64, 0, 0, 0, 0, 0, 0, 217, 255, 255, 236, 2, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 59, 221, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 248, 180, 91, 4, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 225,
+ 255, 255, 142, 0, 0, 0, 0, 3, 183, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 183, 3, 0, 0, 20, 246, 255, 255, 255, 141, 0, 0, 0,
+ 0, 0, 25, 217, 255, 255, 255, 255, 157, 1, 0, 0, 0, 0, 0, 0,
+ 0, 186, 255, 255, 255, 103, 0, 0, 0, 0, 0, 0, 0, 0, 247, 255,
+ 255, 255, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 10, 255, 255, 255, 245, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 102, 255, 255, 255, 202, 0,
+ 0, 0, 0, 0, 0, 0, 244, 255, 255, 255, 18, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 255, 255, 255, 243, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 102,
+ 254, 255, 255, 255, 214, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 184, 255, 255, 255,
+ 255, 139, 0, 0, 1, 188, 255, 255, 255, 125, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 198, 255, 255, 255, 130, 0, 244, 255, 255, 255, 255, 255, 238, 14, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 240, 255, 255, 255, 83, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 242, 255, 255, 255, 81,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 255, 255, 255, 255, 157, 12,
+ 0, 0, 0, 0, 0, 0, 0, 0, 13, 160, 255, 255, 255, 255, 111, 0,
+ 0, 0, 0, 38, 239, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 249, 84, 255, 255, 255, 224, 0, 255, 255, 255, 255, 255, 0,
+ 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 4, 78, 172, 248, 255, 255, 255, 255, 255, 255, 255, 255,
+ 231, 141, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 47, 141, 231, 255, 255, 255, 255, 255, 255,
+ 255, 255, 248, 171, 77, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 189, 255, 255, 255, 150, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 250, 255, 255, 255, 6, 0, 0, 0,
+ 0, 0, 0, 247, 255, 255, 255, 8, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 10, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 72, 255, 255,
+ 255, 196, 0, 0, 0, 0, 0, 0, 123, 255, 255, 255, 202, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 206, 255, 255, 255, 123, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 51, 250, 255, 255, 254, 38, 0, 237, 255,
+ 255, 255, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 55, 255, 255, 255, 212, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 238, 255, 255, 255, 32, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 255, 255, 255, 255, 0, 0, 9, 184, 255, 255, 255, 255, 223,
+ 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 0, 127, 255, 255, 255, 207, 0, 0, 208, 255, 255, 255, 126, 0, 0, 0,
+ 0, 0, 0, 255, 255, 255, 255, 0,
+ 0, 163, 255, 255, 255, 165, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 76, 0, 0, 0, 0, 0, 0, 10, 108, 218, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 225, 89, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 130, 255,
+ 255, 232, 14, 0, 0, 0, 0, 119, 255, 255, 255, 240, 99, 17, 16, 99,
+ 242, 255, 255, 255, 118, 0, 0, 105, 255, 255, 255, 243, 17, 0, 0, 0,
+ 0, 0, 0, 26, 219, 255, 255, 255, 255, 152, 1, 0, 0, 0, 0, 0,
+ 13, 249, 255, 255, 255, 28, 0, 0, 0, 0, 0, 0, 0, 0, 253, 255,
+ 255, 255, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 2, 255, 255, 255, 253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 179, 255, 255, 255, 124, 0,
+ 0, 0, 0, 0, 0, 0, 230, 255, 255, 255, 27, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 255, 255, 255, 228, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 102, 254,
+ 255, 255, 255, 220, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 160, 255, 255,
+ 255, 251, 30, 0, 104, 255, 255, 255, 209, 5, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 102, 255, 255, 255, 195, 0, 229, 255, 255, 255, 255, 255, 141, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 143, 255, 255, 255, 148, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 105, 255, 255, 255, 227, 3,
+ 0, 0, 0, 0, 0, 0, 0, 0, 17, 242, 255, 255, 255, 142, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 146, 255, 255, 255, 242, 16,
+ 0, 0, 0, 0, 39, 216, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 244, 84, 52, 255, 255, 255, 208, 0, 255, 255, 255, 255, 255, 0,
+ 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 17, 104, 198, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 209, 115, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 115, 209, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 198, 104, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 228, 255, 255, 255, 57, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 250, 255, 255, 255, 6, 0, 0, 0,
+ 0, 0, 0, 247, 255, 255, 255, 9, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 10, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 136, 255, 255,
+ 255, 136, 0, 0, 0, 0, 0, 0, 219, 255, 255, 255, 104, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 109, 255, 255, 255, 219, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 149, 255, 255, 255, 129, 0, 214, 255,
+ 255, 255, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 105, 255, 255, 255, 183, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 214, 255, 255, 255, 49, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 7, 178, 255, 255, 255, 255,
+ 227, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 0, 28, 252, 255, 255, 255, 54, 55, 255, 255, 255, 252, 27, 0, 0, 0,
+ 0, 0, 0, 255, 255, 255, 255, 0,
+ 0, 148, 255, 255, 255, 150, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 122, 179,
+ 230, 255, 255, 255, 255, 255, 255, 255, 255, 255, 159, 2, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 247, 255,
+ 255, 95, 0, 0, 0, 0, 14, 243, 255, 255, 254, 63, 0, 0, 0, 0,
+ 69, 255, 255, 255, 242, 13, 0, 175, 255, 255, 255, 143, 0, 0, 0, 0,
+ 0, 0, 0, 0, 28, 220, 255, 255, 255, 255, 147, 0, 0, 0, 0, 0,
+ 100, 255, 255, 255, 208, 0, 0, 0, 0, 0, 0, 0, 0, 0, 246, 255,
+ 255, 255, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 11, 255, 255, 255, 245, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 246, 255, 255, 255, 47, 0,
+ 0, 0, 0, 0, 0, 0, 211, 255, 255, 255, 50, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 255, 255, 255, 209, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 103, 254, 255,
+ 255, 255, 225, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 218, 255,
+ 255, 255, 127, 0, 243, 255, 255, 252, 51, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 38, 255, 255, 255, 221, 0, 209, 255, 255, 255, 255, 255, 72, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 74, 255, 255, 255, 204, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 210, 255, 255, 255, 125, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 109, 255, 255, 255, 215, 4, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 217, 255, 255, 255, 109,
+ 0, 0, 0, 0, 0, 14, 133, 242, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 203, 38, 0, 79, 255, 255, 255, 187, 0, 255, 255, 255, 255, 255, 0,
+ 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 130, 222, 255, 255, 255,
+ 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 222, 130, 36,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, 255, 255, 255, 13, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 239, 255, 255, 255, 18, 0, 0, 0,
+ 0, 0, 0, 229, 255, 255, 255, 39, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 40, 255, 255, 255, 255, 0, 0, 0, 0, 0, 4, 230, 255, 255,
+ 255, 59, 0, 0, 0, 0, 0, 62, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 64, 255, 255, 255, 190, 0, 184, 255,
+ 255, 255, 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 166, 255, 255, 255, 137, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 183, 255, 255, 255, 91, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 5, 171, 255, 255, 255,
+ 255, 230, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 0, 0, 179, 255, 255, 255, 156, 157, 255, 255, 255, 178, 0, 0, 0, 0,
+ 0, 0, 0, 255, 255, 255, 255, 0,
+ 0, 132, 255, 255, 255, 135, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 151, 208, 255, 255, 255, 255, 255, 255, 146, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 177, 255, 255,
+ 198, 1, 0, 0, 0, 0, 103, 255, 255, 255, 176, 0, 0, 0, 0, 0,
+ 0, 181, 255, 255, 255, 103, 0, 222, 255, 255, 255, 67, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 29, 222, 255, 255, 255, 255, 143, 0, 0, 0, 0,
+ 212, 255, 255, 255, 122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 236, 255,
+ 255, 255, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 21, 255, 255, 255, 235, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 78, 255, 255, 255, 225, 0, 0,
+ 0, 0, 0, 0, 0, 0, 184, 255, 255, 255, 83, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, 255, 255, 255, 181, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 106, 254, 255, 255,
+ 255, 228, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 106, 255,
+ 255, 255, 191, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 12, 255, 255, 255, 244, 0, 180, 255, 255, 255, 255, 255, 26, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 255, 255, 255, 226, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, 255, 255, 255, 250, 24, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 181, 255, 255, 255, 105, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 106, 255, 255, 255, 180,
+ 0, 0, 0, 0, 0, 0, 0, 18, 94, 162, 212, 233, 250, 244, 214, 158,
+ 72, 1, 0, 0, 128, 255, 255, 255, 139, 0, 255, 255, 255, 255, 255, 0,
+ 0, 21, 255, 255, 255, 223, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 63, 157, 241,
+ 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 241, 156, 62, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 2, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 219, 255, 255, 255, 48, 0, 0, 0,
+ 0, 0, 0, 194, 255, 255, 255, 110, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 111, 255, 255, 255, 255, 0, 0, 0, 0, 0, 119, 255, 255, 255,
+ 219, 3, 0, 0, 0, 0, 0, 159, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 159, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 21, 255, 255, 255, 231, 0, 140, 255,
+ 255, 255, 143, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 230, 255, 255, 255, 84, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 139, 255, 255, 255, 149, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 3, 164, 255, 255,
+ 255, 255, 234, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 0, 0, 77, 255, 255, 255, 244, 244, 255, 255, 255, 76, 0, 0, 0, 0,
+ 0, 0, 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 0, 0, 29, 135, 247, 255, 255, 255, 254, 52, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 255, 255, 253,
+ 51, 0, 0, 0, 0, 0, 169, 255, 255, 255, 89, 0, 0, 0, 0, 0,
+ 0, 93, 255, 255, 255, 168, 0, 245, 255, 255, 255, 18, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 30, 223, 255, 255, 255, 255, 137, 0, 0, 77,
+ 255, 255, 255, 246, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 218, 255,
+ 255, 255, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 38, 255, 255, 255, 217, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 156, 255, 255, 255, 148, 0, 0,
+ 0, 0, 0, 0, 0, 0, 150, 255, 255, 255, 116, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 117, 255, 255, 255, 148, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 109, 255, 255, 255, 255,
+ 231, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 255,
+ 255, 255, 233, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 12, 255, 255, 255, 245, 0, 146, 255, 255, 255, 255, 255, 8, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 255, 255, 255, 246, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 167, 255, 255, 255, 168, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 227, 255, 255, 255, 38, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, 255, 255, 255, 227,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 184, 255, 255, 255, 86, 0, 255, 255, 255, 255, 255, 0,
+ 0, 86, 255, 255, 255, 126, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9,
+ 89, 183, 252, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 255, 255, 252, 183, 89, 9, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 184, 255, 255, 255, 89, 0, 0, 0,
+ 0, 0, 0, 133, 255, 255, 255, 223, 12, 0, 0, 0, 0, 0, 0, 0,
+ 0, 13, 224, 255, 255, 255, 255, 0, 0, 0, 0, 63, 247, 255, 255, 255,
+ 80, 0, 0, 0, 0, 0, 12, 244, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 244, 12,
+ 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 5, 255, 255, 255, 248, 0, 87, 255,
+ 255, 255, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 92, 255, 255, 255, 251, 16, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 86, 255, 255, 255, 209, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 2, 156, 255,
+ 255, 255, 255, 238, 53, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 0, 0, 3, 227, 255, 255, 255, 255, 255, 255, 226, 3, 0, 0, 0, 0,
+ 0, 0, 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 179, 255, 255, 254,
+ 18, 0, 0, 0, 0, 0, 12, 251, 255, 255, 192, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 0, 0, 0, 0, 46, 236, 255, 255, 255, 151, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 217, 255, 255, 153,
+ 0, 0, 0, 0, 0, 0, 219, 255, 255, 255, 39, 0, 0, 0, 0, 0,
+ 0, 41, 255, 255, 255, 217, 0, 248, 255, 255, 255, 15, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 31, 224, 255, 255, 255, 255, 132, 7, 219,
+ 255, 255, 255, 152, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 188, 255,
+ 255, 255, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 68, 255, 255, 255, 186, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 231, 255, 255, 255, 70, 0, 0,
+ 0, 0, 0, 0, 0, 0, 106, 255, 255, 255, 169, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 255, 255, 255, 104, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 113, 255, 255, 255, 255, 233,
+ 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 255,
+ 255, 255, 249, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 37, 255, 255, 255, 223, 0, 100, 255, 255, 255, 255, 255, 8, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 255, 255, 255, 246, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 23, 249, 255, 255, 255, 62, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 247, 255, 255, 255, 11, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 255, 255, 255, 246,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 5, 239, 255, 255, 255, 33, 0, 0, 0, 0, 0, 0, 0,
+ 0, 152, 255, 255, 253, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 24, 115, 209, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 209, 115, 24, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 141, 255, 255, 255, 150, 0, 0, 0,
+ 0, 0, 0, 47, 255, 255, 255, 255, 182, 12, 0, 0, 0, 0, 0, 0,
+ 12, 182, 255, 255, 255, 255, 255, 0, 0, 0, 91, 243, 255, 255, 255, 180,
+ 0, 0, 0, 0, 0, 0, 98, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 98,
+ 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 22, 255, 255, 255, 244, 0, 19, 253,
+ 255, 255, 254, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 213, 255, 255, 255, 184, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 18, 252, 255, 255, 255, 47, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 1, 149,
+ 255, 255, 255, 255, 241, 58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 0, 0, 0, 128, 255, 255, 255, 255, 255, 255, 127, 0, 0, 0, 0, 0,
+ 0, 0, 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 239, 255, 255, 211,
+ 0, 0, 0, 0, 0, 0, 70, 255, 255, 255, 130, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 0, 0, 0, 0, 0, 101, 255, 255, 255, 217, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, 255, 255, 238, 20,
+ 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 11, 0, 0, 0, 0, 0,
+ 0, 13, 255, 255, 255, 238, 0, 226, 255, 255, 255, 44, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 33, 226, 255, 255, 255, 255, 202, 255,
+ 255, 255, 249, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 156, 255,
+ 255, 255, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 99, 255, 255, 255, 155, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 55, 255, 255, 255, 242, 6, 0, 0,
+ 0, 0, 0, 0, 0, 0, 54, 255, 255, 255, 232, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 233, 255, 255, 255, 53, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 117, 255, 255, 255, 255, 234, 43,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255,
+ 255, 255, 239, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 99, 255, 255, 255, 199, 0, 47, 255, 255, 255, 255, 255, 26, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 255, 255, 255, 226, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 123, 255, 255, 255, 211, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 246, 255, 255, 255, 10, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 255, 255, 255, 246,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 83, 255, 255, 255, 216, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 217, 255, 255, 186, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 81, 255, 255, 255, 223, 1, 0, 0,
+ 0, 0, 0, 0, 186, 255, 255, 255, 255, 224, 110, 39, 9, 8, 38, 109,
+ 223, 255, 254, 255, 255, 255, 255, 29, 92, 196, 255, 255, 255, 255, 199, 11,
+ 0, 0, 0, 0, 0, 0, 196, 255, 255, 255, 131, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 131, 255, 255, 255, 196,
+ 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 67, 255, 255, 255, 222, 0, 0, 190,
+ 255, 255, 255, 142, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 139, 255, 255, 255, 255, 76, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 188, 255, 255, 255, 161, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 141, 255, 255, 255, 255, 244, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 0, 0, 0, 29, 252, 255, 255, 255, 255, 252, 28, 0, 0, 0, 0, 0,
+ 0, 0, 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 255, 255, 255, 149,
+ 0, 0, 0, 0, 0, 0, 132, 255, 255, 255, 67, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 0, 0, 0, 0, 0, 21, 255, 255, 255, 244, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 243, 255, 255, 106, 0,
+ 0, 0, 0, 0, 0, 0, 252, 255, 255, 255, 2, 0, 0, 0, 0, 0,
+ 0, 3, 255, 255, 255, 251, 0, 200, 255, 255, 255, 115, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 227, 255, 255, 255, 255, 255,
+ 255, 255, 135, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 113, 255,
+ 255, 255, 141, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 143, 255, 255, 255, 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 133, 255, 255, 255, 171, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 4, 242, 255, 255, 255, 52, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 52, 255, 255, 255, 240, 3, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 121, 255, 255, 255, 255, 235, 45, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 255,
+ 255, 255, 217, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 192, 255, 255, 255, 139, 0, 1, 234, 255, 255, 255, 255, 72, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 74, 255, 255, 255, 202, 0,
+ 0, 0, 0, 0, 0, 0, 0, 3, 225, 255, 255, 255, 105, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 227, 255, 255, 255, 37, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, 255, 255, 255, 226,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 181, 255, 255, 255, 116, 0, 0, 0, 0, 0, 0, 0, 0,
+ 27, 255, 255, 255, 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 17, 250, 255, 255, 255, 62, 0, 0,
+ 0, 0, 0, 0, 35, 243, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 138, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 187, 17, 0,
+ 0, 0, 0, 0, 0, 37, 255, 255, 255, 254, 34, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 255, 255, 255, 255,
+ 37, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 154, 255, 255, 255, 185, 0, 0, 91,
+ 255, 255, 255, 241, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 101, 255, 255, 255, 255, 200, 1, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 87, 255, 255, 255, 249, 44, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 132, 255, 255, 255, 255, 246, 70, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 179, 255, 255, 255, 255, 179, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 255, 255, 255, 87,
+ 0, 0, 0, 0, 0, 0, 195, 255, 255, 250, 10, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 0, 0, 0, 0, 0, 8, 255, 255, 255, 244, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 166, 255, 255, 208, 3, 0,
+ 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 11, 0, 0, 0, 0, 0,
+ 0, 12, 255, 255, 255, 238, 0, 137, 255, 255, 255, 218, 5, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 228, 255, 255, 255, 255,
+ 255, 227, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 255,
+ 255, 255, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 194, 255, 255, 255, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 210, 255, 255, 255, 93, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 169, 255, 255, 255, 159, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 160, 255, 255, 255, 167, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 124, 255, 255, 255, 255, 237, 47, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120, 255,
+ 255, 255, 182, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 86, 255, 255, 255, 255, 66, 0, 0, 157, 255, 255, 255, 255, 142, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 144, 255, 255, 255, 144, 0,
+ 0, 0, 0, 0, 0, 0, 0, 79, 255, 255, 255, 242, 12, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 202, 255, 255, 255, 104, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 255, 255, 255, 200,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 65, 255, 255, 255, 250, 21, 0, 0, 0, 0, 0, 0, 0, 0,
+ 92, 255, 255, 237, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 181, 255, 255, 255, 178, 0, 0,
+ 0, 0, 0, 0, 0, 76, 247, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 178, 3, 255, 255, 255, 255, 255, 255, 255, 255, 247, 113, 1, 0, 0,
+ 0, 0, 0, 0, 0, 135, 255, 255, 255, 192, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 193, 255, 255, 255,
+ 134, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 55, 251, 255, 255, 255, 112, 0, 0, 5,
+ 222, 255, 255, 255, 189, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 140,
+ 255, 255, 255, 255, 249, 51, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 4, 217, 255, 255, 255, 215, 12, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 125, 255, 255, 255, 255, 248, 77, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 255, 255, 255, 255, 0,
+ 0, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 175, 255, 255, 255, 25,
+ 0, 0, 0, 0, 0, 8, 248, 255, 255, 199, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 0, 0, 0, 0, 0, 54, 255, 255, 255, 221, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 255, 255, 255, 60, 0, 0,
+ 0, 0, 0, 0, 0, 0, 220, 255, 255, 255, 39, 0, 0, 0, 0, 0,
+ 0, 41, 255, 255, 255, 218, 0, 60, 255, 255, 255, 255, 124, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 235, 255, 255, 255,
+ 255, 169, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 252,
+ 255, 255, 242, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3,
+ 243, 255, 255, 250, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 32, 255, 255, 255, 252, 19, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 78, 255, 255, 255, 248, 35, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 35, 248, 255, 255, 255, 76, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 127, 255, 255, 255, 255, 238, 49, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 230, 255,
+ 255, 255, 107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 181,
+ 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61,
+ 245, 255, 255, 255, 216, 4, 0, 0, 62, 255, 255, 255, 255, 238, 14, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 240, 255, 255, 255, 78, 0,
+ 0, 0, 0, 0, 0, 0, 0, 185, 255, 255, 255, 148, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 140, 255, 255, 255, 214, 3, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 218, 255, 255, 255, 139,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 10, 215, 255, 255, 255, 154, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 158, 255, 255, 147, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, 255, 255, 255, 255, 58, 0,
+ 0, 0, 0, 0, 0, 0, 61, 219, 255, 255, 255, 255, 255, 255, 255, 254,
+ 146, 4, 0, 255, 255, 255, 255, 255, 255, 230, 125, 21, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 229, 255, 255, 255, 95, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 255, 255, 255,
+ 228, 3, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 78, 238, 255, 255, 255, 246, 26, 0, 0, 0,
+ 88, 255, 255, 255, 255, 141, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 10, 159, 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 111, 226, 255,
+ 255, 255, 255, 255, 106, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 79, 255, 255, 255, 255, 180, 12, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 116, 255, 255, 255, 255, 250, 84, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 255, 255, 255, 255, 0,
+ 0, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 236, 255, 255, 219, 0,
+ 0, 0, 0, 0, 0, 63, 255, 255, 255, 136, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 0, 0, 0, 0, 1, 177, 255, 255, 255, 168, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 3, 209, 255, 255, 164, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 171, 255, 255, 255, 90, 0, 0, 0, 0, 0,
+ 0, 92, 255, 255, 255, 169, 0, 1, 201, 255, 255, 255, 254, 95, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 144, 254, 255, 255, 255,
+ 255, 255, 140, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 198,
+ 255, 255, 255, 58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60,
+ 255, 255, 255, 193, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 109, 255, 255, 255, 194, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 2, 221, 255, 255, 255, 199, 4, 0, 0,
+ 0, 0, 0, 0, 0, 0, 4, 200, 255, 255, 255, 220, 2, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 128, 255, 255, 255, 255, 238, 50, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 177, 31, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 178, 255, 255,
+ 255, 250, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 255,
+ 249, 151, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 122, 250,
+ 255, 255, 255, 255, 80, 0, 0, 0, 0, 205, 255, 255, 255, 255, 160, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 164, 255, 255, 255, 227, 6, 0,
+ 0, 0, 0, 0, 0, 0, 36, 254, 255, 255, 255, 42, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 69, 255, 255, 255, 255, 139, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, 255, 255, 255, 255, 68,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 10, 190, 255, 255, 255, 232, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 223, 255, 255, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 217, 255, 255, 255, 211, 7,
+ 0, 0, 0, 0, 0, 0, 0, 6, 97, 179, 226, 248, 235, 206, 139, 38,
+ 0, 0, 0, 245, 224, 203, 172, 114, 53, 4, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 74, 255, 255, 255, 242, 10, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 243, 255, 255,
+ 255, 73, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 9, 23, 60, 113, 200, 255, 255, 255, 255, 255, 129, 0, 0, 0, 0,
+ 0, 177, 255, 255, 255, 255, 163, 6, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 72, 221, 255, 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 2, 9, 24, 48, 84, 129, 190, 250, 255, 255, 255,
+ 255, 255, 255, 121, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 165, 255, 255, 255, 255, 212, 36,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 108, 254, 255, 255, 255, 252, 91, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 255, 255, 255, 255, 0,
+ 0, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 255, 255, 255, 157, 0,
+ 0, 0, 0, 0, 0, 126, 255, 255, 255, 74, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 208, 112, 23, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 0, 0, 0, 13, 161, 255, 255, 255, 255, 88, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 108, 255, 255, 242, 25, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 106, 255, 255, 255, 177, 0, 0, 0, 0, 0,
+ 0, 181, 255, 255, 255, 104, 0, 0, 61, 255, 255, 255, 255, 255, 145, 13,
+ 0, 0, 0, 0, 0, 0, 0, 0, 2, 87, 214, 255, 255, 255, 255, 255,
+ 255, 255, 255, 138, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127,
+ 255, 255, 255, 131, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 133,
+ 255, 255, 255, 122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 187, 255, 255, 255, 117, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 93, 255, 255, 255, 255, 182, 15, 0,
+ 0, 0, 0, 0, 0, 16, 184, 255, 255, 255, 255, 91, 0, 0, 0, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 0, 128, 255, 255, 255, 255, 239, 51, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 245, 140, 37,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 200, 255, 255, 255,
+ 255, 141, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 255,
+ 255, 255, 255, 203, 137, 80, 39, 21, 4, 13, 32, 79, 142, 235, 255, 255,
+ 255, 255, 255, 160, 0, 0, 0, 0, 0, 69, 255, 255, 255, 255, 255, 146,
+ 5, 0, 0, 0, 0, 0, 0, 5, 149, 255, 255, 255, 255, 110, 0, 0,
+ 0, 0, 0, 0, 0, 0, 141, 255, 255, 255, 192, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 208, 255, 255, 255, 255, 153, 11,
+ 0, 0, 0, 0, 0, 0, 0, 0, 15, 163, 255, 255, 255, 255, 206, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56,
+ 207, 255, 255, 255, 255, 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 85, 255, 255, 255, 255, 153,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 171, 255, 255, 255, 156, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 157, 255, 255,
+ 255, 170, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 181, 5, 0, 0, 0, 0,
+ 0, 17, 218, 255, 255, 255, 255, 222, 92, 2, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 76, 191, 255, 255, 255, 0, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 239, 83, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 206, 255, 255, 255, 255, 248,
+ 152, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32,
+ 138, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 101, 253, 255, 255, 255, 253, 98, 0, 0, 0, 0,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 255, 255, 255, 255, 0,
+ 0, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 255, 255, 255, 95, 0,
+ 0, 0, 0, 0, 0, 188, 255, 255, 252, 14, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 254, 206, 138, 86, 45, 9, 0,
+ 0, 255, 255, 21, 70, 143, 237, 255, 255, 255, 255, 211, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 20, 238, 255, 255, 117, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 15, 244, 255, 255, 254, 65, 0, 0, 0, 0,
+ 69, 255, 255, 255, 243, 14, 0, 0, 0, 128, 255, 255, 255, 255, 255, 238,
+ 141, 70, 26, 6, 14, 37, 75, 143, 221, 255, 255, 255, 255, 255, 249, 231,
+ 255, 255, 255, 255, 135, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48,
+ 255, 255, 255, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 210,
+ 255, 255, 255, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 14, 250, 255, 255, 255, 39, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 190, 255, 255, 255, 255, 228, 123,
+ 46, 14, 15, 47, 125, 229, 255, 255, 255, 255, 189, 1, 0, 0, 0, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 48, 0, 255, 255, 255, 254,
+ 195, 132, 74, 37, 19, 4, 12, 27, 65, 114, 196, 254, 255, 255, 255, 255,
+ 219, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 181, 4, 0, 0, 0, 0, 0, 0, 161, 255, 255, 255, 255, 255,
+ 217, 107, 38, 11, 11, 39, 107, 218, 255, 255, 255, 255, 194, 4, 0, 0,
+ 0, 0, 0, 0, 0, 9, 237, 255, 255, 255, 85, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 254, 255, 255, 255, 255, 236,
+ 139, 70, 25, 7, 8, 26, 73, 143, 239, 255, 255, 255, 255, 254, 70, 0,
+ 0, 0, 0, 230, 179, 128, 78, 45, 21, 8, 3, 21, 45, 104, 184, 254,
+ 255, 255, 255, 255, 149, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 185, 255, 255, 255, 255,
+ 119, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 19, 249, 255, 255, 255, 59, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 59, 255, 255,
+ 255, 248, 18, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 166, 9, 0, 0, 0, 0, 0,
+ 0, 0, 33, 224, 255, 255, 255, 255, 255, 226, 154, 84, 46, 25, 6, 14,
+ 35, 56, 105, 170, 235, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 249,
+ 153, 21, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 209, 255, 255, 255, 255,
+ 255, 253, 200, 139, 79, 46, 28, 11, 3, 13, 24, 51, 86, 135, 203, 253,
+ 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 93, 252, 255, 255, 255, 254, 106, 0, 0, 0,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 255, 255, 255, 255, 0,
+ 0, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 255, 255, 255, 33, 0,
+ 0, 0, 0, 0, 5, 245, 255, 255, 205, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 249,
+ 235, 255, 255, 255, 255, 255, 255, 255, 255, 255, 241, 47, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 154, 255, 255, 216, 5, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 123, 255, 255, 255, 240, 98, 16, 15, 98,
+ 241, 255, 255, 255, 119, 0, 0, 0, 0, 0, 152, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 225, 56, 35,
+ 229, 255, 255, 255, 255, 133, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 215, 255, 255, 255, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 255,
+ 255, 255, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 86, 255, 255, 255, 218, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 226, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 225, 25, 0, 0, 0, 0, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 48, 0, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 224,
+ 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 251, 126, 3, 0, 0, 0, 0, 0, 0, 0, 11, 204, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 226, 24, 0, 0, 0,
+ 0, 0, 0, 0, 0, 97, 255, 255, 255, 230, 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 121, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 120, 0, 0,
+ 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 159, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 235, 255, 255, 255,
+ 255, 123, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 110, 255, 255, 255, 217, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 218, 255,
+ 255, 255, 109, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 205, 79, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 29, 203, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 207, 122, 24,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 178, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 213, 65, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 6, 255, 255,
+ 255, 248, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 86, 251, 255, 255, 255, 255, 114, 0, 0,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 255, 255, 255, 255, 0,
+ 0, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 255, 255, 227, 0, 0,
+ 0, 0, 0, 0, 57, 255, 255, 255, 143, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 227, 58, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 52, 253, 255, 255, 70, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 3, 186, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 183, 3, 0, 0, 0, 0, 0, 0, 106, 248, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 251, 148, 13, 0, 0,
+ 40, 233, 255, 255, 255, 255, 130, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 123, 255, 255, 255, 140, 0, 0, 0, 0, 0, 0, 0, 0, 0, 142, 255,
+ 255, 255, 119, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 163, 255, 255, 255, 140, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 219, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 218, 36, 0, 0, 0, 0, 0, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 48, 0, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 176, 25,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 46,
+ 139, 226, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 248, 162,
+ 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 193, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 203, 26, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 203, 255, 255, 255, 129, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 115, 249, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 249, 115, 0, 0, 0,
+ 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 250, 119, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 251, 255, 255,
+ 255, 255, 161, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 69, 192, 11, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 207, 255, 255, 255, 120, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 121, 255,
+ 255, 255, 207, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 249, 234, 217, 175, 122, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 7, 132, 249, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 198, 66, 0, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 254, 249, 241, 224, 202, 170, 131, 83, 22, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 234, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 234, 116, 5, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 15, 255, 255,
+ 255, 236, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 79, 249, 255, 255, 255, 255, 123, 0,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 44, 132, 216, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 242, 141, 18, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 199, 255, 255, 174, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 8, 163, 255, 255, 255, 255, 255, 255,
+ 255, 255, 159, 6, 0, 0, 0, 0, 0, 0, 0, 0, 40, 163, 251, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 247, 158, 39, 0, 0, 0, 0,
+ 0, 46, 237, 255, 255, 255, 255, 129, 0, 0, 0, 0, 0, 0, 0, 0,
+ 25, 250, 255, 255, 236, 9, 0, 0, 0, 0, 0, 0, 0, 10, 237, 255,
+ 255, 249, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 3, 237, 255, 255, 255, 63, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 154, 252, 255, 255,
+ 255, 255, 255, 255, 255, 255, 252, 153, 16, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 48, 0, 46, 138, 225, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, 189, 80, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 41, 107, 158, 199, 229, 244, 251, 238, 221, 198, 146, 90, 20, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 120, 241, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 238, 122, 9, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 179, 253, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 179, 49, 0, 0, 0, 0,
+ 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253,
+ 176, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 89, 253, 255,
+ 255, 255, 255, 220, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 19, 150, 254, 255, 150, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 32, 147, 240, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 244, 165, 70, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 112,
+ 215, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, 187,
+ 86, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 255, 255,
+ 255, 223, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 89, 139, 178, 213, 233,
+ 248, 255, 255, 227, 194, 150, 83, 14, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 96, 255, 255, 247, 32, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 59, 155, 219, 244, 244, 219,
+ 155, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 102,
+ 167, 212, 232, 250, 248, 232, 200, 152, 88, 13, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 166, 255, 255, 255, 106, 0, 0, 0, 0, 0, 0, 0, 107, 255, 255,
+ 255, 163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 62, 255, 255, 255, 237, 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 123, 191,
+ 232, 250, 250, 231, 190, 123, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40,
+ 106, 157, 199, 229, 244, 251, 239, 224, 202, 154, 102, 33, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 101,
+ 173, 219, 245, 251, 233, 212, 162, 92, 15, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 110,
+ 167, 213, 231, 248, 248, 231, 213, 167, 110, 34, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 29, 86, 136, 178, 210, 235, 248, 253, 243, 219, 177, 114, 32,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 87, 249,
+ 255, 255, 255, 255, 255, 196, 83, 6, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 32, 146, 245, 255, 255, 255, 254, 67, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 7, 75, 136, 185, 219, 241, 251, 248,
+ 232, 215, 179, 128, 76, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 42, 110, 163, 201, 230, 245, 253, 249, 239, 215, 185, 141, 87, 21, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 255, 255,
+ 255, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 7, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 53, 255, 255, 255, 219, 3, 0, 0, 0, 0, 0, 3, 220, 255, 255,
+ 255, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 140, 255, 255, 255, 164, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61,
+ 229, 255, 255, 255, 255, 255, 255, 240, 167, 107, 60, 27, 10, 2, 10, 33,
+ 71, 125, 196, 253, 255, 255, 255, 255, 255, 205, 34, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 133, 255, 255,
+ 255, 150, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 6, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 183, 255, 255, 255, 99, 0, 0, 0, 0, 0, 100, 255, 255, 255,
+ 181, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 217, 255, 255, 255, 86, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 20, 171, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 250, 133, 7, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 220, 255, 255,
+ 255, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 5, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 55, 255, 255, 255, 226, 7, 0, 0, 0, 8, 227, 255, 255, 255,
+ 53, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 67, 202, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 175, 41, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 132, 255, 255, 255,
+ 252, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 4, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 176, 255, 255, 255, 119, 0, 0, 0, 120, 255, 255, 255, 175,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 63, 166, 243, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 237, 151, 43, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 3, 21, 73, 179, 255, 255, 255, 255,
+ 181, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 3, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 36, 249, 255, 255, 244, 30, 0, 31, 245, 255, 255, 249, 35,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 9, 70, 130, 178, 213, 237, 249, 252, 241, 217,
+ 181, 129, 66, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 252,
+ 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 2, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 138, 255, 255, 255, 177, 0, 178, 255, 255, 255, 137, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 107,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 233, 92, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 241, 221, 175, 103, 14, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 255, 251, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 32, 107, 168, 208, 237, 249, 250, 237, 209, 169, 109,
+ 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 247, 231, 214, 172, 119, 49, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 107,
+ 168, 208, 237, 249, 250, 237, 209, 169, 109, 33, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 248, 233, 218, 181, 132, 70, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 10, 85, 142, 195, 222, 238, 252, 243,
+ 230, 207, 170, 132, 76, 17, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 207, 255, 255, 255, 120, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 121, 255, 255, 255, 207, 0, 224, 255, 255, 255, 63, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 255, 255, 255, 255, 255, 255,
+ 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 255,
+ 255, 255, 224, 0, 0, 0, 171, 255, 255, 255, 249, 43, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 249, 255, 255, 255,
+ 167, 0, 0, 172, 255, 255, 255, 245, 34, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 245, 255, 255, 255, 172, 0,
+ 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 0, 217, 255, 255, 255, 86, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 9, 191, 255, 255, 255, 255, 190, 9, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 150, 255, 255,
+ 255, 216, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 53, 122,
+ 170, 213, 235, 250, 243, 225, 200, 142, 78, 4, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5,
+ 64, 133, 193, 218, 238, 251, 237, 213, 166, 107, 26, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 16, 87, 162,
+ 206, 229, 249, 238, 216, 176, 104, 26, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 25, 124, 186, 228, 245, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 33, 120, 194, 225, 249, 243, 214, 159, 71, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 60, 150, 208, 241, 251, 236, 193, 117, 18, 0, 0, 0, 0,
+ 0, 0, 0, 0, 57, 148, 207, 240, 250, 230, 195, 116, 21, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 132, 198, 237, 251, 237,
+ 215, 160, 84, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
+ 56, 132, 196, 221, 244, 244, 221, 195, 131, 55, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 74, 160, 215, 243, 248, 223,
+ 190, 110, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 23, 111, 191, 223, 248, 243, 214, 160, 73, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 142, 200, 236, 251, 255,
+ 252, 0, 0, 0, 0, 0, 4, 77, 147, 195, 228, 243, 252, 240, 226, 191,
+ 149, 89, 17, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 207,
+ 255, 255, 255, 121, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 121, 255, 255, 255, 206, 0, 222, 255, 255, 255, 69, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 134, 255, 255, 255, 255, 255, 141, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 70, 255, 255, 255, 222, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 255, 255, 169, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 65, 189, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 254, 192, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 202, 77, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 189, 254, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, 192, 68, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 229, 119, 4, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 21, 145, 240, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 254, 205, 123, 41, 0, 0, 0, 0, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 110, 255, 255, 255, 216, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 217, 255, 255, 255, 109, 0, 161, 255, 255, 255, 127, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, 255, 255, 255, 255, 255, 255,
+ 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255,
+ 255, 255, 160, 0, 0, 0, 22, 235, 255, 255, 255, 201, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 210, 255, 255, 255, 229,
+ 17, 0, 0, 23, 237, 255, 255, 255, 189, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 190, 255, 255, 255, 237, 23, 0,
+ 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 0, 140, 255, 255, 255, 164, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8,
+ 187, 255, 255, 255, 255, 255, 255, 186, 8, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 4, 188, 255,
+ 255, 255, 156, 0, 0, 0, 0, 0, 0, 0, 0, 48, 145, 234, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 222, 98, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 102, 226,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, 182, 61, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 12, 130, 245, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 248, 139, 16, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 112, 249, 255, 255, 255, 255, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 10, 140, 252, 255, 255, 255, 255, 255, 255, 255, 255, 199,
+ 38, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0,
+ 0, 28, 186, 255, 255, 255, 255, 255, 255, 255, 255, 235, 81, 0, 0, 0,
+ 0, 0, 28, 184, 255, 255, 255, 255, 255, 255, 255, 255, 237, 88, 0, 0,
+ 0, 0, 255, 255, 255, 255, 0, 0, 13, 152, 252, 255, 255, 255, 255, 255,
+ 255, 255, 255, 211, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 59, 202,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 200, 56, 0, 0, 0, 0,
+ 0, 0, 255, 255, 255, 255, 0, 0, 41, 202, 255, 255, 255, 255, 255, 255,
+ 255, 255, 246, 114, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 115,
+ 247, 255, 255, 255, 255, 255, 255, 255, 255, 202, 40, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 31, 184, 255, 255, 255, 255, 255, 255,
+ 253, 0, 0, 0, 1, 100, 232, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 251, 175, 58, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 108,
+ 255, 255, 255, 218, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 219, 255, 255, 255, 107, 0, 155, 255, 255, 255, 136, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 202, 255, 255, 255, 255, 255, 208, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 137, 255, 255, 255, 154, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 255, 255, 254, 51, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 22, 172, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 174, 23, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 159,
+ 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 172, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 174, 23, 0, 0,
+ 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 209, 31, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 81, 234, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 19, 249, 255, 255, 255, 59, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 59, 255, 255, 255, 249, 19, 0, 97, 255, 255, 255, 191, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 152, 255, 255, 255, 255, 255, 255,
+ 158, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255,
+ 255, 255, 96, 0, 0, 0, 0, 88, 255, 255, 255, 255, 119, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 138, 255, 255, 255, 255, 70,
+ 0, 0, 0, 0, 93, 255, 255, 255, 255, 103, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 104, 255, 255, 255, 255, 92, 0, 0,
+ 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 0, 62, 255, 255, 255, 237, 3, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 182,
+ 255, 255, 255, 255, 255, 255, 255, 255, 182, 6, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 17, 218,
+ 255, 255, 255, 83, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 161, 7, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 211, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 47, 228, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 217, 33, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 111, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0,
+ 0, 0, 0, 19, 210, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 239, 56, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0,
+ 43, 230, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 94, 0, 0,
+ 0, 48, 235, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, 94, 0,
+ 0, 0, 255, 255, 255, 255, 0, 27, 211, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 246, 71, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 124, 0, 0, 0,
+ 0, 0, 255, 255, 255, 255, 0, 57, 241, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 181, 6, 0, 0, 0, 0, 0, 0, 0, 6, 182, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 240, 57, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 50, 234, 255, 255, 255, 255, 255, 255, 255,
+ 254, 0, 0, 6, 176, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 17,
+ 248, 255, 255, 255, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 63, 255, 255, 255, 247, 17, 0, 88, 255, 255, 255, 203, 0,
+ 0, 0, 0, 0, 0, 0, 0, 16, 252, 255, 255, 255, 255, 255, 254, 20,
+ 0, 0, 0, 0, 0, 0, 0, 0, 204, 255, 255, 255, 87, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 255, 255, 255, 187, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 48, 226, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 227, 48, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 171, 2, 0, 0, 0, 0, 0, 0, 0, 48, 226, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 227, 48, 0,
+ 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 219, 23, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 94, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 171, 255, 255, 255, 156, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 156, 255, 255, 255, 170, 0, 0, 34, 255, 255, 255, 247, 7, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 214, 255, 255, 255, 255, 255, 255,
+ 220, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 248, 255,
+ 255, 255, 32, 0, 0, 0, 0, 0, 175, 255, 255, 255, 247, 40, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, 253, 255, 255, 255, 149, 0,
+ 0, 0, 0, 0, 0, 181, 255, 255, 255, 241, 28, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 29, 242, 255, 255, 255, 180, 0, 0, 0,
+ 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 239, 0, 0, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 0, 3, 237, 255, 255, 255, 63, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 178, 255,
+ 255, 255, 255, 249, 249, 255, 255, 255, 255, 177, 5, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 39,
+ 239, 255, 255, 238, 29, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 157, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 57, 235, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 0, 0, 63, 241, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 232, 30, 0, 0, 0, 0,
+ 0, 0, 0, 22, 248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0,
+ 0, 0, 13, 207, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 234, 28, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 22,
+ 225, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 248, 43, 0,
+ 31, 235, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 248, 36,
+ 0, 0, 255, 255, 255, 255, 15, 212, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 239, 25, 0, 0, 0, 0, 0, 144, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 139, 0, 0,
+ 0, 0, 255, 255, 255, 255, 29, 234, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 172, 2, 0, 0, 0, 0, 0, 2, 173, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 234, 28, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 26, 231, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 0, 0, 160, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 167, 255, 255, 255, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 161, 255, 255, 255, 166, 0, 0, 22, 254, 255, 255, 253, 17,
+ 0, 0, 0, 0, 0, 0, 0, 81, 255, 255, 255, 255, 255, 255, 255, 86,
+ 0, 0, 0, 0, 0, 0, 0, 17, 253, 255, 255, 254, 21, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 255, 255, 255, 255, 69, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 49,
+ 239, 255, 255, 255, 255, 255, 215, 136, 65, 37, 13, 11, 36, 63, 132, 212,
+ 255, 255, 255, 255, 255, 239, 49, 0, 0, 0, 0, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 0, 0, 0, 9, 25, 66, 126, 222, 255, 255, 255, 255,
+ 255, 115, 0, 0, 0, 0, 0, 0, 49, 239, 255, 255, 255, 255, 255, 215,
+ 136, 65, 37, 13, 11, 36, 63, 132, 212, 255, 255, 255, 255, 255, 239, 49,
+ 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 8, 21, 56, 107, 192, 255, 255, 255, 255, 255, 167, 0, 0, 0, 0, 0,
+ 0, 0, 0, 63, 252, 255, 255, 255, 255, 227, 140, 78, 35, 15, 4, 18,
+ 38, 74, 131, 191, 252, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 74, 255, 255, 255, 242, 11, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11,
+ 242, 255, 255, 255, 73, 0, 0, 0, 225, 255, 255, 255, 62, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 21, 255, 255, 255, 252, 250, 255, 255,
+ 255, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 255, 255,
+ 255, 224, 0, 0, 0, 0, 0, 0, 23, 237, 255, 255, 255, 198, 2, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 13, 222, 255, 255, 255, 218, 10, 0,
+ 0, 0, 0, 0, 0, 28, 241, 255, 255, 255, 181, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 182, 255, 255, 255, 241, 28, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 9, 204, 255, 255, 255, 252, 71, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 163, 255, 255, 255, 140, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 4, 173, 255, 255,
+ 255, 255, 240, 65, 65, 241, 255, 255, 255, 255, 172, 4, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 70, 252, 255, 255, 193, 2, 0, 0, 0, 0, 0, 255, 255, 243, 175, 111,
+ 67, 30, 15, 3, 14, 39, 88, 170, 251, 255, 255, 255, 255, 82, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 234, 255, 255, 255, 255,
+ 249, 161, 89, 36, 16, 5, 21, 46, 96, 157, 236, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 0, 29, 239, 255, 255, 255, 255, 205, 111,
+ 52, 20, 5, 17, 49, 116, 223, 255, 255, 255, 255, 196, 3, 0, 0, 0,
+ 0, 0, 0, 118, 255, 255, 255, 249, 124, 38, 7, 0, 0, 0, 0, 0,
+ 0, 0, 151, 255, 255, 255, 255, 250, 151, 66, 24, 6, 25, 68, 152, 250,
+ 255, 255, 178, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 173,
+ 255, 255, 241, 136, 58, 20, 6, 26, 88, 207, 255, 255, 255, 255, 182, 2,
+ 199, 255, 255, 241, 136, 59, 21, 5, 25, 87, 207, 255, 255, 255, 255, 176,
+ 0, 0, 255, 255, 255, 255, 166, 255, 255, 252, 162, 81, 29, 8, 13, 45,
+ 121, 236, 255, 255, 255, 255, 166, 0, 0, 0, 0, 90, 255, 255, 255, 255,
+ 255, 203, 106, 39, 13, 12, 39, 106, 204, 255, 255, 255, 255, 255, 84, 0,
+ 0, 0, 255, 255, 255, 255, 179, 255, 255, 254, 165, 73, 26, 6, 26, 73,
+ 165, 254, 255, 255, 255, 255, 109, 0, 0, 0, 0, 0, 111, 255, 255, 255,
+ 255, 254, 165, 73, 26, 6, 27, 75, 167, 254, 255, 255, 178, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 177, 255, 255, 246, 144, 65, 24, 3, 0, 0,
+ 0, 0, 60, 255, 255, 255, 255, 250, 163, 85, 39, 13, 3, 11, 26, 59,
+ 105, 166, 240, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 68, 255, 255, 255, 245, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 14, 245, 255, 255, 255, 67, 0, 0, 0, 210, 255, 255, 255, 81,
+ 0, 0, 0, 0, 0, 0, 0, 148, 255, 255, 255, 249, 255, 255, 255, 154,
+ 0, 0, 0, 0, 0, 0, 0, 83, 255, 255, 255, 209, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 185, 255, 255, 255, 205, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 25, 231,
+ 255, 255, 255, 255, 223, 81, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 75, 221, 255, 255, 255, 255, 231, 25, 0, 0, 0, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 117, 251, 255, 255,
+ 255, 237, 15, 0, 0, 0, 0, 25, 231, 255, 255, 255, 255, 223, 81, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 75, 221, 255, 255, 255, 255, 231,
+ 25, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 65, 229, 255, 255, 255, 254, 46, 0, 0, 0, 0,
+ 0, 0, 4, 211, 255, 255, 255, 249, 112, 5, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 24, 111, 209, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 3, 229, 255, 255, 255, 95, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95,
+ 255, 255, 255, 228, 3, 0, 0, 0, 162, 255, 255, 255, 126, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 83, 255, 255, 255, 205, 198, 255, 255,
+ 255, 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255,
+ 255, 160, 0, 0, 0, 0, 0, 0, 0, 91, 255, 255, 255, 255, 115, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 157, 255, 255, 255, 252, 56, 0, 0,
+ 0, 0, 0, 0, 0, 0, 102, 255, 255, 255, 255, 94, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 95, 255, 255, 255, 255, 101, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 168, 255, 255, 255, 255, 114, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 86, 255, 255, 255, 217, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 3, 168, 255, 255, 255,
+ 255, 229, 46, 0, 0, 47, 230, 255, 255, 255, 255, 167, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 109, 255, 255, 255, 124, 0, 0, 0, 0, 0, 200, 89, 7, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 43, 213, 255, 255, 255, 206, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 196, 255, 255, 255, 255, 179,
+ 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 81, 198, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 2, 193, 255, 255, 255, 234, 82, 0, 0,
+ 0, 0, 0, 0, 0, 0, 5, 142, 255, 255, 255, 255, 106, 0, 0, 0,
+ 0, 0, 0, 182, 255, 255, 255, 120, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 58, 254, 255, 255, 255, 219, 40, 0, 0, 0, 0, 0, 0, 0, 42,
+ 221, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255,
+ 255, 192, 21, 0, 0, 0, 0, 0, 0, 4, 172, 255, 255, 255, 254, 141,
+ 255, 255, 191, 21, 0, 0, 0, 0, 0, 0, 4, 175, 255, 255, 255, 252,
+ 25, 0, 255, 255, 255, 255, 255, 255, 213, 40, 0, 0, 0, 0, 0, 0,
+ 0, 23, 211, 255, 255, 255, 250, 21, 0, 0, 24, 238, 255, 255, 255, 252,
+ 107, 1, 0, 0, 0, 0, 0, 0, 2, 112, 253, 255, 255, 255, 236, 22,
+ 0, 0, 255, 255, 255, 255, 255, 255, 237, 62, 0, 0, 0, 0, 0, 0,
+ 0, 63, 237, 255, 255, 255, 241, 27, 0, 0, 0, 27, 242, 255, 255, 255,
+ 236, 62, 0, 0, 0, 0, 0, 0, 0, 65, 239, 255, 255, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 255, 255, 206, 31, 0, 0, 0, 0, 0, 0,
+ 0, 0, 156, 255, 255, 255, 231, 46, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 5, 84, 199, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 1, 224, 255, 255, 255, 102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 103, 255, 255, 255, 223, 1, 0, 0, 0, 142, 255, 255, 255, 148,
+ 0, 0, 0, 0, 0, 0, 0, 216, 255, 255, 255, 146, 255, 255, 255, 221,
+ 0, 0, 0, 0, 0, 0, 0, 150, 255, 255, 255, 141, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 49, 254, 255, 255, 255, 88, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 1, 191, 255,
+ 255, 255, 255, 173, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 8, 169, 255, 255, 255, 255, 190, 0, 0, 0, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 89, 255, 255,
+ 255, 255, 97, 0, 0, 0, 1, 191, 255, 255, 255, 255, 173, 9, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 169, 255, 255, 255, 255,
+ 190, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 36, 243, 255, 255, 255, 128, 0, 0, 0, 0,
+ 0, 0, 78, 255, 255, 255, 253, 69, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 75, 196, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 0, 135, 255, 255, 255, 193, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192,
+ 255, 255, 255, 134, 0, 0, 0, 0, 99, 255, 255, 255, 189, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 145, 255, 255, 255, 143, 136, 255, 255,
+ 255, 149, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 255, 255,
+ 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 178, 255, 255, 255, 246, 37,
+ 0, 0, 0, 0, 0, 0, 0, 78, 255, 255, 255, 255, 132, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 189, 255, 255, 255, 237, 24, 0, 0, 0,
+ 0, 0, 0, 0, 0, 24, 238, 255, 255, 255, 189, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 124, 255, 255, 255, 255, 159, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 14, 250, 255, 255, 255, 39,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 2, 163, 255, 255, 255, 255,
+ 215, 31, 0, 0, 0, 0, 31, 215, 255, 255, 255, 255, 162, 2, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 152, 255, 255, 251, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 231, 255, 255, 255, 56, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 85, 255, 255, 255, 255, 150, 2,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 84, 255, 255, 255, 233, 41, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 139, 255, 255, 255, 219, 2, 0, 0,
+ 0, 0, 0, 226, 255, 255, 255, 34, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 173, 255, 255, 255, 231, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 30, 233, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255,
+ 202, 7, 0, 0, 0, 0, 0, 0, 0, 0, 15, 234, 255, 255, 255, 255,
+ 255, 199, 6, 0, 0, 0, 0, 0, 0, 0, 0, 16, 236, 255, 255, 255,
+ 108, 0, 255, 255, 255, 255, 255, 216, 15, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 38, 248, 255, 255, 255, 105, 0, 0, 133, 255, 255, 255, 255, 89,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 255, 130,
+ 0, 0, 255, 255, 255, 255, 255, 247, 54, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 54, 247, 255, 255, 255, 139, 0, 0, 0, 141, 255, 255, 255, 247,
+ 53, 0, 0, 0, 0, 0, 0, 0, 0, 0, 57, 249, 255, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 255, 219, 17, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 219, 255, 255, 255, 86, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 127, 255, 255, 255, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 201, 255, 255, 255, 126, 0, 0, 0, 0, 75, 255, 255, 255, 215,
+ 0, 0, 0, 0, 0, 0, 27, 255, 255, 255, 251, 23, 248, 255, 255, 255,
+ 32, 0, 0, 0, 0, 0, 0, 217, 255, 255, 255, 74, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 166, 255, 255, 255, 220, 5, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 98, 255, 255,
+ 255, 255, 167, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 164, 255, 255, 255, 255, 96, 0, 0, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 174, 255,
+ 255, 255, 167, 0, 0, 0, 98, 255, 255, 255, 255, 167, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 164, 255, 255, 255,
+ 255, 97, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 255, 195, 0, 0, 0, 0,
+ 0, 0, 158, 255, 255, 255, 149, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 0, 38, 255, 255, 255, 254, 35, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 254,
+ 255, 255, 255, 38, 0, 0, 0, 0, 35, 255, 255, 255, 246, 6, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 207, 255, 255, 255, 80, 75, 255, 255,
+ 255, 211, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 247, 255, 255,
+ 255, 32, 0, 0, 0, 0, 0, 0, 0, 0, 25, 238, 255, 255, 255, 195,
+ 2, 0, 0, 0, 0, 0, 21, 233, 255, 255, 255, 206, 5, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 34, 245, 255, 255, 255, 173, 0, 0, 0,
+ 0, 0, 0, 0, 0, 174, 255, 255, 255, 244, 33, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 80, 254, 255, 255, 255, 198, 6, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 187, 255, 255, 255, 117,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 1, 158, 255, 255, 255, 255, 197,
+ 18, 0, 0, 0, 0, 0, 0, 19, 198, 255, 255, 255, 255, 158, 1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 5, 190, 255, 255, 221, 14, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 106, 255, 255, 255, 123, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 204, 255, 255, 255, 195, 3, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 196, 255, 255, 255, 79, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 4, 219, 255, 255, 255, 68, 0, 0,
+ 0, 0, 0, 243, 255, 255, 255, 5, 0, 0, 0, 0, 0, 0, 0, 0,
+ 23, 250, 255, 255, 255, 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 91, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 251,
+ 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 142, 255, 255, 255, 255,
+ 251, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 144, 255, 255, 255,
+ 161, 0, 255, 255, 255, 255, 254, 47, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 163, 255, 255, 255, 159, 0, 6, 233, 255, 255, 255, 158, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 165, 255, 255, 255, 231,
+ 5, 0, 255, 255, 255, 255, 255, 128, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 129, 255, 255, 255, 233, 5, 0, 5, 234, 255, 255, 255, 128,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 132, 255, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 255, 65, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 244, 255, 255, 255, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 31, 253, 255, 255, 255, 43, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 44, 255, 255, 255, 253, 30, 0, 0, 0, 0, 13, 251, 255, 255, 255,
+ 26, 0, 0, 0, 0, 0, 95, 255, 255, 255, 197, 0, 191, 255, 255, 255,
+ 99, 0, 0, 0, 0, 0, 28, 255, 255, 255, 250, 12, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 34, 250, 255, 255, 255, 106, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 6, 227, 255, 255,
+ 255, 211, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 5, 209, 255, 255, 255, 225, 5, 0, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 76, 255,
+ 255, 255, 215, 0, 0, 6, 227, 255, 255, 255, 211, 6, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 209, 255, 255,
+ 255, 226, 6, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 45, 255, 255, 255, 225, 0, 0, 0, 0,
+ 0, 0, 212, 255, 255, 255, 53, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 0, 0, 196, 255, 255, 255, 132, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 131, 255,
+ 255, 255, 196, 0, 0, 0, 0, 0, 0, 227, 255, 255, 255, 61, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 15, 253, 255, 255, 254, 19, 15, 253, 255,
+ 255, 254, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 255, 255, 255,
+ 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 255, 255, 255, 255,
+ 111, 0, 0, 0, 0, 0, 176, 255, 255, 255, 248, 43, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 112, 255, 255, 255, 255, 85, 0, 0,
+ 0, 0, 0, 0, 86, 255, 255, 255, 255, 111, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 46, 243, 255, 255, 255, 227, 23, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 109, 255, 255, 255, 194,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 1, 153, 255, 255, 255, 255, 176, 9,
+ 0, 0, 0, 0, 0, 0, 0, 0, 9, 177, 255, 255, 255, 255, 152, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 18, 219, 255, 255, 164, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 255, 255, 178, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 47, 255, 255, 255, 255, 44, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 41, 255, 255, 255, 201, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 110, 255, 255, 255, 131, 0, 0,
+ 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 96, 255, 255, 255, 220, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 223, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 169,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 255, 255, 255, 255,
+ 167, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 255, 255, 255,
+ 207, 0, 255, 255, 255, 255, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 82, 255, 255, 255, 207, 0, 71, 255, 255, 255, 250, 23, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 251, 255, 255, 255,
+ 70, 0, 255, 255, 255, 255, 242, 10, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 10, 243, 255, 255, 255, 72, 0, 72, 255, 255, 255, 242, 10,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 244, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 205, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 247, 255, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 186, 255, 255, 255, 142, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 143, 255, 255, 255, 185, 0, 0, 0, 0, 0, 0, 197, 255, 255, 255,
+ 94, 0, 0, 0, 0, 0, 162, 255, 255, 255, 129, 0, 123, 255, 255, 255,
+ 166, 0, 0, 0, 0, 0, 95, 255, 255, 255, 196, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 147, 255, 255, 255, 232, 11, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 96, 255, 255, 255,
+ 251, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 49, 251, 255, 255, 255, 93, 0, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 255,
+ 255, 255, 235, 0, 0, 96, 255, 255, 255, 251, 51, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 251, 255,
+ 255, 255, 95, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 11, 255, 255, 255, 246, 0, 0, 0, 0,
+ 0, 0, 236, 255, 255, 255, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 0, 0, 98, 255, 255, 255, 226, 2, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 226, 255,
+ 255, 255, 98, 0, 0, 0, 0, 0, 0, 164, 255, 255, 255, 125, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 76, 255, 255, 255, 212, 0, 0, 207, 255,
+ 255, 255, 79, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 255,
+ 161, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 181, 255, 255, 255,
+ 245, 35, 0, 0, 0, 97, 255, 255, 255, 255, 114, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 2, 197, 255, 255, 255, 233, 19, 0,
+ 0, 0, 0, 19, 233, 255, 255, 255, 197, 2, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 21, 224, 255, 255, 255, 246, 50, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 255, 255, 252,
+ 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 148, 255, 255, 255, 255, 153, 3, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 153, 255, 255, 255, 255, 147,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 255, 255, 255, 217, 0,
+ 255, 255, 255, 255, 0, 0, 0, 1, 74, 160, 215, 243, 248, 223, 190, 110,
+ 23, 0, 0, 0, 0, 0, 0, 0, 111, 255, 255, 255, 187, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 23, 111, 191, 223, 248, 243, 214, 160, 73, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 105, 255, 255, 255, 112, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 255, 255, 255, 189, 0, 0,
+ 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 151, 255, 255, 255, 131, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 132, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0,
+ 37, 132, 198, 237, 251, 237, 215, 160, 84, 3, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 89,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 255, 255, 255, 255,
+ 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 255, 255, 255,
+ 230, 0, 255, 255, 255, 255, 93, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 35, 255, 255, 255, 229, 0, 128, 255, 255, 255, 172, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255,
+ 126, 0, 255, 255, 255, 255, 159, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 160, 255, 255, 255, 127, 0, 129, 255, 255, 255, 159, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 161, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 118, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 222, 255, 255, 255, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 87, 255, 255, 255, 234, 5, 0, 0, 0, 0, 0, 0, 0, 0,
+ 6, 235, 255, 255, 255, 86, 0, 0, 0, 0, 0, 0, 129, 255, 255, 255,
+ 161, 0, 0, 0, 0, 0, 229, 255, 255, 255, 61, 0, 56, 255, 255, 255,
+ 232, 1, 0, 0, 0, 0, 163, 255, 255, 255, 128, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 22, 242, 255, 255, 255, 125, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 193, 255, 255, 255,
+ 162, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 161, 255, 255, 255, 191, 0, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 255,
+ 255, 255, 250, 0, 0, 193, 255, 255, 255, 162, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 161, 255,
+ 255, 255, 193, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 13, 255, 255, 255, 248, 0, 0, 0, 0,
+ 0, 0, 250, 255, 255, 255, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 0, 0, 12, 244, 255, 255, 255, 71, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 255, 255,
+ 255, 244, 12, 0, 0, 0, 0, 0, 0, 100, 255, 255, 255, 188, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 138, 255, 255, 255, 149, 0, 0, 145, 255,
+ 255, 255, 141, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 255, 255, 255,
+ 97, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 240, 255, 255,
+ 255, 192, 1, 0, 31, 241, 255, 255, 255, 192, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 248, 255, 255, 255, 165, 0,
+ 0, 0, 0, 166, 255, 255, 255, 247, 39, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 5, 194, 255, 255, 255, 255, 87, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 210, 255, 255, 255,
+ 93, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 142, 255, 255, 255, 255, 126, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 255, 255,
+ 141, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 90,
+ 146, 194, 224, 238, 251, 255, 255, 255, 255, 255, 255, 255, 255, 255, 233, 0,
+ 255, 255, 255, 255, 0, 0, 41, 202, 255, 255, 255, 255, 255, 255, 255, 255,
+ 246, 114, 2, 0, 0, 0, 0, 0, 173, 255, 255, 255, 112, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 115, 247, 255, 255, 255, 255, 255, 255, 255, 255, 202, 40, 0,
+ 0, 255, 255, 255, 255, 0, 165, 255, 255, 255, 59, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 255, 255, 255, 220, 0, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0,
+ 201, 255, 255, 255, 75, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 77, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 13, 152,
+ 252, 255, 255, 255, 255, 255, 255, 255, 255, 211, 57, 0, 0, 0, 0, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 142, 255, 255,
+ 255, 255, 254, 114, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 36,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 255, 255, 255, 255,
+ 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 255, 255, 255,
+ 244, 0, 255, 255, 255, 255, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 14, 255, 255, 255, 244, 0, 183, 255, 255, 255, 102, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 105, 255, 255, 255,
+ 181, 0, 255, 255, 255, 255, 94, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 95, 255, 255, 255, 180, 0, 182, 255, 255, 255, 94, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 161, 255, 255, 255, 250, 116, 8, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 7, 237, 255, 255, 255, 83, 0, 0, 0, 0, 0, 0, 0, 0,
+ 84, 255, 255, 255, 237, 7, 0, 0, 0, 0, 0, 0, 62, 255, 255, 255,
+ 228, 0, 0, 0, 0, 41, 255, 255, 255, 244, 5, 0, 3, 241, 255, 255,
+ 255, 45, 0, 0, 0, 0, 229, 255, 255, 255, 61, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 127, 255, 255, 255, 241, 20, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 20, 253, 255, 255, 255,
+ 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 56, 255, 255, 255, 253, 19, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 255,
+ 255, 255, 235, 0, 20, 253, 255, 255, 255, 57, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 255,
+ 255, 255, 253, 19, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 47, 255, 255, 255, 230, 0, 0, 0, 0,
+ 0, 0, 234, 255, 255, 255, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 159, 255, 255, 255, 168, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 166, 255, 255,
+ 255, 159, 0, 0, 0, 0, 0, 0, 0, 36, 255, 255, 255, 245, 6, 0,
+ 0, 0, 0, 0, 0, 0, 0, 200, 255, 255, 255, 87, 0, 0, 83, 255,
+ 255, 255, 203, 0, 0, 0, 0, 0, 0, 0, 0, 7, 247, 255, 255, 255,
+ 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 98, 255, 255,
+ 255, 255, 108, 2, 192, 255, 255, 255, 241, 31, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 121, 255, 255, 255, 255, 76,
+ 0, 0, 77, 255, 255, 255, 255, 120, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 155, 255, 255, 255, 255, 132, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 133, 255, 255, 255,
+ 171, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 136, 255, 255, 255, 252, 99, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 252, 255, 255,
+ 255, 135, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 151, 244, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 249, 0,
+ 255, 255, 255, 255, 0, 57, 241, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 181, 6, 0, 0, 0, 0, 210, 255, 255, 255, 54, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 6, 182, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 240, 57,
+ 0, 255, 255, 255, 255, 0, 208, 255, 255, 255, 252, 252, 252, 252, 252, 253,
+ 253, 253, 253, 254, 254, 254, 254, 255, 255, 255, 255, 255, 255, 238, 0, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0,
+ 225, 255, 255, 255, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 32, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 27, 211, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 246, 71, 0, 0, 0, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 168, 255, 255, 255,
+ 255, 249, 88, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 13,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 255, 255, 255, 255,
+ 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 1, 255, 255, 255, 255, 0, 215, 255, 255, 255, 50, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 255, 255, 255,
+ 213, 0, 255, 255, 255, 255, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 47, 255, 255, 255, 215, 0, 216, 255, 255, 255, 46, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 57, 255, 255, 255, 255, 255, 237, 153, 76, 14, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 146, 255, 255, 255, 182, 0, 0, 0, 0, 0, 0, 0, 0,
+ 183, 255, 255, 255, 145, 0, 0, 0, 0, 0, 0, 0, 6, 245, 255, 255,
+ 255, 39, 0, 0, 0, 109, 255, 255, 255, 182, 0, 0, 0, 177, 255, 255,
+ 255, 112, 0, 0, 0, 41, 255, 255, 255, 244, 5, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 12, 233, 255, 255, 255, 143, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 87, 255, 255, 255, 214,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 213, 255, 255, 255, 85, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 77, 255,
+ 255, 255, 215, 0, 87, 255, 255, 255, 214, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 213,
+ 255, 255, 255, 86, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 131, 255, 255, 255, 189, 0, 0, 0, 0,
+ 0, 0, 204, 255, 255, 255, 186, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 62, 255, 255, 255, 248, 17,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 247, 255, 255,
+ 255, 62, 0, 0, 0, 0, 0, 0, 0, 0, 228, 255, 255, 255, 60, 0,
+ 0, 0, 0, 0, 0, 0, 11, 251, 255, 255, 255, 25, 0, 0, 21, 255,
+ 255, 255, 252, 12, 0, 0, 0, 0, 0, 0, 0, 62, 255, 255, 255, 224,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 184, 255,
+ 255, 255, 244, 144, 255, 255, 255, 255, 97, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 205, 255, 255, 255, 228,
+ 15, 15, 229, 255, 255, 255, 204, 3, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 110,
+ 255, 255, 255, 255, 176, 1, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 255, 255, 255,
+ 242, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 131, 255, 255, 255, 245, 76, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 77, 245, 255,
+ 255, 255, 130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 235, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0,
+ 255, 255, 255, 255, 29, 234, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 172, 2, 0, 0, 0, 229, 255, 255, 255, 24, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 2, 173, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 234,
+ 28, 255, 255, 255, 255, 0, 226, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 0, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0,
+ 240, 255, 255, 255, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 15, 255, 255, 255, 255, 0, 255, 255, 255, 255, 15, 212, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 239, 25, 0, 0, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 15, 191, 255, 255, 255, 255,
+ 241, 65, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 0, 232, 255, 255, 255, 22, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 255, 255, 255,
+ 230, 0, 255, 255, 255, 255, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 22, 255, 255, 255, 231, 0, 233, 255, 255, 255, 21, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 148, 255, 255, 255, 255, 255, 255, 255, 252, 206, 149, 87, 23,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 47, 255, 255, 255, 252, 27, 0, 0, 0, 0, 0, 0, 28,
+ 253, 255, 255, 255, 47, 0, 0, 0, 0, 0, 0, 0, 0, 184, 255, 255,
+ 255, 106, 0, 0, 0, 176, 255, 255, 255, 114, 0, 0, 0, 110, 255, 255,
+ 255, 179, 0, 0, 0, 108, 255, 255, 255, 183, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 108, 255, 255, 255, 248, 31, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 140, 255, 255, 255, 152,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 153, 255, 255, 255, 137, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 174, 255,
+ 255, 255, 167, 0, 140, 255, 255, 255, 152, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 153,
+ 255, 255, 255, 138, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 39, 245, 255, 255, 255, 123, 0, 0, 0, 0,
+ 0, 0, 141, 255, 255, 255, 255, 170, 21, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 219, 255, 255, 255, 107,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 105, 255, 255, 255,
+ 220, 1, 0, 0, 0, 0, 0, 0, 0, 0, 165, 255, 255, 255, 123, 0,
+ 0, 0, 0, 0, 0, 0, 69, 255, 255, 255, 219, 0, 0, 0, 0, 215,
+ 255, 255, 255, 70, 0, 0, 0, 0, 0, 0, 0, 126, 255, 255, 255, 161,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 241,
+ 255, 255, 255, 255, 255, 255, 255, 176, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47, 250, 255, 255, 255,
+ 156, 157, 255, 255, 255, 250, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 252,
+ 255, 255, 255, 210, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 231, 255, 255,
+ 255, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 243, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0,
+ 255, 255, 255, 255, 179, 255, 255, 254, 165, 73, 26, 6, 26, 73, 165, 254,
+ 255, 255, 255, 255, 109, 0, 0, 0, 247, 255, 255, 255, 8, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 111, 255, 255, 255, 255, 254, 165, 73, 26, 6, 27, 75, 167, 254, 255, 255,
+ 178, 255, 255, 255, 255, 0, 243, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0,
+ 252, 255, 255, 255, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 4, 255, 255, 255, 255, 0, 255, 255, 255, 255, 166, 255, 255, 252,
+ 162, 81, 29, 8, 13, 45, 121, 236, 255, 255, 255, 255, 166, 0, 0, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 27, 210, 255, 255, 255, 255, 229,
+ 46, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 0, 248, 255, 255, 255, 7, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 255, 255, 255,
+ 247, 0, 255, 255, 255, 255, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 7, 255, 255, 255, 247, 0, 248, 255, 255, 255, 7, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 2, 138, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254,
+ 194, 99, 5, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 205, 255, 255, 255, 123, 0, 0, 0, 0, 0, 0, 124,
+ 255, 255, 255, 204, 0, 0, 0, 0, 0, 0, 0, 0, 0, 116, 255, 255,
+ 255, 173, 0, 0, 3, 240, 255, 255, 255, 46, 0, 0, 0, 42, 255, 255,
+ 255, 242, 4, 0, 0, 176, 255, 255, 255, 116, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 5, 220, 255, 255, 255, 162, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 184, 255, 255, 255, 97,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 98, 255, 255, 255, 182, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 87, 255, 255,
+ 255, 255, 97, 0, 184, 255, 255, 255, 97, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 98,
+ 255, 255, 255, 183, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 67, 231, 255, 255, 255, 250, 31, 0, 0, 0, 0,
+ 0, 0, 49, 255, 255, 255, 255, 255, 243, 156, 71, 8, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 123, 255, 255, 255, 205,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 202, 255, 255, 255,
+ 123, 0, 0, 0, 0, 0, 0, 0, 0, 0, 101, 255, 255, 255, 187, 0,
+ 0, 0, 0, 0, 0, 0, 131, 255, 255, 255, 156, 0, 0, 0, 0, 153,
+ 255, 255, 255, 132, 0, 0, 0, 0, 0, 0, 0, 190, 255, 255, 255, 97,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 101,
+ 255, 255, 255, 255, 255, 255, 234, 22, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 130, 255, 255, 255,
+ 255, 255, 255, 255, 255, 129, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 238, 255,
+ 255, 255, 235, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 156, 255, 255,
+ 255, 148, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 208, 255, 255, 255, 255, 242,
+ 161, 102, 61, 34, 15, 8, 1, 0, 0, 0, 0, 255, 255, 255, 255, 0,
+ 255, 255, 255, 255, 255, 255, 237, 62, 0, 0, 0, 0, 0, 0, 0, 63,
+ 237, 255, 255, 255, 241, 27, 0, 0, 247, 255, 255, 255, 8, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27,
+ 242, 255, 255, 255, 236, 62, 0, 0, 0, 0, 0, 0, 0, 65, 239, 255,
+ 255, 255, 255, 255, 255, 0, 250, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 241, 255, 255, 255, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 15, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 213, 40,
+ 0, 0, 0, 0, 0, 0, 0, 23, 211, 255, 255, 255, 250, 21, 0, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 43, 226, 255, 255, 255, 255, 214, 30,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 0, 248, 255, 255, 255, 7, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 255, 255, 255,
+ 247, 0, 255, 255, 255, 255, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 7, 255, 255, 255, 247, 0, 248, 255, 255, 255, 7, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 48, 171, 249, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 224, 70, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 106, 255, 255, 255, 220, 1, 0, 0, 0, 0, 1, 221,
+ 255, 255, 255, 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 255, 255,
+ 255, 238, 2, 0, 55, 255, 255, 255, 233, 1, 0, 0, 0, 0, 230, 255,
+ 255, 255, 58, 0, 3, 240, 255, 255, 255, 48, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 89, 255, 255, 255, 253, 45, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 214, 255, 255, 255, 51,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 51, 255, 255, 255, 212, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 112, 250, 255, 255,
+ 255, 238, 16, 0, 214, 255, 255, 255, 51, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51,
+ 255, 255, 255, 212, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 6, 20, 55, 107, 193, 255, 255, 255, 255, 255, 141, 0, 0, 0, 0, 0,
+ 0, 0, 0, 172, 255, 255, 255, 255, 255, 255, 255, 247, 198, 144, 98, 54,
+ 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 28, 253, 255, 255, 255,
+ 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 255, 255, 255, 253,
+ 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 255, 255, 255, 245, 5,
+ 0, 0, 0, 0, 0, 0, 193, 255, 255, 255, 94, 0, 0, 0, 0, 91,
+ 255, 255, 255, 194, 0, 0, 0, 0, 0, 0, 7, 246, 255, 255, 255, 33,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 186, 255, 255, 255, 255, 255, 79, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 212, 255, 255,
+ 255, 255, 255, 255, 211, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 215, 255, 255,
+ 255, 250, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 78, 255, 255,
+ 255, 225, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, 255, 255, 255, 255, 157, 13,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 255, 255, 255, 255, 0,
+ 255, 255, 255, 255, 255, 247, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 54, 247, 255, 255, 255, 139, 0, 0, 229, 255, 255, 255, 24, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 141,
+ 255, 255, 255, 247, 53, 0, 0, 0, 0, 0, 0, 0, 0, 0, 57, 249,
+ 255, 255, 255, 255, 255, 0, 232, 255, 255, 255, 16, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 225, 255, 255, 255, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 32, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 216, 15, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 38, 248, 255, 255, 255, 105, 0, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 62, 239, 255, 255, 255, 255, 196, 17, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 0, 232, 255, 255, 255, 22, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 255, 255, 255,
+ 230, 0, 255, 255, 255, 255, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 22, 255, 255, 255, 231, 0, 233, 255, 255, 255, 21, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 19, 89, 155, 215, 254, 255, 255, 255, 255,
+ 255, 255, 255, 254, 95, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 16, 247, 255, 255, 255, 64, 0, 0, 0, 0, 65, 255,
+ 255, 255, 247, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 236, 255,
+ 255, 255, 51, 0, 123, 255, 255, 255, 167, 0, 0, 0, 0, 0, 164, 255,
+ 255, 255, 125, 0, 54, 255, 255, 255, 235, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 1, 205, 255, 255, 255, 181, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 237, 255, 255, 255, 32,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 33, 255, 255, 255, 236, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 0, 0, 0, 7, 23, 64, 125, 219, 255, 255, 255, 255,
+ 255, 118, 0, 0, 237, 255, 255, 255, 32, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33,
+ 255, 255, 255, 236, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 188, 6, 0, 0, 0, 0, 0,
+ 0, 0, 0, 16, 217, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 254, 210, 147, 76, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 184, 255, 255, 255,
+ 144, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 140, 255, 255, 255, 185,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 229, 255, 255, 255, 58,
+ 0, 0, 0, 0, 0, 7, 247, 255, 255, 255, 32, 0, 0, 0, 0, 29,
+ 255, 255, 255, 248, 7, 0, 0, 0, 0, 0, 62, 255, 255, 255, 224, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 81, 255, 255, 255, 255, 218, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 252, 255,
+ 255, 255, 255, 252, 53, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 183, 255, 255, 255,
+ 255, 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 246, 255,
+ 255, 255, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 160, 255, 255, 255, 180, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 255, 255, 255, 255, 0,
+ 255, 255, 255, 255, 255, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 129, 255, 255, 255, 233, 5, 0, 210, 255, 255, 255, 54, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 234,
+ 255, 255, 255, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 132,
+ 255, 255, 255, 255, 255, 0, 213, 255, 255, 255, 50, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 202, 255, 255, 255, 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 76, 255, 255, 255, 255, 0, 255, 255, 255, 255, 254, 47, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 163, 255, 255, 255, 159, 0, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 84, 248, 255, 255, 255, 255, 174, 8, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 0, 215, 255, 255, 255, 49, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 255, 255, 255,
+ 213, 0, 255, 255, 255, 255, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 47, 255, 255, 255, 215, 0, 216, 255, 255, 255, 46, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 70, 129, 198, 254,
+ 255, 255, 255, 255, 249, 33, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 165, 255, 255, 255, 163, 0, 0, 0, 0, 164, 255,
+ 255, 255, 164, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 171, 255,
+ 255, 255, 118, 0, 190, 255, 255, 255, 99, 0, 0, 0, 0, 0, 96, 255,
+ 255, 255, 192, 0, 121, 255, 255, 255, 170, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 69, 255, 255, 255, 255, 62,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 248, 255, 255, 255, 16,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 17, 255, 255, 255, 246, 0, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 175, 3, 0, 0, 248, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17,
+ 255, 255, 255, 246, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 249, 137, 7, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 20, 189, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 232, 130, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 86, 255, 255, 255,
+ 235, 5, 0, 0, 0, 0, 0, 0, 0, 0, 4, 232, 255, 255, 255, 87,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 166, 255, 255, 255, 122,
+ 0, 0, 0, 0, 0, 62, 255, 255, 255, 225, 0, 0, 0, 0, 0, 0,
+ 224, 255, 255, 255, 62, 0, 0, 0, 0, 0, 126, 255, 255, 255, 161, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11,
+ 220, 255, 255, 255, 255, 255, 105, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 139, 255,
+ 255, 255, 255, 138, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 141, 255, 255, 255, 255,
+ 150, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 179, 255,
+ 255, 255, 124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 216, 255, 255, 255, 64, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 74, 255, 255, 255, 255, 0,
+ 255, 255, 255, 255, 242, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 10, 243, 255, 255, 255, 72, 0, 171, 255, 255, 255, 112, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 255,
+ 255, 255, 242, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12,
+ 244, 255, 255, 255, 255, 0, 181, 255, 255, 255, 103, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 151, 255, 255, 255, 130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 131, 255, 255, 255, 255, 0, 255, 255, 255, 255, 176, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, 255, 255, 255, 207, 0, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255,
+ 255, 255, 0, 0, 0, 110, 253, 255, 255, 255, 255, 148, 2, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 0, 182, 255, 255, 255, 101, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 104, 255, 255, 255,
+ 181, 0, 255, 255, 255, 255, 94, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 95, 255, 255, 255, 180, 0, 182, 255, 255, 255, 94, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30,
+ 151, 255, 255, 255, 255, 145, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 245, 255, 255, 255, 12, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 66, 255, 255, 255, 246, 15, 0, 0, 15, 246, 255,
+ 255, 255, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 104, 255,
+ 255, 255, 186, 9, 248, 255, 255, 255, 31, 0, 0, 0, 0, 0, 29, 255,
+ 255, 255, 249, 10, 188, 255, 255, 255, 103, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 188, 255, 255, 255, 198,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 254, 255, 255, 255, 3,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 4, 255, 255, 255, 253, 0, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 164,
+ 8, 0, 0, 0, 254, 255, 255, 255, 3, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,
+ 255, 255, 255, 253, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 252, 93, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 91, 220, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 230, 73, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 7, 237, 255, 255,
+ 255, 83, 0, 0, 0, 0, 0, 0, 0, 0, 79, 255, 255, 255, 238, 7,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 103, 255, 255, 255, 186,
+ 0, 0, 0, 0, 0, 124, 255, 255, 255, 163, 0, 0, 0, 0, 0, 0,
+ 162, 255, 255, 255, 124, 0, 0, 0, 0, 0, 189, 255, 255, 255, 98, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 153,
+ 255, 255, 255, 255, 255, 255, 243, 31, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 255,
+ 255, 255, 255, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 255, 191,
+ 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 102, 255,
+ 255, 255, 202, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 239, 255, 255, 255, 15, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 139, 255, 255, 255, 255, 0,
+ 255, 255, 255, 255, 159, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 160, 255, 255, 255, 127, 0, 109, 255, 255, 255, 187, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 255,
+ 255, 255, 159, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 161, 255, 255, 255, 255, 0, 117, 255, 255, 255, 180, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 97, 255, 255, 255, 219, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 222, 255, 255, 255, 255, 0, 255, 255, 255, 255, 93, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 255, 255, 255, 229, 0, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255,
+ 255, 255, 0, 1, 138, 255, 255, 255, 255, 255, 122, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 0, 127, 255, 255, 255, 171, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 175, 255, 255, 255,
+ 125, 0, 255, 255, 255, 255, 159, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 159, 255, 255, 255, 127, 0, 129, 255, 255, 255, 159, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 161, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 133, 255, 255, 255, 212, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 230, 255, 255, 255, 34, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 94, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 1, 222, 255, 255, 255, 104, 0, 0, 105, 255, 255,
+ 255, 221, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 255,
+ 255, 255, 246, 76, 255, 255, 255, 220, 0, 0, 0, 0, 0, 0, 0, 218,
+ 255, 255, 255, 79, 247, 255, 255, 255, 35, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 51, 254, 255, 255, 255,
+ 81, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 248, 255, 255, 255, 15,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 16, 255, 255, 255, 246, 0, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 205, 80, 0,
+ 0, 0, 0, 0, 248, 255, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16,
+ 255, 255, 255, 243, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 254, 114, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 66, 158, 227, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 252, 98, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 147, 255, 255,
+ 255, 180, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 148, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, 255, 255, 255, 244,
+ 5, 0, 0, 0, 0, 186, 255, 255, 255, 101, 0, 0, 0, 0, 0, 0,
+ 100, 255, 255, 255, 186, 0, 0, 0, 0, 6, 246, 255, 255, 255, 34, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 74, 255,
+ 255, 255, 255, 255, 255, 255, 255, 186, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 249, 255, 255, 255, 222, 19,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 254,
+ 255, 255, 254, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 250, 255, 255, 255, 8, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 234, 255, 255, 255, 255, 0,
+ 255, 255, 255, 255, 94, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 95, 255, 255, 255, 180, 0, 45, 255, 255, 255, 255, 44, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 182, 255,
+ 255, 255, 94, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 96, 255, 255, 255, 255, 0, 52, 255, 255, 255, 252, 40, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 24, 251, 255, 255, 255, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 88, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 36, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 255, 255, 255, 244, 0, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255,
+ 255, 255, 5, 164, 255, 255, 255, 255, 251, 95, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 0, 71, 255, 255, 255, 249, 21, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 251, 255, 255, 255,
+ 69, 0, 255, 255, 255, 255, 242, 10, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 10, 242, 255, 255, 255, 72, 0, 73, 255, 255, 255, 242, 10,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 243, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 26, 255, 255, 255, 242, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 207, 255, 255, 255, 82, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 177, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 125, 255, 255, 255, 203, 0, 0, 204, 255, 255,
+ 255, 124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 225,
+ 255, 255, 255, 200, 255, 255, 255, 152, 0, 0, 0, 0, 0, 0, 0, 150,
+ 255, 255, 255, 204, 255, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 168, 255, 255, 255,
+ 214, 3, 0, 0, 0, 0, 255, 255, 255, 255, 0, 238, 255, 255, 255, 32,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 33, 255, 255, 255, 236, 0, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 248, 233, 216, 174, 121, 50, 0, 0, 0,
+ 0, 0, 0, 0, 238, 255, 255, 255, 32, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33,
+ 255, 255, 255, 231, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 5,
+ 20, 57, 120, 215, 255, 255, 255, 255, 254, 78, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 90, 134, 178, 224,
+ 255, 255, 255, 255, 255, 255, 255, 255, 251, 59, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 50, 255, 255,
+ 255, 252, 25, 0, 0, 0, 0, 0, 0, 22, 251, 255, 255, 255, 51, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 231, 255, 255, 255,
+ 57, 0, 0, 0, 4, 243, 255, 255, 255, 39, 0, 0, 0, 0, 0, 0,
+ 38, 255, 255, 255, 243, 3, 0, 0, 0, 61, 255, 255, 255, 225, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 231, 255,
+ 255, 255, 210, 117, 255, 255, 255, 255, 101, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 29, 232, 255, 255, 255, 243, 44, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 203,
+ 255, 255, 255, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 233, 255, 255, 255, 49, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 255, 255, 0,
+ 255, 255, 255, 255, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 47, 255, 255, 255, 215, 0, 0, 200, 255, 255, 255, 195, 3, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 216, 255,
+ 255, 255, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 48, 255, 255, 255, 255, 0, 1, 209, 255, 255, 255, 182, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 174, 255, 255, 255, 230, 25, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 28, 232, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 14, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 255, 255, 255, 255, 0, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255,
+ 255, 255, 188, 255, 255, 255, 255, 243, 72, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 0, 6, 232, 255, 255, 255, 155, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 163, 255, 255, 255, 230,
+ 5, 0, 255, 255, 255, 255, 255, 126, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 127, 255, 255, 255, 233, 5, 0, 5, 234, 255, 255, 255, 126,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 131, 255, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 9, 255, 255, 255, 245, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 159, 255, 255, 255, 161, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 49, 254, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 29, 253, 255, 255, 255, 46, 46, 255, 255, 255,
+ 253, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 158,
+ 255, 255, 255, 255, 255, 255, 255, 84, 0, 0, 0, 0, 0, 0, 0, 83,
+ 255, 255, 255, 255, 255, 255, 255, 157, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 36, 250, 255, 255,
+ 255, 99, 0, 0, 0, 0, 255, 255, 255, 255, 0, 215, 255, 255, 255, 50,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 51, 255, 255, 255, 212, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 214, 255, 255, 255, 50, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51,
+ 255, 255, 255, 215, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 2, 122, 254, 255, 255, 255, 233, 18, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 22, 84, 162, 245, 255, 255, 255, 255, 255, 214, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 2, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 2, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 208, 255,
+ 255, 255, 119, 0, 0, 0, 0, 0, 0, 115, 255, 255, 255, 209, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 168, 255, 255, 255,
+ 121, 0, 0, 0, 55, 255, 255, 255, 232, 0, 0, 0, 0, 0, 0, 0,
+ 0, 231, 255, 255, 255, 53, 0, 0, 0, 125, 255, 255, 255, 162, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 255, 255,
+ 255, 249, 47, 2, 195, 255, 255, 255, 241, 29, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 10, 206, 255, 255, 255, 254, 78, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 125,
+ 255, 255, 255, 178, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 196, 255, 255, 255, 154, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 72, 252, 255, 255, 255, 255, 255, 0,
+ 255, 255, 255, 255, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 22, 255, 255, 255, 231, 0, 0, 82, 255, 255, 255, 255, 150, 2,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 233, 255,
+ 255, 255, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 22, 255, 255, 255, 255, 0, 0, 88, 255, 255, 255, 255, 140, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 59, 254, 255, 255, 255, 217, 38, 0, 0, 0, 0, 0, 0, 0, 40,
+ 219, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255,
+ 255, 255, 255, 255, 255, 255, 233, 51, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 132, 255, 255, 255, 254, 85,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 92, 255, 255, 255, 255, 128,
+ 0, 0, 255, 255, 255, 255, 255, 246, 51, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 51, 247, 255, 255, 255, 139, 0, 0, 0, 141, 255, 255, 255, 246,
+ 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 248, 255, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 73, 255, 255, 255, 217, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 104, 255, 255, 255, 247, 36, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 16, 218, 255, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 184, 255, 255, 255, 144, 145, 255, 255, 255,
+ 183, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 91,
+ 255, 255, 255, 255, 255, 255, 253, 18, 0, 0, 0, 0, 0, 0, 0, 17,
+ 253, 255, 255, 255, 255, 255, 255, 90, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 149, 255, 255,
+ 255, 227, 8, 0, 0, 0, 255, 255, 255, 255, 0, 185, 255, 255, 255, 96,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 97, 255, 255, 255, 183, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 184, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 97,
+ 255, 255, 255, 178, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 116, 255, 255, 255, 255, 144, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 18, 139, 252, 255, 255, 255, 255, 77, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 247, 255, 255, 255, 14, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 16, 255, 255, 255, 246, 0, 0, 0, 0, 0, 0, 0, 0, 0, 111, 255,
+ 255, 255, 216, 0, 0, 0, 0, 0, 0, 212, 255, 255, 255, 112, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 104, 255, 255, 255,
+ 185, 0, 0, 0, 117, 255, 255, 255, 170, 0, 0, 0, 0, 0, 0, 0,
+ 0, 170, 255, 255, 255, 115, 0, 0, 0, 189, 255, 255, 255, 98, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 93, 255, 255, 255,
+ 255, 121, 0, 0, 37, 246, 255, 255, 255, 183, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 171, 255, 255, 255, 255, 123, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48,
+ 255, 255, 255, 246, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 254, 105, 0,
+ 0, 0, 0, 0, 0, 0, 1, 102, 250, 255, 255, 255, 255, 255, 255, 0,
+ 255, 255, 255, 255, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 7, 255, 255, 255, 247, 0, 0, 2, 191, 255, 255, 255, 255, 179,
+ 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 81, 197, 0, 248, 255,
+ 255, 255, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 7, 255, 255, 255, 255, 0, 0, 2, 198, 255, 255, 255, 255, 176, 26, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 111, 207, 0, 0, 0,
+ 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 154, 255, 255, 255, 255, 249, 150, 65, 22, 5, 23, 66, 151, 250,
+ 255, 255, 178, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255,
+ 255, 255, 255, 255, 255, 255, 182, 6, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 23, 237, 255, 255, 255, 251,
+ 102, 1, 0, 0, 0, 0, 0, 0, 2, 109, 252, 255, 255, 255, 235, 22,
+ 0, 0, 255, 255, 255, 255, 255, 255, 235, 60, 0, 0, 0, 0, 0, 0,
+ 0, 60, 235, 255, 255, 255, 242, 27, 0, 0, 0, 28, 242, 255, 255, 255,
+ 235, 60, 0, 0, 0, 0, 0, 0, 0, 63, 237, 255, 255, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 209, 117, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 41, 225, 255, 255, 255, 149, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 21, 250, 255, 255, 255, 208, 21, 0,
+ 0, 0, 0, 0, 0, 0, 41, 215, 255, 255, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 85, 255, 255, 255, 236, 236, 255, 255, 255,
+ 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24,
+ 255, 255, 255, 255, 255, 255, 205, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 204, 255, 255, 255, 255, 255, 255, 23, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 244, 255,
+ 255, 255, 118, 0, 0, 0, 255, 255, 255, 255, 0, 141, 255, 255, 255, 151,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 152, 255, 255, 255, 138, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 139, 255, 255, 255, 151, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 152,
+ 255, 255, 255, 139, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 179, 255, 255, 255, 248, 27, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 80, 252, 255, 255, 255, 153, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 231, 255, 255, 255, 27, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 29, 255, 255, 255, 229, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 249,
+ 255, 255, 255, 58, 0, 0, 0, 0, 53, 255, 255, 255, 250, 21, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 255, 255, 255,
+ 243, 4, 0, 0, 179, 255, 255, 255, 108, 0, 0, 0, 0, 0, 0, 0,
+ 0, 108, 255, 255, 255, 177, 0, 0, 6, 246, 255, 255, 255, 34, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 240, 255, 255, 255,
+ 197, 3, 0, 0, 0, 114, 255, 255, 255, 255, 98, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 127, 255, 255, 255, 255, 168, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 225, 255, 255, 255, 77, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 249, 255, 255, 255, 255, 185,
+ 82, 26, 5, 15, 42, 108, 206, 255, 255, 255, 171, 255, 255, 255, 255, 0,
+ 255, 255, 255, 255, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 7, 255, 255, 255, 247, 0, 0, 0, 25, 233, 255, 255, 255, 255,
+ 249, 161, 88, 36, 15, 5, 20, 46, 95, 156, 235, 255, 255, 0, 248, 255,
+ 255, 255, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 7, 255, 255, 255, 255, 0, 0, 0, 27, 233, 255, 255, 255, 255, 249, 164,
+ 92, 38, 16, 3, 12, 25, 54, 94, 140, 207, 254, 255, 255, 0, 0, 0,
+ 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 15, 209, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 235, 29, 255, 255, 255, 253, 0, 255, 255, 255, 255, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255,
+ 255, 255, 230, 255, 255, 255, 255, 184, 10, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 89, 255, 255, 255, 255,
+ 255, 201, 103, 37, 12, 11, 38, 104, 203, 255, 255, 255, 255, 255, 84, 0,
+ 0, 0, 255, 255, 255, 255, 179, 255, 255, 254, 164, 72, 25, 6, 25, 72,
+ 164, 254, 255, 255, 255, 255, 110, 0, 0, 0, 0, 0, 111, 255, 255, 255,
+ 255, 254, 163, 71, 24, 6, 25, 73, 166, 254, 255, 255, 178, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 255, 255, 255, 216, 150, 100, 59, 27, 13, 2, 9, 33, 79, 158,
+ 248, 255, 255, 255, 252, 47, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 164, 255, 255, 255, 255, 234, 119,
+ 43, 11, 8, 29, 81, 163, 252, 255, 255, 166, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 6, 236, 255, 255, 255, 255, 255, 255, 235,
+ 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 212, 255, 255, 255, 255, 255, 137, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 137, 255, 255, 255, 255, 255, 211, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 130, 255,
+ 255, 255, 238, 16, 0, 0, 255, 255, 255, 255, 0, 89, 255, 255, 255, 213,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 212, 255, 255, 255, 86, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 86, 255, 255, 255, 213, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 212,
+ 255, 255, 255, 91, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 27, 246, 255, 255, 255, 137, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 149, 255, 255, 255, 211, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 214, 255, 255, 255, 58, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 60, 255, 255, 255, 212, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172,
+ 255, 255, 255, 156, 0, 0, 0, 0, 150, 255, 255, 255, 174, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 255, 255,
+ 255, 56, 0, 2, 239, 255, 255, 255, 45, 0, 0, 0, 0, 0, 0, 0,
+ 0, 46, 255, 255, 255, 237, 1, 0, 60, 255, 255, 255, 225, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 189, 255, 255, 255, 244,
+ 35, 0, 0, 0, 0, 2, 198, 255, 255, 255, 240, 27, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 83, 254, 255, 255, 255, 204, 9, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 149, 255, 255, 255, 155, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 134, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 222, 20, 255, 255, 255, 255, 0,
+ 255, 255, 255, 255, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 22, 255, 255, 255, 231, 0, 0, 0, 0, 55, 235, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 233, 255,
+ 255, 255, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 22, 255, 255, 255, 255, 0, 0, 0, 0, 54, 233, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 21, 212, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 240, 57, 4, 255, 255, 255, 239, 0, 255, 255, 255, 255, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255,
+ 255, 255, 35, 223, 255, 255, 255, 255, 195, 14, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 144, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 139, 0, 0,
+ 0, 0, 255, 255, 255, 255, 29, 235, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 173, 2, 0, 0, 0, 0, 0, 2, 173, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 234, 29, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 128, 0, 0, 0, 0, 0, 253, 255, 255, 255, 2, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 238, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 212, 15, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 144, 255, 255, 255, 255, 255, 255, 143,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 145, 255, 255, 255, 255, 255, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 69, 255, 255, 255, 255, 255, 144, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 234,
+ 255, 255, 255, 136, 0, 0, 255, 255, 255, 255, 0, 22, 254, 255, 255, 255,
+ 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 55, 255, 255, 255, 253, 20, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 19, 253, 255, 255, 255, 55, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 255,
+ 255, 255, 254, 21, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 134, 255, 255, 255, 239, 16, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 51, 255, 255, 255, 235, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 185, 255, 255, 255, 107, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 109, 255, 255, 255, 183, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 74,
+ 255, 255, 255, 242, 10, 0, 0, 8, 239, 255, 255, 255, 76, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 169, 255, 255,
+ 255, 120, 0, 48, 255, 255, 255, 237, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 238, 255, 255, 255, 45, 0, 124, 255, 255, 255, 162, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 111, 255, 255, 255, 255, 103,
+ 0, 0, 0, 0, 0, 0, 39, 247, 255, 255, 255, 180, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 48, 244, 255, 255, 255, 231, 28, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 71, 255, 255, 255, 231, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 175, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 222, 37, 0, 255, 255, 255, 255, 0,
+ 255, 255, 255, 255, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 47, 255, 255, 255, 215, 0, 0, 0, 0, 0, 35, 215, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 216, 255,
+ 255, 255, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 48, 255, 255, 255, 255, 0, 0, 0, 0, 0, 30, 206, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 11, 143, 252, 255, 255, 255, 255, 255, 255, 255, 255, 202,
+ 41, 0, 21, 255, 255, 255, 224, 0, 255, 255, 255, 255, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255,
+ 255, 255, 0, 28, 215, 255, 255, 255, 255, 205, 20, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 129, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 125, 0, 0, 0,
+ 0, 0, 255, 255, 255, 255, 0, 57, 241, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 182, 6, 0, 0, 0, 0, 0, 0, 0, 6, 183, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 240, 57, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 131, 0, 0, 0, 0, 0, 0, 241, 255, 255, 255, 13, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 245, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 213, 27, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 45, 255, 255, 255, 255, 255, 255, 45,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 78, 255, 255, 255, 255, 248, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 9, 248, 255, 255, 255, 255, 77, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 110,
+ 255, 255, 255, 246, 27, 0, 255, 255, 255, 255, 0, 0, 195, 255, 255, 255,
+ 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 159, 255, 255, 255, 192, 0, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 190, 255, 255, 255, 160, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 159, 255,
+ 255, 255, 203, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 16, 238, 255, 255, 255, 133, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 11, 255, 255, 255, 250, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 255, 255, 255, 169, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 171, 255, 255, 255, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3,
+ 229, 255, 255, 255, 95, 0, 0, 89, 255, 255, 255, 231, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 105, 255, 255,
+ 255, 183, 0, 110, 255, 255, 255, 177, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 179, 255, 255, 255, 107, 0, 188, 255, 255, 255, 98, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 247, 255, 255, 255, 182, 0,
+ 0, 0, 0, 0, 0, 0, 0, 117, 255, 255, 255, 255, 94, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 22, 225, 255, 255, 255, 248, 57, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 6, 242, 255, 255, 255, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 130, 247, 255, 255,
+ 255, 255, 255, 255, 255, 255, 254, 164, 20, 0, 0, 255, 255, 255, 255, 0,
+ 255, 255, 255, 255, 94, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 95, 255, 255, 255, 180, 0, 0, 0, 0, 0, 0, 6, 111, 233,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 180, 60, 0, 182, 255,
+ 255, 255, 94, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 96, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 4, 98, 221, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 232, 144, 48, 0, 0, 0,
+ 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 34, 122, 196, 227, 250, 245, 215, 161, 74, 1,
+ 0, 0, 47, 255, 255, 255, 207, 0, 255, 255, 255, 255, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255,
+ 255, 255, 0, 0, 21, 207, 255, 255, 255, 255, 213, 26, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 60, 202,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 200, 57, 0, 0, 0, 0,
+ 0, 0, 255, 255, 255, 255, 0, 0, 41, 202, 255, 255, 255, 255, 255, 255,
+ 255, 255, 246, 116, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 117,
+ 247, 255, 255, 255, 255, 255, 255, 255, 255, 202, 41, 0, 0, 255, 255, 255,
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 46, 137, 224, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 203, 60, 0, 0, 0, 0, 0, 0, 0, 225, 255, 255, 255, 47, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 209, 255, 255, 255,
+ 255, 255, 255, 255, 255, 253, 156, 14, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6,
+ 222, 255, 255, 255, 155, 0, 255, 255, 255, 255, 0, 0, 99, 255, 255, 255,
+ 250, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 47, 250, 255, 255, 255, 95, 0, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 92, 255, 255, 255, 250, 48, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47, 250, 255,
+ 255, 255, 106, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 119, 255, 255, 255, 244, 23, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 16, 255, 255, 255, 235, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 255, 255, 255, 247, 16,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18,
+ 248, 255, 255, 255, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 135, 255, 255, 255, 192, 0, 0, 186, 255, 255, 255, 137, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 255, 255,
+ 255, 242, 4, 172, 255, 255, 255, 115, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 117, 255, 255, 255, 168, 6, 245, 255, 255, 255, 35, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 4, 204, 255, 255, 255, 237, 25, 0,
+ 0, 0, 0, 0, 0, 0, 0, 3, 200, 255, 255, 255, 238, 25, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 6, 196, 255, 255, 255, 255, 95, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 172, 255, 255, 255, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 123, 190,
+ 230, 249, 248, 230, 191, 128, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 159, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 159, 255, 255, 255, 127, 0, 0, 0, 0, 0, 0, 0, 0, 8,
+ 73, 145, 201, 223, 244, 249, 235, 209, 163, 103, 23, 0, 0, 0, 129, 255,
+ 255, 255, 159, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 161, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 4, 62, 130,
+ 191, 218, 238, 252, 242, 229, 200, 161, 114, 47, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 102, 255, 255, 255, 163, 0, 255, 255, 255, 255, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255,
+ 255, 255, 0, 0, 0, 15, 197, 255, 255, 255, 255, 221, 33, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
+ 58, 134, 197, 223, 245, 245, 223, 197, 133, 57, 1, 0, 0, 0, 0, 0,
+ 0, 0, 255, 255, 255, 255, 0, 0, 0, 1, 75, 161, 216, 245, 249, 224,
+ 191, 111, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 23, 112, 192, 225, 249, 245, 215, 161, 74, 1, 0, 0, 0, 255, 255, 255,
+ 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 40, 107, 158, 201, 231, 246, 251, 238, 220, 178, 127,
+ 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 185, 255, 255, 255, 127, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 81, 159, 216,
+ 238, 252, 239, 201, 136, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 91, 255, 255, 255, 252, 40, 255, 255, 255, 255, 0, 0, 8, 230, 255, 255,
+ 255, 208, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 5, 207, 255, 255, 255, 226, 6, 0, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 5, 223, 255, 255, 255, 208, 5, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 207, 255, 255,
+ 255, 240, 11, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 10, 230, 255, 255, 255, 147, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 58, 255, 255, 255, 212, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 235, 255, 255, 255, 138,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 142,
+ 255, 255, 255, 234, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 38, 255, 255, 255, 254, 35, 29, 253, 255, 255, 255, 40, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 233, 255,
+ 255, 255, 55, 233, 255, 255, 255, 52, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 55, 255, 255, 255, 229, 60, 255, 255, 255, 226, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 130, 255, 255, 255, 255, 86, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 42, 248, 255, 255, 255, 177, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 158, 255, 255, 255, 255, 141, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 94, 255, 255, 255, 209, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 242, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 10, 242, 255, 255, 255, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 255,
+ 255, 255, 242, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11,
+ 243, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 181, 255, 255, 255, 113, 0, 255, 255, 255, 255, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 10, 186, 255, 255, 255, 255, 228, 41, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 131, 255, 255, 255, 248, 125,
+ 44, 11, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 207, 255, 255, 255, 174, 255, 255, 255, 255, 0, 0, 0, 101, 255, 255,
+ 255, 255, 164, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 160, 255, 255, 255, 255, 97, 0, 0, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 91, 255, 255, 255, 255, 164, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 160, 255, 255, 255,
+ 255, 124, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 106, 255, 255, 255, 249, 32, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 159, 255, 255, 255, 163, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 131, 255, 255, 255, 253,
+ 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 78, 254,
+ 255, 255, 255, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 196, 255, 255, 255, 131, 124, 255, 255, 255, 199, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 255,
+ 255, 255, 160, 255, 255, 255, 242, 3, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 4, 244, 255, 255, 255, 160, 255, 255, 255, 163, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 55, 252, 255, 255, 255, 166, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 121, 255, 255, 255, 255, 91, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 113, 255, 255, 255, 255, 183, 3, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 20, 252, 255, 255, 255, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 255, 126, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 127, 255, 255, 255, 233, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 234,
+ 255, 255, 255, 126, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 131,
+ 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 45, 254, 255, 255, 255, 53, 0, 255, 255, 255, 255, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 7, 175, 255, 255, 255, 255, 235, 49, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 72, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 1, 195, 255,
+ 255, 255, 255, 171, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 7, 166, 255, 255, 255, 255, 191, 1, 0, 0, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 184, 255, 255, 255, 255, 171, 8, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 166, 255, 255, 255, 255,
+ 216, 6, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 221, 255, 255, 255, 160, 0,
+ 0, 0, 201, 91, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 86, 255, 255, 255, 255, 83, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 239, 255, 255, 255,
+ 251, 117, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 119, 252, 255,
+ 255, 255, 237, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 99, 255, 255, 255, 226, 220, 255, 255, 255, 101, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 255,
+ 255, 255, 251, 255, 255, 255, 184, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 187, 255, 255, 255, 251, 255, 255, 255, 99, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 10, 217, 255, 255, 255, 228, 17, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 203, 255, 255, 255, 236, 23,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 71, 252, 255, 255, 255, 217, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 195, 255, 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 255, 246, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 51, 247, 255, 255, 255, 139, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 141,
+ 255, 255, 255, 246, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 248,
+ 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 11, 211, 255, 255, 255, 212, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 4, 163, 255, 255, 255, 255, 240, 59,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 158, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 190, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 27, 233,
+ 255, 255, 255, 255, 221, 78, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 73, 219, 255, 255, 255, 255, 231, 25, 0, 0, 0, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 21, 226, 255, 255, 255, 255, 221, 78, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 219, 255, 255, 255, 255, 247,
+ 54, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 92, 255, 255, 255, 253, 42,
+ 0, 0, 255, 255, 232, 142, 61, 2, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 13, 132, 253, 255, 255, 255, 221, 8, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 255, 255, 255,
+ 255, 255, 232, 140, 79, 34, 16, 4, 16, 33, 78, 140, 232, 255, 255, 255,
+ 255, 254, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 12, 244, 255, 255, 255, 255, 255, 255, 246, 14, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 255,
+ 255, 255, 255, 255, 255, 255, 121, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 125, 255, 255, 255, 255, 255, 255, 255, 35, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 149, 255, 255, 255, 254, 70, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 249, 255, 255, 255, 173,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 239, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 118, 255, 255, 255, 186, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 255, 255, 235, 60, 0, 0, 0, 0, 0, 0, 0, 60,
+ 235, 255, 255, 255, 242, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28,
+ 242, 255, 255, 255, 235, 60, 0, 0, 0, 0, 0, 0, 0, 63, 237, 255,
+ 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 195, 75, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39,
+ 209, 255, 255, 255, 255, 110, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 1, 150, 255, 255, 255, 255, 245,
+ 70, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 159, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 53, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 52,
+ 241, 255, 255, 255, 255, 255, 214, 134, 64, 35, 12, 10, 34, 61, 130, 210,
+ 255, 255, 255, 255, 255, 239, 49, 0, 0, 0, 0, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 42, 234, 255, 255, 255, 255, 255, 214,
+ 134, 64, 35, 12, 10, 34, 61, 130, 210, 255, 255, 255, 255, 255, 254, 85,
+ 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 211, 255, 255, 255, 174,
+ 0, 0, 255, 255, 255, 255, 255, 234, 170, 121, 72, 39, 23, 6, 7, 19,
+ 47, 89, 158, 238, 255, 255, 255, 255, 255, 73, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 125, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 121, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 160, 255, 255, 255, 255, 255, 255, 163, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 234,
+ 255, 255, 255, 255, 255, 255, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 63, 255, 255, 255, 255, 255, 255, 226, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 71, 255, 255, 255, 255, 148, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 124, 255, 255, 255, 255,
+ 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 40, 255, 255, 255, 249, 13, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 179, 255, 255, 254, 164, 72, 25, 6, 25, 72, 164, 254,
+ 255, 255, 255, 255, 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 111, 255, 255, 255, 255, 254, 163, 71, 24, 6, 25, 73, 166, 254, 255, 255,
+ 178, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 255, 255, 229, 146, 86, 39, 16, 4, 16, 38, 93, 170, 252,
+ 255, 255, 255, 255, 208, 5, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 137, 255, 255, 255, 255,
+ 249, 81, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 135, 188,
+ 227, 243, 254, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 171, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 51, 228, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 228, 49, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 220, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 245, 84, 0,
+ 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 78, 255, 255, 255, 255,
+ 54, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 254, 110, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 88, 246,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 245,
+ 86, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 62, 255, 255, 255, 255, 255, 255, 65, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172,
+ 255, 255, 255, 255, 255, 246, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 8, 248, 255, 255, 255, 255, 255, 163, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 17, 229, 255, 255, 255, 217, 10, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 206, 255, 255, 255,
+ 235, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 219, 255, 255, 255, 85, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 29, 235, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 173, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 2, 173, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 234,
+ 29, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 242, 46, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 123, 255, 255, 255,
+ 255, 252, 93, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 37, 251, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 23, 175, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 176, 24, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 163, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 209, 50, 0, 0,
+ 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 199, 255, 255, 255,
+ 188, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 233, 88, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29,
+ 146, 246, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 246, 146, 29,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 220, 255, 255, 255, 255, 223, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 108,
+ 255, 255, 255, 255, 255, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 195, 255, 255, 255, 255, 255, 99, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 168, 255, 255, 255, 252, 55, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 250, 255, 255,
+ 255, 171, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 141, 255, 255, 255, 162, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 57, 241, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 182, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 6, 183, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 240, 57,
+ 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 230, 48, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 109, 254, 255,
+ 255, 255, 254, 105, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 68, 191, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 192, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 57, 180, 253, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 134, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 37, 111, 185, 245, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 227, 134, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 17, 83, 145, 199, 220, 238, 251, 239, 221, 199, 146, 83, 17, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 64, 255, 255, 255, 236, 3, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 41, 202, 255, 255, 255, 255, 255, 255, 255, 255,
+ 246, 116, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 117, 247, 255, 255, 255, 255, 255, 255, 255, 255, 202, 41, 0,
+ 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 60, 179, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 248, 147, 24, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255,
+ 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 252,
+ 255, 255, 255, 255, 120, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 33, 110, 170, 210, 238, 250, 250, 238, 211, 171, 111,
+ 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 103,
+ 164, 205, 235, 249, 255, 248, 234, 255, 255, 255, 255, 220, 27, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 4, 49, 104, 148, 183, 216, 234, 246, 252, 240, 227,
+ 209, 167, 121, 62, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 4, 238, 255, 255, 255, 61, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 75, 161, 216, 245, 249, 224, 191, 111,
+ 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 23, 112, 192, 225, 249, 245, 215, 161, 74, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 23, 102, 161, 206, 234, 248, 248, 230, 210, 164, 98,
+ 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 2, 255, 255, 255, 252, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
+ 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 3, 168, 255, 255, 255, 255, 217, 24, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 165, 255, 255, 255, 139, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 10, 255, 255, 255, 240, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 4, 171, 255, 255, 255, 255, 214, 22, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 87, 255, 255, 255, 217, 0, 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 255, 255, 226, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 4, 175, 255, 255, 255, 255, 211, 20,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 72, 255, 255, 255, 191, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 179, 255, 255, 255, 255, 208,
+ 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 156, 255, 255, 255, 147, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 182, 255, 255, 255, 255,
+ 205, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 4, 34, 133, 254, 255, 255, 255, 72, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 225, 5, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 82, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 255, 255, 255, 255, 255, 248, 104, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 255, 253, 237, 206, 138, 32, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 164, 255, 255, 255, 254, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 73, 254, 255, 255, 255, 164, 0, 0, 203, 255, 255, 255, 136, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 142,
+ 255, 255, 255, 201, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 32, 115, 170, 209, 232, 244, 254, 255, 0, 255, 255,
+ 255, 255, 0, 255, 254, 244, 233, 210, 171, 118, 33, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 14, 222, 255, 255, 255, 232, 22, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 23, 233, 255, 255, 255, 222, 14, 0, 0, 98, 255, 255, 255, 234, 7,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 239,
+ 255, 255, 255, 92, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 9, 151, 254, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255,
+ 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 154, 10, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 119, 188, 223, 245, 246,
+ 222, 178, 116, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 122, 250, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 56, 251, 255, 255, 255, 181, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 182, 255, 255, 255, 252, 57, 0, 0, 0, 9, 238, 255, 255, 255, 92,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 104, 255,
+ 255, 255, 233, 6, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 158, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255,
+ 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 160, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 56, 188, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 210, 121, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 70, 209, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 124, 255, 255, 255, 255, 110, 0, 0, 0, 0, 0, 0, 0,
+ 111, 255, 255, 255, 255, 126, 0, 0, 0, 0, 0, 142, 255, 255, 255, 198,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 211, 255,
+ 255, 255, 131, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 251, 0, 0, 0, 0, 0, 0,
+ 0, 0, 39, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255,
+ 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 39, 0, 0,
+ 0, 0, 0, 0, 0, 0, 15, 157, 254, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 201, 127, 66, 23, 5, 23, 54, 125, 209,
+ 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 2, 193, 255, 255, 255, 247, 45, 0, 0, 0, 0, 0, 46,
+ 248, 255, 255, 255, 195, 3, 0, 0, 0, 0, 0, 37, 254, 255, 255, 255,
+ 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 255, 255,
+ 255, 251, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 10, 203, 255, 255, 255, 255, 102, 0, 0, 0, 0, 0, 0,
+ 0, 0, 129, 255, 255, 255, 255, 173, 75, 30, 10, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 9, 28, 73, 169, 255, 255, 255, 255, 129, 0, 0,
+ 0, 0, 0, 0, 0, 0, 225, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 225, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 29, 238, 255, 255, 255, 212, 8, 0, 0, 0, 9, 213,
+ 255, 255, 255, 240, 31, 0, 0, 0, 0, 0, 0, 0, 186, 255, 255, 255,
+ 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 174, 255, 255,
+ 255, 170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 175, 255, 255, 255, 255, 141, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 181, 255, 255, 255, 169, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 168, 255, 255, 255, 180, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 215, 129, 56, 25, 5, 24,
+ 67, 129, 202, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 160, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 84, 255, 255, 255, 255, 149, 0, 0, 0, 150, 255,
+ 255, 255, 255, 88, 0, 0, 0, 0, 0, 0, 0, 0, 81, 255, 255, 255,
+ 244, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 252, 255, 255,
+ 255, 61, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 140, 255, 255, 255, 255, 177, 2, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 222, 255, 255, 255, 68, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 68, 255, 255, 255, 221, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 255, 221, 81, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 34, 123, 212, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 193, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 156, 255, 255, 255, 255, 77, 0, 77, 255, 255,
+ 255, 255, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 227, 255, 255,
+ 255, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 135, 255, 255, 255,
+ 208, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 104, 255, 255, 255, 255, 206, 11, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 239, 255, 255, 255, 23, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 24, 255, 255, 255, 237, 0, 0,
+ 0, 0, 0, 0, 0, 0, 255, 149, 9, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 44, 118, 179, 223, 247, 246, 224, 190, 123, 46,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 11, 217, 255, 255, 255, 234, 50, 235, 255, 255,
+ 255, 220, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 125, 255, 255,
+ 255, 213, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 235, 255, 255, 255,
+ 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 71, 251, 255, 255, 255, 229, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 250, 255, 255, 255, 5, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 7, 255, 255, 255, 250, 0, 0,
+ 0, 0, 0, 0, 0, 0, 90, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 50, 249, 255, 255, 255, 250, 255, 255, 255,
+ 251, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 250, 255,
+ 255, 255, 64, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 238,
+ 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44,
+ 241, 255, 255, 255, 245, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 116, 255, 255, 255, 255, 255, 255, 255,
+ 122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 169, 255,
+ 255, 255, 170, 0, 0, 0, 0, 0, 0, 0, 0, 205, 255, 255, 255, 139,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 225,
+ 255, 255, 255, 253, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 186, 255, 255, 255, 255, 255, 193,
+ 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 255,
+ 255, 255, 250, 25, 0, 0, 0, 0, 0, 0, 58, 255, 255, 255, 253, 33,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 203, 255,
+ 255, 255, 255, 117, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 255, 255, 255, 255, 254, 36,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 213,
+ 255, 255, 255, 125, 0, 0, 0, 0, 0, 0, 166, 255, 255, 255, 178, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 174, 255, 255,
+ 255, 255, 155, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 12, 218, 255, 255, 255, 255, 255, 146,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 108,
+ 255, 255, 255, 227, 3, 0, 0, 0, 0, 25, 250, 255, 255, 255, 69, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 140, 255, 255, 255,
+ 255, 189, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 161, 255, 255, 255, 255, 255, 255, 255,
+ 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14,
+ 243, 255, 255, 255, 81, 0, 0, 0, 0, 128, 255, 255, 255, 216, 1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 103, 255, 255, 255, 255,
+ 216, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 255, 255, 255, 254, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 254, 255, 255, 255, 3, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 91, 255, 255, 255, 255, 252, 255, 255, 255,
+ 241, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 152, 255, 255, 255, 187, 0, 0, 0, 5, 230, 255, 255, 255, 108, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 251, 255, 255, 255, 236,
+ 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 18, 255, 255, 255, 246, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 247, 255, 255, 255, 17, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 35, 242, 255, 255, 255, 213, 61, 249, 255, 255,
+ 255, 205, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 47, 255, 255, 255, 254, 37, 0, 0, 89, 255, 255, 255, 242, 13, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 241, 255, 255, 255, 249, 61,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 56, 255, 255, 255, 228, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 230, 255, 255, 255, 55, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 5, 202, 255, 255, 255, 247, 45, 0, 107, 255, 255,
+ 255, 255, 150, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 197, 255, 255, 255, 142, 0, 0, 198, 255, 255, 255, 147, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 23, 224, 255, 255, 255, 255, 94, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 128, 255, 255, 255, 194, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 196, 255, 255, 255, 127, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 139, 255, 255, 255, 255, 105, 0, 0, 0, 169, 255,
+ 255, 255, 255, 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 91, 255, 255, 255, 238, 9, 50, 255, 255, 255, 254, 39, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 9, 202, 255, 255, 255, 255, 132, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 33, 239, 255, 255, 255, 134, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 137, 255, 255, 255, 238, 33,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 70, 254, 255, 255, 255, 174, 0, 0, 0, 0, 13, 218,
+ 255, 255, 255, 242, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 6, 234, 255, 255, 255, 98, 159, 255, 255, 255, 186, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 173, 255, 255, 255, 255, 169, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 15, 51, 123,
+ 236, 255, 255, 255, 252, 38, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 41, 253, 255, 255, 255, 236,
+ 123, 51, 15, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 23, 232, 255, 255, 255, 225, 17, 0, 0, 0, 0, 0, 48,
+ 247, 255, 255, 255, 208, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 135, 255, 255, 255, 213, 247, 255, 255, 255, 77, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 139, 255, 255, 255, 255, 200, 8, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255,
+ 255, 255, 255, 254, 108, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 254, 255, 255, 255,
+ 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 184, 255, 255, 255, 252, 60, 0, 0, 0, 0, 0, 0, 0,
+ 102, 255, 255, 255, 255, 154, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 32, 253, 255, 255, 255, 255, 255, 255, 222, 2, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 102, 255, 255, 255, 255, 225, 23, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255,
+ 255, 252, 187, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 187, 251, 255,
+ 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 255, 255, 255, 255, 125, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 164, 255, 255, 255, 255, 91, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 180, 255, 255, 255, 255, 255, 255, 116, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 251, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255,
+ 255, 248, 175, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 174, 248, 255,
+ 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 52, 250, 255, 255, 255, 191, 2, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 12, 215, 255, 255, 255, 243, 40, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 74, 255, 255, 255, 255, 255, 245, 17, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255,
+ 255, 255, 255, 251, 93, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 252, 255, 255, 255,
+ 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 13, 219, 255, 255, 255, 236, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 44, 246, 255, 255, 255, 211, 9, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 2, 221, 255, 255, 255, 255, 155, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 2, 16, 54, 129,
+ 240, 255, 255, 255, 248, 31, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 34, 249, 255, 255, 255, 239,
+ 127, 53, 16, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 163, 255, 255, 255, 255, 77, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 97, 255, 255, 255, 255, 159, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 120, 255, 255, 255, 255, 46, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
+ 40, 242, 255, 255, 255, 129, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 132, 255, 255, 255, 241, 37,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 151, 255, 255, 255, 193, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 134, 255, 255, 255, 191, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 193, 255, 255, 255, 132, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 24, 244, 255, 255, 255, 85, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 60, 255, 255, 255, 227, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 228, 255, 255, 255, 58, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 172, 255, 255, 255, 228, 4, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 20, 255, 255, 255, 245, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 247, 255, 255, 255, 19, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 93, 255, 255, 255, 255, 124, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 5, 255, 255, 255, 254, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 254, 255, 255, 255, 5, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 51, 244, 255, 255, 255, 245, 22, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 31,
+ 115, 240, 255, 255, 255, 255, 137, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 231, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255,
+ 255, 255, 255, 255, 253, 75, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255,
+ 255, 255, 255, 245, 93, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 254,
+ 240, 205, 135, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 250, 255, 255, 255, 5, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 7, 255, 255, 255, 250, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 239, 255, 255, 255, 22, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 23, 255, 255, 255, 237, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 222, 255, 255, 255, 67, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 67, 255, 255, 255, 221, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 181, 255, 255, 255, 167, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 165, 255, 255, 255, 180, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 129, 255, 255, 255, 255, 169, 74, 28, 8, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 8, 27, 71, 166, 255, 255, 255, 255, 129, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 39, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255,
+ 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 39, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 158, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255,
+ 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 160, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 9, 153, 254, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255,
+ 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 156, 10, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 32, 116, 171, 210, 234, 245, 254, 255, 0, 255, 255,
+ 255, 255, 0, 255, 254, 245, 235, 212, 172, 118, 35, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0};
+
+Font DejaVu = {56, 45, -11, DejaVu_chars, DejaVu_kern, 1016, 141, DejaVu_tex};
+
+}}
+
diff --git a/Samples/CommonSrc/Render/Render_GL_Device.cpp b/Samples/CommonSrc/Render/Render_GL_Device.cpp
new file mode 100644
index 0000000..d2ba42e
--- /dev/null
+++ b/Samples/CommonSrc/Render/Render_GL_Device.cpp
@@ -0,0 +1,849 @@
+/************************************************************************************
+
+Filename : Render_GL_Device.cpp
+Content : RenderDevice implementation for OpenGL
+Created : September 10, 2012
+Authors : Andrew Reisse
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#include "../Render/Render_GL_Device.h"
+#include "Kernel/OVR_Log.h"
+
+namespace OVR { namespace Render { namespace GL {
+
+
+
+static const char* StdVertexShaderSrc =
+ "uniform mat4 Proj;\n"
+ "uniform mat4 View;\n"
+ "attribute vec4 Position;\n"
+ "attribute vec4 Color;\n"
+ "attribute vec2 TexCoord;\n"
+ "attribute vec2 TexCoord1;\n"
+ "attribute vec3 Normal;\n"
+ "varying vec4 oColor;\n"
+ "varying vec2 oTexCoord;\n"
+ "varying vec2 oTexCoord1;\n"
+ "varying vec3 oNormal;\n"
+ "varying vec3 oVPos;\n"
+ "void main()\n"
+ "{\n"
+ " gl_Position = Proj * (View * Position);\n"
+ " oNormal = vec3(View * vec4(Normal,0));\n"
+ " oVPos = vec3(View * Position);\n"
+ " oTexCoord = TexCoord;\n"
+ " oTexCoord1 = TexCoord1;\n"
+ " oColor = Color;\n"
+ "}\n";
+
+static const char* DirectVertexShaderSrc =
+ "uniform mat4 View;\n"
+ "attribute vec4 Position;\n"
+ "attribute vec4 Color;\n"
+ "attribute vec2 TexCoord;\n"
+ "attribute vec3 Normal;\n"
+ "varying vec4 oColor;\n"
+ "varying vec2 oTexCoord;\n"
+ "varying vec3 oNormal;\n"
+ "void main()\n"
+ "{\n"
+ " gl_Position = View * Position;\n"
+ " oTexCoord = TexCoord;\n"
+ " oColor = Color;\n"
+ " oNormal = vec3(View * vec4(Normal,0));\n"
+ "}\n";
+
+static const char* SolidFragShaderSrc =
+ "uniform vec4 Color;\n"
+ "void main()\n"
+ "{\n"
+ " gl_FragColor = Color;\n"
+ "}\n";
+
+static const char* GouraudFragShaderSrc =
+ "varying vec4 oColor;\n"
+ "void main()\n"
+ "{\n"
+ " gl_FragColor = oColor;\n"
+ "}\n";
+
+static const char* TextureFragShaderSrc =
+ "uniform sampler2D Texture0;\n"
+ "varying vec4 oColor;\n"
+ "varying vec2 oTexCoord;\n"
+ "void main()\n"
+ "{\n"
+ " gl_FragColor = oColor * texture2D(Texture0, oTexCoord);\n"
+ " if (gl_FragColor.a < 0.4)\n"
+ " discard;\n"
+ "}\n";
+
+#define LIGHTING_COMMON \
+ "uniform vec3 Ambient;\n" \
+ "uniform vec4 LightPos[8];\n" \
+ "uniform vec4 LightColor[8];\n" \
+ "uniform float LightCount;\n" \
+ "varying vec4 oColor;\n" \
+ "varying vec2 oTexCoord;\n" \
+ "varying vec3 oNormal;\n" \
+ "varying vec3 oVPos;\n" \
+ "vec4 DoLight()\n" \
+ "{\n" \
+ " vec3 norm = normalize(oNormal);\n" \
+ " vec3 light = Ambient;\n" \
+ " for (int i = 0; i < int(LightCount); i++)\n" \
+ " {\n" \
+ " vec3 ltp = (LightPos[i].xyz - oVPos);\n" \
+ " float ldist = length(ltp);\n" \
+ " ltp = normalize(ltp);\n" \
+ " light += clamp(LightColor[i].rgb * oColor.rgb * (dot(norm, ltp) / ldist), 0.0,1.0);\n" \
+ " }\n" \
+ " return vec4(light, oColor.a);\n" \
+ "}\n"
+
+static const char* LitSolidFragShaderSrc =
+ LIGHTING_COMMON
+ "void main()\n"
+ "{\n"
+ " gl_FragColor = DoLight() * oColor;\n"
+ "}\n";
+
+static const char* LitTextureFragShaderSrc =
+ "uniform sampler2D Texture0;\n"
+ LIGHTING_COMMON
+ "void main()\n"
+ "{\n"
+ " gl_FragColor = DoLight() * texture2D(Texture0, oTexCoord);\n"
+ "}\n";
+
+static const char* AlphaTextureFragShaderSrc =
+ "uniform sampler2D Texture0;\n"
+ "varying vec4 oColor;\n"
+ "varying vec2 oTexCoord;\n"
+ "void main()\n"
+ "{\n"
+ " gl_FragColor = oColor * vec4(1,1,1,texture2D(Texture0, oTexCoord).a);\n"
+ "}\n";
+
+static const char* MultiTextureFragShaderSrc =
+ "uniform sampler2D Texture0;\n"
+ "uniform sampler2D Texture1;\n"
+ "varying vec4 oColor;\n"
+ "varying vec2 oTexCoord;\n"
+ "varying vec2 oTexCoord1;\n"
+ "void main()\n"
+ "{\n"
+ " vec4 color1 = texture2D(Texture0, oTexCoord);\n"
+ " vec4 color2 = texture2D(Texture1, oTexCoord1);\n"
+ " color2.rgb = color2.rgb * mix(1.9, 1.2, clamp(length(color2.rgb),0.0,1.0));\n"
+ " color2 = color1 * color2;\n"
+ " if (color2.a <= 0.6)\n"
+ " discard;\n"
+ " gl_FragColor = color2;\n"
+ "}\n";
+
+static const char* PostProcessVertexShaderSrc =
+ "uniform mat4 View;\n"
+ "uniform mat4 Texm;\n"
+ "attribute vec4 Position;\n"
+ "attribute vec2 TexCoord;\n"
+ "varying vec2 oTexCoord;\n"
+ "void main()\n"
+ "{\n"
+ " gl_Position = View * Position;\n"
+ " oTexCoord = vec2(Texm * vec4(TexCoord,0,1));\n"
+ " oTexCoord.y = 1.0-oTexCoord.y;\n"
+ "}\n";
+
+static const char* PostProcessFragShaderSrc =
+ "uniform vec2 LensCenter;\n"
+ "uniform vec2 ScreenCenter;\n"
+ "uniform vec2 Scale;\n"
+ "uniform vec2 ScaleIn;\n"
+ "uniform vec4 HmdWarpParam;\n"
+ "uniform sampler2D Texture0;\n"
+ "varying vec2 oTexCoord;\n"
+ "\n"
+ "vec2 HmdWarp(vec2 in01)\n"
+ "{\n"
+ " vec2 theta = (in01 - LensCenter) * ScaleIn;\n" // Scales to [-1, 1]
+ " float rSq = theta.x * theta.x + theta.y * theta.y;\n"
+ " vec2 theta1 = theta * (HmdWarpParam.x + HmdWarpParam.y * rSq + "
+ " HmdWarpParam.z * rSq * rSq + HmdWarpParam.w * rSq * rSq * rSq);\n"
+ " return LensCenter + Scale * theta1;\n"
+ "}\n"
+ "void main()\n"
+ "{\n"
+ " vec2 tc = HmdWarp(oTexCoord);\n"
+ " if (!all(equal(clamp(tc, ScreenCenter-vec2(0.25,0.5), ScreenCenter+vec2(0.25,0.5)), tc)))\n"
+ " gl_FragColor = vec4(0);\n"
+ " else\n"
+ " gl_FragColor = texture2D(Texture0, tc);\n"
+ "}\n";
+
+// Shader with lens distortion and chromatic aberration correction.
+static const char* PostProcessFullFragShaderSrc =
+ "uniform vec2 LensCenter;\n"
+ "uniform vec2 ScreenCenter;\n"
+ "uniform vec2 Scale;\n"
+ "uniform vec2 ScaleIn;\n"
+ "uniform vec4 HmdWarpParam;\n"
+ "uniform vec4 ChromAbParam;\n"
+ "uniform sampler2D Texture0;\n"
+ "varying vec2 oTexCoord;\n"
+ "\n"
+ // Scales input texture coordinates for distortion.
+ // ScaleIn maps texture coordinates to Scales to ([-1, 1]), although top/bottom will be
+ // larger due to aspect ratio.
+ "void main()\n"
+ "{\n"
+ " vec2 theta = (oTexCoord - LensCenter) * ScaleIn;\n" // Scales to [-1, 1]
+ " float rSq= theta.x * theta.x + theta.y * theta.y;\n"
+ " vec2 theta1 = theta * (HmdWarpParam.x + HmdWarpParam.y * rSq + "
+ " HmdWarpParam.z * rSq * rSq + HmdWarpParam.w * rSq * rSq * rSq);\n"
+ " \n"
+ " // Detect whether blue texture coordinates are out of range since these will scaled out the furthest.\n"
+ " vec2 thetaBlue = theta1 * (ChromAbParam.z + ChromAbParam.w * rSq);\n"
+ " vec2 tcBlue = LensCenter + Scale * thetaBlue;\n"
+ " if (!all(equal(clamp(tcBlue, ScreenCenter-vec2(0.25,0.5), ScreenCenter+vec2(0.25,0.5)), tcBlue)))\n"
+ " {\n"
+ " gl_FragColor = vec4(0);\n"
+ " return;\n"
+ " }\n"
+ " \n"
+ " // Now do blue texture lookup.\n"
+ " float blue = texture2D(Texture0, tcBlue).b;\n"
+ " \n"
+ " // Do green lookup (no scaling).\n"
+ " vec2 tcGreen = LensCenter + Scale * theta1;\n"
+ " vec4 center = texture2D(Texture0, tcGreen);\n"
+ " \n"
+ " // Do red scale and lookup.\n"
+ " vec2 thetaRed = theta1 * (ChromAbParam.x + ChromAbParam.y * rSq);\n"
+ " vec2 tcRed = LensCenter + Scale * thetaRed;\n"
+ " float red = texture2D(Texture0, tcRed).r;\n"
+ " \n"
+ " gl_FragColor = vec4(red, center.g, blue, center.a);\n"
+ "}\n";
+
+static const char* VShaderSrcs[VShader_Count] =
+{
+ DirectVertexShaderSrc,
+ StdVertexShaderSrc,
+ PostProcessVertexShaderSrc
+};
+static const char* FShaderSrcs[FShader_Count] =
+{
+ SolidFragShaderSrc,
+ GouraudFragShaderSrc,
+ TextureFragShaderSrc,
+ AlphaTextureFragShaderSrc,
+ PostProcessFragShaderSrc,
+ PostProcessFullFragShaderSrc,
+ LitSolidFragShaderSrc,
+ LitTextureFragShaderSrc,
+ MultiTextureFragShaderSrc
+};
+
+
+
+RenderDevice::RenderDevice(const RendererParams& p)
+{
+ for (int i = 0; i < VShader_Count; i++)
+ VertexShaders[i] = *new Shader(this, Shader_Vertex, VShaderSrcs[i]);
+
+ for (int i = 0; i < FShader_Count; i++)
+ FragShaders[i] = *new Shader(this, Shader_Fragment, FShaderSrcs[i]);
+
+ Ptr<ShaderSet> gouraudShaders = *new ShaderSet();
+ gouraudShaders->SetShader(VertexShaders[VShader_MVP]);
+ gouraudShaders->SetShader(FragShaders[FShader_Gouraud]);
+ DefaultFill = *new ShaderFill(gouraudShaders);
+
+ glGenFramebuffersEXT(1, &CurrentFbo);
+}
+
+Shader *RenderDevice::LoadBuiltinShader(ShaderStage stage, int shader)
+{
+ switch (stage)
+ {
+ case Shader_Vertex: return VertexShaders[shader];
+ case Shader_Fragment: return FragShaders[shader];
+ default:
+ return NULL;
+ }
+}
+
+
+void RenderDevice::BeginRendering()
+{
+ glEnable(GL_DEPTH_TEST);
+ glEnable(GL_CULL_FACE);
+ glFrontFace(GL_CW);
+
+ glLineWidth(3.0f);
+ glEnable(GL_LINE_SMOOTH);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+}
+
+void RenderDevice::SetDepthMode(bool enable, bool write, CompareFunc func)
+{
+ if (enable)
+ {
+ glEnable(GL_DEPTH_TEST);
+ glDepthMask(write);
+ switch (func)
+ {
+ case Compare_Always: glDepthFunc(GL_ALWAYS); break;
+ case Compare_Less: glDepthFunc(GL_LESS); break;
+ case Compare_Greater: glDepthFunc(GL_GREATER); break;
+ default: assert(0);
+ }
+ }
+ else
+ glDisable(GL_DEPTH_TEST);
+}
+
+void RenderDevice::SetRealViewport(const Viewport& vp)
+{
+ int wh;
+ if (CurRenderTarget)
+ wh = CurRenderTarget->Height;
+ else
+ wh = WindowHeight;
+ glViewport(vp.x, wh-vp.y-vp.h, vp.w, vp.h);
+
+ glEnable(GL_SCISSOR_TEST);
+ glScissor(vp.x, wh-vp.y-vp.h, vp.w, vp.h);
+}
+
+void RenderDevice::Clear(float r, float g, float b, float a, float depth)
+{
+ glClearColor(r,g,b,a);
+ glClearDepth(depth);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+}
+
+RBuffer* RenderDevice::GetDepthBuffer(int w, int h, int ms)
+{
+ for (unsigned i = 0; i < DepthBuffers.GetSize(); i++)
+ if (w == DepthBuffers[i]->Width && h == DepthBuffers[i]->Height)// && ms == DepthBuffers[i]->Samples)
+ return DepthBuffers[i];
+
+ //Ptr<Texture> newDepth = *CreateTexture(Texture_Depth|Texture_RenderTarget|ms, w, h, NULL);
+ Ptr<RBuffer> newDepth = *new RBuffer(GL_DEPTH24_STENCIL8, w, h); // combined depth stencil
+ DepthBuffers.PushBack(newDepth);
+ return newDepth.GetPtr();
+}
+
+void RenderDevice::SetRenderTarget(Render::Texture* color, Render::Texture*, Render::Texture* stencil)
+{
+ OVR_UNUSED(stencil);
+
+ CurRenderTarget = (Texture*)color;
+ if (color == NULL)
+ {
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+ return;
+ }
+ //if (depth == NULL)
+ RBuffer* depth = GetDepthBuffer(color->GetWidth(), color->GetHeight(), 0); //CurRenderTarget->Samples);
+
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, CurrentFbo);
+ glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, ((Texture*)color)->TexId, 0);
+ if (depth)
+ //glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, ((Texture*)depth)->TexId, 0);
+ glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, ((RBuffer*)depth)->BufId);
+ else
+ glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, 0);
+
+ GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
+ if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
+ OVR_DEBUG_LOG(("framebuffer not complete: %x", status));
+}
+
+
+void RenderDevice::SetWorldUniforms(const Matrix4f& proj)
+{
+ Proj = proj.Transposed();
+}
+
+void RenderDevice::SetTexture(Render::ShaderStage, int slot, const Texture* t)
+{
+ glActiveTexture(GL_TEXTURE0 + slot);
+ glBindTexture(GL_TEXTURE_2D, ((Texture*)t)->TexId);
+ glActiveTexture(GL_TEXTURE0);
+}
+
+Buffer* RenderDevice::CreateBuffer()
+{
+ return new Buffer(this);
+}
+
+Fill* RenderDevice::CreateSimpleFill(int flags)
+{
+ OVR_UNUSED(flags);
+ return DefaultFill;
+}
+
+
+void RenderDevice::Render(const Matrix4f& matrix, Model* model)
+{
+ // Store data in buffers if not already
+ if (!model->VertexBuffer)
+ {
+ Ptr<Render::Buffer> vb = *CreateBuffer();
+ vb->Data(Buffer_Vertex, &model->Vertices[0], model->Vertices.GetSize() * sizeof(Vertex));
+ model->VertexBuffer = vb;
+ }
+ if (!model->IndexBuffer)
+ {
+ Ptr<Render::Buffer> ib = *CreateBuffer();
+ ib->Data(Buffer_Index, &model->Indices[0], model->Indices.GetSize() * 2);
+ model->IndexBuffer = ib;
+ }
+
+ Render(model->Fill ? (const Fill*)model->Fill : (const Fill*)DefaultFill,
+ model->VertexBuffer, model->IndexBuffer,
+ matrix, 0, (int)model->Indices.GetSize(), model->GetPrimType());
+}
+
+void RenderDevice::Render(const Fill* fill, Render::Buffer* vertices, Render::Buffer* indices,
+ const Matrix4f& matrix, int offset, int count, PrimitiveType rprim)
+{
+ ShaderSet* shaders = (ShaderSet*) ((ShaderFill*)fill)->GetShaders();
+
+ GLenum prim;
+ switch (rprim)
+ {
+ case Prim_Triangles:
+ prim = GL_TRIANGLES;
+ break;
+ case Prim_Lines:
+ prim = GL_LINES;
+ break;
+ case Prim_TriangleStrip:
+ prim = GL_TRIANGLE_STRIP;
+ break;
+ default:
+ assert(0);
+ return;
+ }
+
+ fill->Set();
+ if (shaders->ProjLoc >= 0)
+ glUniformMatrix4fv(shaders->ProjLoc, 1, 0, &Proj.M[0][0]);
+ if (shaders->ViewLoc >= 0)
+ glUniformMatrix4fv(shaders->ViewLoc, 1, 0, &matrix.Transposed().M[0][0]);
+
+ if (shaders->UsesLighting && Lighting->Version != shaders->LightingVer)
+ {
+ shaders->LightingVer = Lighting->Version;
+ Lighting->Set(shaders);
+ }
+
+ glBindBuffer(GL_ARRAY_BUFFER, ((Buffer*)vertices)->GLBuffer);
+ for (int i = 0; i < 5; i++)
+ glEnableVertexAttribArray(i);
+
+ glVertexAttribPointer(0, 3, GL_FLOAT, false, sizeof(Vertex), (char*)offset + offsetof(Vertex, Pos));
+ glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, true, sizeof(Vertex), (char*)offset + offsetof(Vertex, C));
+ glVertexAttribPointer(2, 2, GL_FLOAT, false, sizeof(Vertex), (char*)offset + offsetof(Vertex, U));
+ glVertexAttribPointer(3, 2, GL_FLOAT, false, sizeof(Vertex), (char*)offset + offsetof(Vertex, U2));
+ glVertexAttribPointer(4, 3, GL_FLOAT, false, sizeof(Vertex), (char*)offset + offsetof(Vertex, Norm));
+
+ if (indices)
+ {
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ((Buffer*)indices)->GLBuffer);
+ glDrawElements(prim, count, GL_UNSIGNED_SHORT, NULL);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ }
+ else
+ {
+ glDrawArrays(prim, 0, count);
+ }
+
+ for (int i = 0; i < 5; i++)
+ glDisableVertexAttribArray(i);
+}
+
+void RenderDevice::RenderWithAlpha(const Fill* fill, Render::Buffer* vertices, Render::Buffer* indices,
+ const Matrix4f& matrix, int offset, int count, PrimitiveType rprim)
+{
+ //glEnable(GL_BLEND);
+ Render(fill, vertices, indices, matrix, offset, count, rprim);
+ //glDisable(GL_BLEND);
+}
+
+void RenderDevice::SetLighting(const LightingParams* lt)
+{
+ Lighting = lt;
+}
+
+Buffer::~Buffer()
+{
+ if (GLBuffer)
+ glDeleteBuffers(1, &GLBuffer);
+}
+
+bool Buffer::Data(int use, const void* buffer, size_t size)
+{
+ switch (use & Buffer_TypeMask)
+ {
+ case Buffer_Index: Use = GL_ELEMENT_ARRAY_BUFFER; break;
+ default: Use = GL_ARRAY_BUFFER; break;
+ }
+
+ if (!GLBuffer)
+ glGenBuffers(1, &GLBuffer);
+
+ int mode = GL_DYNAMIC_DRAW;
+ if (use & Buffer_ReadOnly)
+ mode = GL_STATIC_DRAW;
+
+ glBindBuffer(Use, GLBuffer);
+ glBufferData(Use, size, buffer, mode);
+ glBindBuffer(Use, 0);
+ return 1;
+}
+
+void* Buffer::Map(size_t start, size_t size, int flags)
+{
+ int mode = GL_WRITE_ONLY;
+ //if (flags & Map_Unsynchronized)
+ // mode |= GL_MAP_UNSYNCHRONIZED;
+
+ glBindBuffer(Use, GLBuffer);
+ void* v = glMapBuffer(Use, mode);
+ glBindBuffer(Use, 0);
+ return v;
+}
+
+bool Buffer::Unmap(void*)
+{
+ glBindBuffer(Use, GLBuffer);
+ int r = glUnmapBuffer(Use);
+ glBindBuffer(Use, 0);
+ return r;
+}
+
+bool Shader::Compile(const char* src)
+{
+ if (!GLShader)
+ GLShader = glCreateShader(GLStage());
+
+ glShaderSource(GLShader, 1, &src, 0);
+ glCompileShader(GLShader);
+ GLint r;
+ glGetShaderiv(GLShader, GL_COMPILE_STATUS, &r);
+ if (!r)
+ {
+ GLchar msg[1024];
+ glGetShaderInfoLog(GLShader, sizeof(msg), 0, msg);
+ if (msg[0])
+ OVR_DEBUG_LOG(("Compiling shader\n%s\nfailed: %s\n", src, msg));
+ if (!r)
+ return 0;
+ }
+ return 1;
+}
+
+ShaderSet::ShaderSet()
+{
+ Prog = glCreateProgram();
+}
+ShaderSet::~ShaderSet()
+{
+ glDeleteProgram(Prog);
+}
+
+bool ShaderSet::Link()
+{
+ glBindAttribLocation(Prog, 0, "Position");
+ glBindAttribLocation(Prog, 1, "Color");
+ glBindAttribLocation(Prog, 2, "TexCoord");
+ glBindAttribLocation(Prog, 3, "TexCoord1");
+ glBindAttribLocation(Prog, 4, "Normal");
+
+ glLinkProgram(Prog);
+ GLint r;
+ glGetProgramiv(Prog, GL_LINK_STATUS, &r);
+ if (!r)
+ {
+ GLchar msg[1024];
+ glGetProgramInfoLog(Prog, sizeof(msg), 0, msg);
+ OVR_DEBUG_LOG(("Linking shaders failed: %s\n", msg));
+ if (!r)
+ return 0;
+ }
+ glUseProgram(Prog);
+
+ UniformInfo.Clear();
+ LightingVer = 0;
+ UsesLighting = 0;
+ GLuint i = 0;
+ for(;; i++)
+ {
+ GLsizei namelen;
+ GLint size = 0;
+ GLenum type;
+ GLchar name[32];
+ glGetActiveUniform(Prog, i, sizeof(name), &namelen, &size, &type, name);
+ if (size)
+ {
+ int l = glGetUniformLocation(Prog, name);
+ char *np = name;
+ while (*np)
+ {
+ if (*np == '[')
+ *np = 0;
+ np++;
+ }
+ Uniform u;
+ u.Name = name;
+ u.Location = l;
+ u.Size = size;
+ switch (type)
+ {
+ case GL_FLOAT: u.Type = 1; break;
+ case GL_FLOAT_VEC2: u.Type = 2; break;
+ case GL_FLOAT_VEC3: u.Type = 3; break;
+ case GL_FLOAT_VEC4: u.Type = 4; break;
+ case GL_FLOAT_MAT4: u.Type = 16; break;
+ default:
+ continue;
+ }
+ UniformInfo.PushBack(u);
+ if (!strcmp(name, "LightCount"))
+ UsesLighting = 1;
+ }
+ else
+ break;
+ }
+
+ ProjLoc = glGetUniformLocation(Prog, "Proj");
+ ViewLoc = glGetUniformLocation(Prog, "View");
+ for (int i = 0; i < 8; i++)
+ {
+ char texv[32];
+ sprintf(texv, "Texture%d", i);
+ TexLoc[i] = glGetUniformLocation(Prog, texv);
+ if (TexLoc[i] < 0)
+ break;
+
+ glUniform1i(TexLoc[i], i);
+ }
+ if (UsesLighting)
+ OVR_ASSERT(ProjLoc >= 0 && ViewLoc >= 0);
+ return 1;
+}
+
+void ShaderSet::Set(PrimitiveType) const
+{
+ glUseProgram(Prog);
+}
+
+bool ShaderSet::SetUniform(const char* name, int n, const float* v)
+{
+ for (int i = 0; i < UniformInfo.GetSize(); i++)
+ if (!strcmp(UniformInfo[i].Name.ToCStr(), name))
+ {
+ OVR_ASSERT(UniformInfo[i].Location >= 0);
+ glUseProgram(Prog);
+ switch (UniformInfo[i].Type)
+ {
+ case 1: glUniform1fv(UniformInfo[i].Location, n, v); break;
+ case 2: glUniform2fv(UniformInfo[i].Location, n/2, v); break;
+ case 3: glUniform3fv(UniformInfo[i].Location, n/3, v); break;
+ case 4: glUniform4fv(UniformInfo[i].Location, n/4, v); break;
+ default: OVR_ASSERT(0);
+ }
+ return 1;
+ }
+
+ OVR_DEBUG_LOG(("Warning: uniform %s not present in selected shader", name));
+ return 0;
+}
+
+bool ShaderSet::SetUniform4x4f(const char* name, const Matrix4f& m)
+{
+ for (int i = 0; i < UniformInfo.GetSize(); i++)
+ if (!strcmp(UniformInfo[i].Name.ToCStr(), name))
+ {
+ glUseProgram(Prog);
+ glUniformMatrix4fv(UniformInfo[i].Location, 1, 1, &m.M[0][0]);
+ return 1;
+ }
+
+ OVR_DEBUG_LOG(("Warning: uniform %s not present in selected shader", name));
+ return 0;
+}
+
+Texture::Texture(RenderDevice* r, int w, int h) : Ren(r), Width(w), Height(h)
+{
+ glGenTextures(1, &TexId);
+}
+
+Texture::~Texture()
+{
+ if (TexId)
+ glDeleteTextures(1, &TexId);
+}
+
+void Texture::Set(int slot, Render::ShaderStage stage) const
+{
+ Ren->SetTexture(stage, slot, this);
+}
+
+void Texture::SetSampleMode(int sm)
+{
+ glBindTexture(GL_TEXTURE_2D, TexId);
+ switch (sm & Sample_FilterMask)
+ {
+ case Sample_Linear:
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 0);
+ break;
+
+ case Sample_Anisotropic:
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 8);
+ break;
+
+ case Sample_Nearest:
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 0);
+ break;
+ }
+
+ switch (sm & Sample_AddressMask)
+ {
+ case Sample_Repeat:
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ break;
+
+ case Sample_Clamp:
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ break;
+
+ case Sample_ClampBorder:
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
+ break;
+ }
+ glBindTexture(GL_TEXTURE_2D, 0);
+}
+
+Texture* RenderDevice::CreateTexture(int format, int width, int height, const void* data, int mipcount)
+{
+ GLenum glformat, gltype = GL_UNSIGNED_BYTE;
+ switch(format & Texture_TypeMask)
+ {
+ case Texture_RGBA: glformat = GL_RGBA; break;
+ case Texture_R: glformat = GL_ALPHA; break;
+ case Texture_Depth: glformat = GL_DEPTH; gltype = GL_DEPTH_COMPONENT; break;
+ case Texture_DXT1: glformat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; break;
+ case Texture_DXT3: glformat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; break;
+ case Texture_DXT5: glformat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; break;
+ default:
+ return NULL;
+ }
+ Texture* NewTex = new Texture(this, width, height);
+ glBindTexture(GL_TEXTURE_2D, NewTex->TexId);
+ glGetError();
+
+ if (format & Texture_Compressed)
+ {
+ const unsigned char* level = (const unsigned char*)data;
+ int w = width, h = height;
+ for (int i = 0; i < mipcount; i++)
+ {
+ int mipsize = GetTextureSize(format, w, h);
+ glCompressedTexImage2D(GL_TEXTURE_2D, i, glformat, w, h, 0, mipsize, level);
+
+ level += mipsize;
+ w >>= 1;
+ h >>= 1;
+ if (w < 1) w = 1;
+ if (h < 1) h = 1;
+ }
+ }
+ else
+ glTexImage2D(GL_TEXTURE_2D, 0, glformat, width, height, 0, glformat, gltype, data);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ if (format == (Texture_RGBA|Texture_GenMipmaps)) // not render target
+ {
+ int srcw = width, srch = height;
+ int level = 0;
+ UByte* mipmaps = NULL;
+ do
+ {
+ level++;
+ int mipw = srcw >> 1; if (mipw < 1) mipw = 1;
+ int miph = srch >> 1; if (miph < 1) miph = 1;
+ if (mipmaps == NULL)
+ mipmaps = (UByte*)OVR_ALLOC(mipw * miph * 4);
+ FilterRgba2x2(level == 1 ? (const UByte*)data : mipmaps, srcw, srch, mipmaps);
+ glTexImage2D(GL_TEXTURE_2D, level, glformat, mipw, miph, 0, glformat, gltype, mipmaps);
+ srcw = mipw;
+ srch = miph;
+ } while (srcw > 1 || srch > 1);
+ if (mipmaps)
+ OVR_FREE(mipmaps);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, level);
+ }
+ else
+ {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, mipcount-1);
+ }
+
+ OVR_ASSERT(!glGetError());
+ glBindTexture(GL_TEXTURE_2D, 0);
+ return NewTex;
+}
+
+RBuffer::RBuffer(GLenum format, GLint w, GLint h)
+{
+ Width = w;
+ Height = h;
+ glGenRenderbuffersEXT(1, &BufId);
+ glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, BufId);
+ glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, format, w, h);
+ glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
+}
+
+RBuffer::~RBuffer()
+{
+ glDeleteRenderbuffersEXT(1, &BufId);
+}
+
+}}}
diff --git a/Samples/CommonSrc/Render/Render_GL_Device.h b/Samples/CommonSrc/Render/Render_GL_Device.h
new file mode 100644
index 0000000..37cd4f0
--- /dev/null
+++ b/Samples/CommonSrc/Render/Render_GL_Device.h
@@ -0,0 +1,230 @@
+/************************************************************************************
+
+Filename : Render_GL_Device.h
+Content : RenderDevice implementation header for OpenGL
+Created : September 10, 2012
+Authors : Andrew Reisse
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#ifndef OVR_Render_GL_Device_h
+#define OVR_Render_GL_Device_h
+
+#include "../Render/Render_Device.h"
+
+#if defined(OVR_OS_WIN32)
+#include <Windows.h>
+#endif
+
+#if defined(OVR_OS_MAC)
+#include <OpenGL/gl.h>
+#include <OpenGL/glext.h>
+#else
+#define GL_GLEXT_PROTOTYPES
+#include <GL/gl.h>
+#include <GL/glext.h>
+#endif
+
+namespace OVR { namespace Render { namespace GL {
+
+class RenderDevice;
+
+class Buffer : public Render::Buffer
+{
+public:
+ RenderDevice* Ren;
+ size_t Size;
+ GLenum Use;
+ GLuint GLBuffer;
+
+public:
+ Buffer(RenderDevice* r) : Ren(r), Size(0), Use(0), GLBuffer(0) {}
+ ~Buffer();
+
+ GLuint GetBuffer() { return GLBuffer; }
+
+ virtual size_t GetSize() { return Size; }
+ virtual void* Map(size_t start, size_t size, int flags = 0);
+ virtual bool Unmap(void *m);
+ virtual bool Data(int use, const void* buffer, size_t size);
+};
+
+class Texture : public Render::Texture
+{
+public:
+ RenderDevice* Ren;
+ GLuint TexId;
+ int Width, Height;
+
+ Texture(RenderDevice* r, int w, int h);
+ ~Texture();
+
+ virtual int GetWidth() const { return Width; }
+ virtual int GetHeight() const { return Height; }
+
+ virtual void SetSampleMode(int);
+
+ virtual void Set(int slot, ShaderStage stage = Shader_Fragment) const;
+};
+
+class Shader : public Render::Shader
+{
+public:
+ GLuint GLShader;
+
+ Shader(RenderDevice*, ShaderStage st, GLuint s) : Render::Shader(st), GLShader(s) {}
+ Shader(RenderDevice*, ShaderStage st, const char* src) : Render::Shader(st), GLShader(0)
+ {
+ Compile(src);
+ }
+ ~Shader()
+ {
+ if (GLShader)
+ glDeleteShader(GLShader);
+ }
+ bool Compile(const char* src);
+
+ GLenum GLStage() const
+ {
+ switch (Stage)
+ {
+ default: OVR_ASSERT(0); return GL_NONE;
+ case Shader_Vertex: return GL_VERTEX_SHADER;
+ case Shader_Fragment: return GL_FRAGMENT_SHADER;
+ }
+ }
+
+ //void Set(PrimitiveType prim) const;
+ //void SetUniformBuffer(Render::Buffer* buffers, int i = 0);
+};
+
+class ShaderSet : public Render::ShaderSet
+{
+public:
+ GLuint Prog;
+
+ struct Uniform
+ {
+ String Name;
+ int Location, Size;
+ int Type; // currently number of floats in vector
+ };
+ Array<Uniform> UniformInfo;
+
+ int ProjLoc, ViewLoc;
+ int TexLoc[8];
+ bool UsesLighting;
+ int LightingVer;
+
+ ShaderSet();
+ ~ShaderSet();
+
+ virtual void SetShader(Render::Shader *s)
+ {
+ Shaders[s->GetStage()] = s;
+ Shader* gls = (Shader*)s;
+ glAttachShader(Prog, gls->GLShader);
+ if (Shaders[Shader_Vertex] && Shaders[Shader_Fragment])
+ Link();
+ }
+ virtual void UnsetShader(int stage)
+ {
+ Shader* gls = (Shader*)(Render::Shader*)Shaders[stage];
+ if (gls)
+ glDetachShader(Prog, gls->GLShader);
+ Shaders[stage] = NULL;
+ Link();
+ }
+
+ virtual void Set(PrimitiveType prim) const;
+
+ // Set a uniform (other than the standard matrices). It is undefined whether the
+ // uniforms from one shader occupy the same space as those in other shaders
+ // (unless a buffer is used, then each buffer is independent).
+ virtual bool SetUniform(const char* name, int n, const float* v);
+ virtual bool SetUniform4x4f(const char* name, const Matrix4f& m);
+
+ bool Link();
+};
+
+ class RBuffer : public RefCountBase<RBuffer>
+{
+ public:
+ int Width, Height;
+ GLuint BufId;
+
+ RBuffer(GLenum format, GLint w, GLint h);
+ ~RBuffer();
+};
+
+class RenderDevice : public Render::RenderDevice
+{
+ Ptr<Shader> VertexShaders[VShader_Count];
+ Ptr<Shader> FragShaders[FShader_Count];
+
+ Ptr<ShaderFill> DefaultFill;
+
+ Matrix4f Proj;
+
+ Ptr<Texture> CurRenderTarget;
+ Array<Ptr<RBuffer> > DepthBuffers;
+ GLuint CurrentFbo;
+
+ const LightingParams* Lighting;
+
+
+public:
+ RenderDevice(const RendererParams& p);
+
+ virtual void SetRealViewport(const Viewport& vp);
+
+ //virtual void SetScissor(int x, int y, int w, int h);
+
+ virtual void Clear(float r = 0, float g = 0, float b = 0, float a = 1, float depth = 1);
+ virtual void Rect(float left, float top, float right, float bottom) { OVR_UNUSED4(left,top,right,bottom); }
+
+ virtual void BeginRendering();
+ virtual void SetDepthMode(bool enable, bool write, CompareFunc func = Compare_Less);
+ virtual void SetWorldUniforms(const Matrix4f& proj);
+
+ RBuffer* GetDepthBuffer(int w, int h, int ms);
+
+ virtual void SetRenderTarget(Render::Texture* color,
+ Render::Texture* depth = NULL, Render::Texture* stencil = NULL);
+
+ virtual void SetLighting(const LightingParams* lt);
+
+ virtual void Render(const Matrix4f& matrix, Model* model);
+ virtual void Render(const Fill* fill, Render::Buffer* vertices, Render::Buffer* indices,
+ const Matrix4f& matrix, int offset, int count, PrimitiveType prim = Prim_Triangles);
+ virtual void RenderWithAlpha(const Fill* fill, Render::Buffer* vertices, Render::Buffer* indices,
+ const Matrix4f& matrix, int offset, int count, PrimitiveType prim = Prim_Triangles);
+
+ virtual Buffer* CreateBuffer();
+ virtual Texture* CreateTexture(int format, int width, int height, const void* data, int mipcount=1);
+ virtual ShaderSet* CreateShaderSet() { return new ShaderSet; }
+
+ virtual Fill *CreateSimpleFill(int flags = Fill::F_Solid);
+
+ virtual Shader *LoadBuiltinShader(ShaderStage stage, int shader);
+
+ void SetTexture(Render::ShaderStage, int slot, const Texture* t);
+};
+
+}}}
+
+#endif
diff --git a/Samples/CommonSrc/Render/Render_GL_Win32_Device.cpp b/Samples/CommonSrc/Render/Render_GL_Win32_Device.cpp
new file mode 100644
index 0000000..8bbbf88
--- /dev/null
+++ b/Samples/CommonSrc/Render/Render_GL_Win32_Device.cpp
@@ -0,0 +1,90 @@
+/************************************************************************************
+
+Filename : Render_GL_Win32 Device.cpp
+Content : Win32 OpenGL Device implementation
+Created : September 10, 2012
+Authors : Andrew Reisse, Michael Antonov
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#include "Render_GL_Win32_Device.h"
+
+namespace OVR { namespace Render { namespace GL { namespace Win32 {
+
+
+// ***** GL::Win32::RenderDevice
+
+// Implement static initializer function to create this class.
+Render::RenderDevice* RenderDevice::CreateDevice(const RendererParams&, void* oswnd)
+{
+ HWND hwnd = (HWND)oswnd;
+
+ PIXELFORMATDESCRIPTOR pfd;
+ memset(&pfd, 0, sizeof(pfd));
+
+ pfd.nSize = sizeof(pfd);
+ pfd.nVersion = 1;
+ pfd.iPixelType = PFD_TYPE_RGBA;
+ pfd.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER;
+ pfd.cColorBits = 32;
+ pfd.cDepthBits = 16;
+
+ HDC dc = GetDC(hwnd);
+ int pf = ChoosePixelFormat(dc, &pfd);
+ if (!pf)
+ {
+ ReleaseDC(hwnd, dc);
+ return NULL;
+ }
+ if (!SetPixelFormat(dc, pf, &pfd))
+ {
+ ReleaseDC(hwnd, dc);
+ return NULL;
+ }
+ HGLRC context = wglCreateContext(dc);
+ if (!wglMakeCurrent(dc, context))
+ {
+ wglDeleteContext(context);
+ ReleaseDC(hwnd, dc);
+ return NULL;
+ }
+
+ // return new RenderDevice(rp, hwnd, dc, context);
+ return 0;
+}
+
+
+void RenderDevice::Present()
+{
+ SwapBuffers(GdiDc);
+}
+
+void RenderDevice::Shutdown()
+{
+ if (WglContext)
+ {
+ wglMakeCurrent(NULL,NULL);
+ wglDeleteContext(WglContext);
+ ReleaseDC(Window, GdiDc);
+ WglContext = NULL;
+ GdiDc = NULL;
+ Window = NULL;
+ }
+}
+
+}}}}
+
diff --git a/Samples/CommonSrc/Render/Render_GL_Win32_Device.h b/Samples/CommonSrc/Render/Render_GL_Win32_Device.h
new file mode 100644
index 0000000..f58a625
--- /dev/null
+++ b/Samples/CommonSrc/Render/Render_GL_Win32_Device.h
@@ -0,0 +1,59 @@
+/************************************************************************************
+
+Filename : Render_GL_Win32 Device.h
+Content : Win32 OpenGL Device implementation header
+Created : September 10, 2012
+Authors : Andrew Reisse, Michael Antonov
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#ifndef OVR_Render_GL_Win32_Device_h
+#define OVR_Render_GL_Win32_Device_h
+
+#include "Render_GL_Device.h"
+
+#ifdef WIN32
+#include <Windows.h>
+#endif
+
+
+namespace OVR { namespace Render { namespace GL { namespace Win32 {
+
+// ***** GL::Win32::RenderDevice
+
+// Win32-Specific GL Render Device, used to create OpenGL under Windows.
+class RenderDevice : public GL::RenderDevice
+{
+ HWND Window;
+ HGLRC WglContext;
+ HDC GdiDc;
+
+public:
+ RenderDevice(const Render::RendererParams& p, HWND win, HDC dc, HGLRC gl)
+ : GL::RenderDevice(p), Window(win), WglContext(gl), GdiDc(dc) { OVR_UNUSED(p); }
+
+ // Implement static initializer function to create this class.
+ static Render::RenderDevice* CreateDevice(const RendererParams& rp, void* oswnd);
+
+ virtual void Shutdown();
+ virtual void Present();
+};
+
+
+}}}} // OVR::Render::GL::Win32
+
+#endif
diff --git a/Samples/CommonSrc/Render/Render_LoadTextureDDS.cpp b/Samples/CommonSrc/Render/Render_LoadTextureDDS.cpp
new file mode 100644
index 0000000..e11b07f
--- /dev/null
+++ b/Samples/CommonSrc/Render/Render_LoadTextureDDS.cpp
@@ -0,0 +1,121 @@
+/************************************************************************************
+
+Filename : WavPlayer_OSX.h
+Content : A DDS file loader for cross-platform compressed texture support.
+Created : March 5, 2013
+Authors : Peter Hoff, Dan Goodman, Bryan Croteau
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+#include "Render_Device.h"
+
+#ifdef OVR_DEFINE_NEW
+#undef new
+#endif
+
+namespace OVR { namespace Render {
+
+static const UPInt OVR_DDS_PF_FOURCC = 0x4;
+static const UInt32 OVR_DTX1_MAGIC_NUMBER = 827611204;
+static const UInt32 OVR_DTX5_MAGIC_NUMBER = 894720068;
+
+struct OVR_DDS_PIXELFORMAT
+{
+ UInt32 Size;
+ UInt32 Flags;
+ UInt32 FourCC;
+ UInt32 RGBBitCount;
+ UInt32 RBitMask;
+ UInt32 GBitMask;
+ UInt32 BBitMask;
+ UInt32 ABitMask;
+};
+
+struct OVR_DDS_HEADER
+{
+ UInt32 Size;
+ UInt32 Flags;
+ UInt32 Height;
+ UInt32 Width;
+ UInt32 PitchOrLinearSize;
+ UInt32 Depth;
+ UInt32 MipMapCount;
+ UInt32 Reserved1[11];
+ OVR_DDS_PIXELFORMAT PixelFormat;
+ UInt32 Caps;
+ UInt32 Caps2;
+ UInt32 Caps3;
+ UInt32 Caps4;
+ UInt32 Reserved2;
+};
+
+Texture* LoadTextureDDS(RenderDevice* ren, File* f)
+{
+ OVR_DDS_HEADER header;
+ unsigned char filecode[4];
+
+ f->Read(filecode, 4);
+ if (strncmp((const char*)filecode, "DDS ", 4) != 0)
+ {
+ return NULL;
+ }
+
+ f->Read((unsigned char*)(&header), sizeof(header));
+
+ int width = header.Width;
+ int height = header.Height;
+
+ int format = 0;
+
+ UInt32 mipCount = header.MipMapCount;
+ if(mipCount <= 0)
+ {
+ mipCount = 1;
+ }
+ if(header.PixelFormat.Flags & OVR_DDS_PF_FOURCC)
+ {
+ if(header.PixelFormat.FourCC == OVR_DTX1_MAGIC_NUMBER)
+ {
+ format = Texture_DXT1;
+ }
+ else if(header.PixelFormat.FourCC == OVR_DTX5_MAGIC_NUMBER)
+ {
+ format = Texture_DXT5;
+ }
+ else
+ {
+ return NULL;
+ }
+ }
+
+ int byteLen = f->BytesAvailable();
+ unsigned char* bytes = new unsigned char[byteLen];
+ f->Read(bytes, byteLen);
+ Texture* out = ren->CreateTexture(format, (int)width, (int)height, bytes, mipCount);
+ if(strstr(f->GetFilePath(), "_c."))
+ {
+ out->SetSampleMode(Sample_Clamp);
+ }
+ OVR_FREE(bytes);
+ return out;
+}
+
+
+}}
+
+#ifdef OVR_DEFINE_NEW
+#define new OVR_DEFINE_NEW
+#endif \ No newline at end of file
diff --git a/Samples/CommonSrc/Render/Render_LoadTextureTGA.cpp b/Samples/CommonSrc/Render/Render_LoadTextureTGA.cpp
new file mode 100644
index 0000000..0331540
--- /dev/null
+++ b/Samples/CommonSrc/Render/Render_LoadTextureTGA.cpp
@@ -0,0 +1,102 @@
+/************************************************************************************
+
+Filename : Render_LoadeTextureTGA.cpp
+Content : Loading of TGA implementation
+Created : October, 2012
+Authors : Andrew Reisse
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+
+#include "Render_Device.h"
+
+namespace OVR { namespace Render {
+
+Texture* LoadTextureTga(RenderDevice* ren, File* f)
+{
+ int desclen = f->ReadUByte();
+ int palette = f->ReadUByte();
+ OVR_UNUSED(palette);
+ int imgtype = f->ReadUByte();
+ f->ReadUInt16();
+ int palCount = f->ReadUInt16();
+ int palSize = f->ReadUByte();
+ f->ReadUInt16();
+ f->ReadUInt16();
+ int width = f->ReadUInt16();
+ int height = f->ReadUInt16();
+ int bpp = f->ReadUByte();
+ f->ReadUByte();
+ int imgsize = width * height * 4;
+ unsigned char* imgdata = (unsigned char*) OVR_ALLOC(imgsize);
+ unsigned char buf[16];
+ f->Read(imgdata, desclen);
+ f->Read(imgdata, palCount * (palSize + 7) >> 3);
+ int bpl = width * 4;
+
+ switch (imgtype)
+ {
+ case 2:
+ switch (bpp)
+ {
+ case 24:
+ for (int y = 0; y < height; y++)
+ for (int x = 0; x < width; x++)
+ {
+ f->Read(buf, 3);
+ imgdata[y*bpl+x*4+0] = buf[2];
+ imgdata[y*bpl+x*4+1] = buf[1];
+ imgdata[y*bpl+x*4+2] = buf[0];
+ imgdata[y*bpl+x*4+3] = 255;
+ }
+ break;
+ case 32:
+ for (int y = 0; y < height; y++)
+ for (int x = 0; x < width; x++)
+ {
+ f->Read(buf, 4);
+ imgdata[y*bpl+x*4+0] = buf[2];
+ imgdata[y*bpl+x*4+1] = buf[1];
+ imgdata[y*bpl+x*4+2] = buf[0];
+ imgdata[y*bpl+x*4+3] = buf[3];
+ }
+ break;
+
+ default:
+ OVR_FREE(imgdata);
+ return NULL;
+ }
+ break;
+
+ default:
+ OVR_FREE(imgdata);
+ return NULL;
+ }
+
+ Texture* out = ren->CreateTexture(Texture_RGBA|Texture_GenMipmaps, width, height, imgdata);
+
+ // check for clamp based on texture name
+ if(strstr(f->GetFilePath(), "_c."))
+ {
+ out->SetSampleMode(Sample_Clamp);
+ }
+
+ OVR_FREE(imgdata);
+ return out;
+}
+
+}}
diff --git a/Samples/CommonSrc/Render/Render_XmlSceneLoader.cpp b/Samples/CommonSrc/Render/Render_XmlSceneLoader.cpp
new file mode 100644
index 0000000..cc0eb47
--- /dev/null
+++ b/Samples/CommonSrc/Render/Render_XmlSceneLoader.cpp
@@ -0,0 +1,390 @@
+/************************************************************************************
+
+Filename : Render_XmlSceneLoader.cpp
+Content : Imports and exports XML files - implementation
+Created : January 21, 2013
+Authors : Robotic Arm Software - Peter Hoff, Dan Goodman, Bryan Croteau
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#include "Render_XmlSceneLoader.h"
+#include <Kernel/OVR_Log.h>
+
+#ifdef OVR_DEFINE_NEW
+#undef new
+#endif
+
+namespace OVR { namespace Render {
+
+XmlHandler::XmlHandler() : pXmlDocument(NULL)
+{
+ pXmlDocument = new tinyxml2::XMLDocument();
+}
+
+XmlHandler::~XmlHandler()
+{
+ delete pXmlDocument;
+}
+
+bool XmlHandler::ReadFile(const char* fileName, OVR::Render::RenderDevice* pRender,
+ OVR::Render::Scene* pScene,
+ OVR::Array<Ptr<CollisionModel> >* pCollisions,
+ OVR::Array<Ptr<CollisionModel> >* pGroundCollisions)
+{
+ if(pXmlDocument->LoadFile(fileName) != 0)
+ {
+ return false;
+ }
+
+ // Extract the relative path to our working directory for loading textures
+ filePath[0] = 0;
+ SPInt pos = 0;
+ SPInt len = strlen(fileName);
+ for(SPInt i = len; i > 0; i--)
+ {
+ if (fileName[i-1]=='\\' || fileName[i-1]=='/')
+ {
+ memcpy(filePath, fileName, i);
+ filePath[i] = 0;
+ break;
+ }
+ }
+
+ // Load the textures
+ OVR_DEBUG_LOG_TEXT(("Loading textures..."));
+ XMLElement* pXmlTexture = pXmlDocument->FirstChildElement("scene")->FirstChildElement("textures");
+ if (pXmlTexture)
+ {
+ pXmlTexture->QueryIntAttribute("count", &textureCount);
+ pXmlTexture = pXmlTexture->FirstChildElement("texture");
+ }
+
+ for(int i = 0; i < textureCount; ++i)
+ {
+ const char* textureName = pXmlTexture->Attribute("fileName");
+ SPInt dotpos = strcspn(textureName, ".");
+ char fname[300];
+
+ if (pos == len)
+ {
+ OVR_sprintf(fname, 300, "%s", textureName);
+ }
+ else
+ {
+ OVR_sprintf(fname, 300, "%s%s", filePath, textureName);
+ }
+
+ SysFile* pFile = new SysFile(fname);
+ Ptr<Texture> texture;
+ if (textureName[dotpos + 1] == 'd' || textureName[dotpos + 1] == 'D')
+ {
+ // DDS file
+ texture.SetPtr(*LoadTextureDDS(pRender, pFile));
+ }
+ else
+ {
+ texture.SetPtr(*LoadTextureTga(pRender, pFile));
+ }
+
+ Textures.PushBack(texture);
+ pFile->Close();
+ pFile->Release();
+ pXmlTexture = pXmlTexture->NextSiblingElement("texture");
+ }
+ OVR_DEBUG_LOG_TEXT(("Done.\n"));
+
+ // Load the models
+ pXmlDocument->FirstChildElement("scene")->FirstChildElement("models")->
+ QueryIntAttribute("count", &modelCount);
+
+ OVR_DEBUG_LOG(("Loading models... %i models to load...", modelCount));
+ XMLElement* pXmlModel = pXmlDocument->FirstChildElement("scene")->
+ FirstChildElement("models")->FirstChildElement("model");
+ for(int i = 0; i < modelCount; ++i)
+ {
+ if (i % 15 == 0)
+ {
+ OVR_DEBUG_LOG_TEXT(("%i models remaining...", modelCount - i));
+ }
+ Models.PushBack(*new Model(Prim_Triangles));
+ bool isCollisionModel = false;
+ pXmlModel->QueryBoolAttribute("isCollisionModel", &isCollisionModel);
+ Models[i]->IsCollisionModel = isCollisionModel;
+ if (isCollisionModel)
+ {
+ Models[i]->Visible = false;
+ }
+
+ //read the vertices
+ OVR::Array<Vector3f> *vertices = new OVR::Array<Vector3f>();
+ ParseVectorString(pXmlModel->FirstChildElement("vertices")->FirstChild()->
+ ToText()->Value(), vertices);
+
+ for (unsigned int vertexIndex = 0; vertexIndex < vertices->GetSize(); ++vertexIndex)
+ {
+ vertices->At(vertexIndex).x *= -1.0f;
+ }
+
+ //read the normals
+ OVR::Array<Vector3f> *normals = new OVR::Array<Vector3f>();
+ ParseVectorString(pXmlModel->FirstChildElement("normals")->FirstChild()->
+ ToText()->Value(), normals);
+
+ for (unsigned int normalIndex = 0; normalIndex < normals->GetSize(); ++normalIndex)
+ {
+ normals->At(normalIndex).z *= -1.0f;
+ }
+
+ //read the textures
+ OVR::Array<Vector3f> *diffuseUVs = new OVR::Array<Vector3f>();
+ OVR::Array<Vector3f> *lightmapUVs = new OVR::Array<Vector3f>();
+ int diffuseTextureIndex = -1;
+ int lightmapTextureIndex = -1;
+ XMLElement* pXmlCurMaterial = pXmlModel->FirstChildElement("material");
+
+ while(pXmlCurMaterial != NULL)
+ {
+ if(pXmlCurMaterial->Attribute("name", "diffuse"))
+ {
+ pXmlCurMaterial->FirstChildElement("texture")->
+ QueryIntAttribute("index", &diffuseTextureIndex);
+ if(diffuseTextureIndex > -1)
+ {
+ ParseVectorString(pXmlCurMaterial->FirstChildElement("texture")->
+ FirstChild()->ToText()->Value(), diffuseUVs, true);
+ }
+ }
+ else if(pXmlCurMaterial->Attribute("name", "lightmap"))
+ {
+ pXmlCurMaterial->FirstChildElement("texture")->
+ QueryIntAttribute("index", &lightmapTextureIndex);
+ if(lightmapTextureIndex > -1)
+ {
+ XMLElement* firstChildElement = pXmlCurMaterial->FirstChildElement("texture");
+ XMLNode* firstChild = firstChildElement->FirstChild();
+ XMLText* text = firstChild->ToText();
+ const char* value = text->Value();
+ ParseVectorString(value, lightmapUVs, true);
+ }
+ }
+
+ pXmlCurMaterial = pXmlCurMaterial->NextSiblingElement("material");
+ }
+
+ //set up the shader
+ Ptr<ShaderFill> shader = *new ShaderFill(*pRender->CreateShaderSet());
+ shader->GetShaders()->SetShader(pRender->LoadBuiltinShader(Shader_Vertex, VShader_MVP));
+ if(diffuseTextureIndex > -1)
+ {
+ shader->SetTexture(0, Textures[diffuseTextureIndex]);
+ if(lightmapTextureIndex > -1)
+ {
+ shader->GetShaders()->SetShader(pRender->LoadBuiltinShader(Shader_Fragment, FShader_MultiTexture));
+ shader->SetTexture(1, Textures[lightmapTextureIndex]);
+ }
+ else
+ {
+ shader->GetShaders()->SetShader(pRender->LoadBuiltinShader(Shader_Fragment, FShader_Texture));
+ }
+ }
+ else
+ {
+ shader->GetShaders()->SetShader(pRender->LoadBuiltinShader(Shader_Fragment, FShader_LitGouraud));
+ }
+ Models[i]->Fill = shader;
+
+ //add all the vertices to the model
+ const UPInt numVerts = vertices->GetSize();
+ for(UPInt v = 0; v < numVerts; ++v)
+ {
+ if(diffuseTextureIndex > -1)
+ {
+ if(lightmapTextureIndex > -1)
+ {
+ Models[i]->AddVertex(vertices->At(v).z, vertices->At(v).y, vertices->At(v).x, Color(255, 255, 255),
+ diffuseUVs->At(v).x, diffuseUVs->At(v).y, lightmapUVs->At(v).x, lightmapUVs->At(v).y,
+ normals->At(v).x, normals->At(v).y, normals->At(v).z);
+ }
+ else
+ {
+ Models[i]->AddVertex(vertices->At(v).z, vertices->At(v).y, vertices->At(v).x, Color(255, 255, 255),
+ diffuseUVs->At(v).x, diffuseUVs->At(v).y, 0, 0,
+ normals->At(v).x, normals->At(v).y, normals->At(v).z);
+ }
+ }
+ else
+ {
+ Models[i]->AddVertex(vertices->At(v).z, vertices->At(v).y, vertices->At(v).x, Color(255, 0, 0, 128),
+ 0, 0, 0, 0,
+ normals->At(v).x, normals->At(v).y, normals->At(v).z);
+ }
+ }
+
+ // Read the vertex indices for the triangles
+ const char* indexStr = pXmlModel->FirstChildElement("indices")->
+ FirstChild()->ToText()->Value();
+ UPInt stringLength = strlen(indexStr);
+
+ for(UPInt j = 0; j < stringLength;)
+ {
+ UPInt k = j + 1;
+ for(; k < stringLength; ++k)
+ {
+ if(indexStr[k] == ' ')
+ {
+ break;
+ }
+ }
+ char text[20];
+ for(UPInt l = 0; l < k - j; ++l)
+ {
+ text[l] = indexStr[j + l];
+ }
+ text[k - j] = '\0';
+ Models[i]->Indices.InsertAt(0, (unsigned short)atoi(text));
+ j = k + 1;
+ }
+
+ delete vertices;
+ delete normals;
+ delete diffuseUVs;
+ delete lightmapUVs;
+
+ pScene->World.Add(Models[i]);
+ pScene->Models.PushBack(Models[i]);
+ pXmlModel = pXmlModel->NextSiblingElement("model");
+ }
+ OVR_DEBUG_LOG(("Done."));
+
+ //load the collision models
+ OVR_DEBUG_LOG(("Loading collision models... "));
+ XMLElement* pXmlCollisionModel = pXmlDocument->FirstChildElement("scene")->FirstChildElement("collisionModels");
+ if (pXmlCollisionModel)
+ {
+ pXmlCollisionModel->QueryIntAttribute("count", &collisionModelCount);
+ pXmlCollisionModel = pXmlCollisionModel->FirstChildElement("collisionModel");
+ }
+
+ XMLElement* pXmlPlane = NULL;
+ for(int i = 0; i < collisionModelCount; ++i)
+ {
+ Ptr<CollisionModel> cm = *new CollisionModel();
+ int planeCount = 0;
+ pXmlCollisionModel->QueryIntAttribute("planeCount", &planeCount);
+
+ pXmlPlane = pXmlCollisionModel->FirstChildElement("plane");
+ for(int j = 0; j < planeCount; ++j)
+ {
+ Vector3f norm;
+ pXmlPlane->QueryFloatAttribute("nx", &norm.x);
+ pXmlPlane->QueryFloatAttribute("ny", &norm.y);
+ pXmlPlane->QueryFloatAttribute("nz", &norm.z);
+ float D;
+ pXmlPlane->QueryFloatAttribute("d", &D);
+ D -= 0.5f;
+ Planef p(norm.z, norm.y, norm.x * -1.0f, D);
+ cm->Add(p);
+ pXmlPlane = pXmlPlane->NextSiblingElement("plane");
+ }
+
+ pCollisions->PushBack(cm);
+ pXmlCollisionModel = pXmlCollisionModel->NextSiblingElement("collisionModel");
+ }
+ OVR_DEBUG_LOG(("done."));
+
+ //load the ground collision models
+ OVR_DEBUG_LOG(("Loading ground collision models..."));
+ pXmlCollisionModel = pXmlDocument->FirstChildElement("scene")->FirstChildElement("groundCollisionModels");
+ if (pXmlCollisionModel)
+ {
+ pXmlCollisionModel->QueryIntAttribute("count", &groundCollisionModelCount);
+ pXmlCollisionModel = pXmlCollisionModel->FirstChildElement("collisionModel");
+ }
+ pXmlPlane = NULL;
+ for(int i = 0; i < groundCollisionModelCount; ++i)
+ {
+ Ptr<CollisionModel> cm = *new CollisionModel();
+ int planeCount = 0;
+ pXmlCollisionModel->QueryIntAttribute("planeCount", &planeCount);
+
+ pXmlPlane = pXmlCollisionModel->FirstChildElement("plane");
+ for(int j = 0; j < planeCount; ++j)
+ {
+ Vector3f norm;
+ pXmlPlane->QueryFloatAttribute("nx", &norm.x);
+ pXmlPlane->QueryFloatAttribute("ny", &norm.y);
+ pXmlPlane->QueryFloatAttribute("nz", &norm.z);
+ float D;
+ pXmlPlane->QueryFloatAttribute("d", &D);
+ Planef p(norm.z, norm.y, norm.x * -1.0f, D);
+ cm->Add(p);
+ pXmlPlane = pXmlPlane->NextSiblingElement("plane");
+ }
+
+ pGroundCollisions->PushBack(cm);
+ pXmlCollisionModel = pXmlCollisionModel->NextSiblingElement("collisionModel");
+ }
+ OVR_DEBUG_LOG(("done."));
+ return true;
+}
+
+void XmlHandler::ParseVectorString(const char* str, OVR::Array<OVR::Vector3f> *array,
+ bool is2element)
+{
+ UPInt stride = is2element ? 2 : 3;
+ UPInt stringLength = strlen(str);
+ UPInt element = 0;
+ float v[3];
+
+ for(UPInt j = 0; j < stringLength;)
+ {
+ UPInt k = j + 1;
+ for(; k < stringLength; ++k)
+ {
+ if(str[k] == ' ')
+ {
+ break;
+ }
+ }
+ char text[20];
+ for(UPInt l = 0; l < k - j; ++l)
+ {
+ text[l] = str[j + l];
+ }
+ text[k - j] = '\0';
+ v[element] = (float)atof(text);
+
+ if(element == (stride - 1))
+ {
+ //we've got all the elements of our vertex, so store them
+ OVR::Vector3f vect;
+ vect.x = v[0];
+ vect.y = v[1];
+ vect.z = is2element ? 0.0f : v[2];
+ array->PushBack(vect);
+ }
+
+ j = k + 1;
+ element = (element + 1) % stride;
+ }
+}
+
+}} // OVR::Render
+
+#ifdef OVR_DEFINE_NEW
+#define new OVR_DEFINE_NEW
+#endif
diff --git a/Samples/CommonSrc/Render/Render_XmlSceneLoader.h b/Samples/CommonSrc/Render/Render_XmlSceneLoader.h
new file mode 100644
index 0000000..2b888c5
--- /dev/null
+++ b/Samples/CommonSrc/Render/Render_XmlSceneLoader.h
@@ -0,0 +1,74 @@
+/************************************************************************************
+
+Filename : Render_XmlSceneLoader.h
+Content : Imports and exports XML files
+Created : January 21, 2013
+Authors : Robotic Arm Software - Peter Hoff, Dan Goodman, Bryan Croteau
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#ifndef INC_Render_XMLSceneLoader_h
+#define INC_Render_XMLSceneLoader_h
+
+#include "Render_Device.h"
+#include <Kernel/OVR_SysFile.h>
+using namespace OVR;
+using namespace OVR::Render;
+
+#ifdef OVR_DEFINE_NEW
+#undef new
+#endif
+
+#include "../../../3rdParty/TinyXml/tinyxml2.h"
+
+namespace OVR { namespace Render {
+
+using namespace tinyxml2;
+
+class XmlHandler
+{
+public:
+ XmlHandler();
+ ~XmlHandler();
+
+ bool ReadFile(const char* fileName, OVR::Render::RenderDevice* pRender,
+ OVR::Render::Scene* pScene,
+ OVR::Array<Ptr<CollisionModel> >* pColisions,
+ OVR::Array<Ptr<CollisionModel> >* pGroundCollisions);
+
+protected:
+ void ParseVectorString(const char* str, OVR::Array<OVR::Vector3f> *array,
+ bool is2element = false);
+
+private:
+ tinyxml2::XMLDocument* pXmlDocument;
+ char filePath[250];
+ int textureCount;
+ OVR::Array<Ptr<Texture> > Textures;
+ int modelCount;
+ OVR::Array<Ptr<Model> > Models;
+ int collisionModelCount;
+ int groundCollisionModelCount;
+};
+
+}} // OVR::Render
+
+#ifdef OVR_DEFINE_NEW
+#define new OVR_DEFINE_NEW
+#endif
+
+#endif // INC_Render_XMLSceneLoader_h
diff --git a/Samples/LibOVR_Samples_Msvc2010.sln b/Samples/LibOVR_Samples_Msvc2010.sln
new file mode 100644
index 0000000..b3ad124
--- /dev/null
+++ b/Samples/LibOVR_Samples_Msvc2010.sln
@@ -0,0 +1,43 @@
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual Studio 2010
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OculusWorldDemo", "OculusWorldDemo\OculusWorldDemo_Msvc2010.vcxproj", "{8051B877-2992-4F64-8C3B-FAF88B6D83AA}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SensorBoxTest", "SensorBox\SensorBoxTest_Msvc2010.vcxproj", "{8051B837-2982-4F64-8C3B-FAF88B6D83AB}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OculusRoomTiny", "OculusRoomTiny\OculusRoomTiny_Msvc2010.vcxproj", "{80523489-2881-4F64-8C3B-FAF88B60ABCD}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Debug|x64 = Debug|x64
+ Release|Win32 = Release|Win32
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {8051B877-2992-4F64-8C3B-FAF88B6D83AA}.Debug|Win32.ActiveCfg = Debug|Win32
+ {8051B877-2992-4F64-8C3B-FAF88B6D83AA}.Debug|Win32.Build.0 = Debug|Win32
+ {8051B877-2992-4F64-8C3B-FAF88B6D83AA}.Debug|x64.ActiveCfg = Debug|x64
+ {8051B877-2992-4F64-8C3B-FAF88B6D83AA}.Debug|x64.Build.0 = Debug|x64
+ {8051B877-2992-4F64-8C3B-FAF88B6D83AA}.Release|Win32.ActiveCfg = Release|Win32
+ {8051B877-2992-4F64-8C3B-FAF88B6D83AA}.Release|Win32.Build.0 = Release|Win32
+ {8051B877-2992-4F64-8C3B-FAF88B6D83AA}.Release|x64.ActiveCfg = Release|x64
+ {8051B877-2992-4F64-8C3B-FAF88B6D83AA}.Release|x64.Build.0 = Release|x64
+ {8051B837-2982-4F64-8C3B-FAF88B6D83AB}.Debug|Win32.ActiveCfg = Debug|Win32
+ {8051B837-2982-4F64-8C3B-FAF88B6D83AB}.Debug|Win32.Build.0 = Debug|Win32
+ {8051B837-2982-4F64-8C3B-FAF88B6D83AB}.Debug|x64.ActiveCfg = Debug|Win32
+ {8051B837-2982-4F64-8C3B-FAF88B6D83AB}.Release|Win32.ActiveCfg = Release|Win32
+ {8051B837-2982-4F64-8C3B-FAF88B6D83AB}.Release|Win32.Build.0 = Release|Win32
+ {8051B837-2982-4F64-8C3B-FAF88B6D83AB}.Release|x64.ActiveCfg = Release|Win32
+ {80523489-2881-4F64-8C3B-FAF88B60ABCD}.Debug|Win32.ActiveCfg = Debug|Win32
+ {80523489-2881-4F64-8C3B-FAF88B60ABCD}.Debug|Win32.Build.0 = Debug|Win32
+ {80523489-2881-4F64-8C3B-FAF88B60ABCD}.Debug|x64.ActiveCfg = Debug|x64
+ {80523489-2881-4F64-8C3B-FAF88B60ABCD}.Debug|x64.Build.0 = Debug|x64
+ {80523489-2881-4F64-8C3B-FAF88B60ABCD}.Release|Win32.ActiveCfg = Release|Win32
+ {80523489-2881-4F64-8C3B-FAF88B60ABCD}.Release|Win32.Build.0 = Release|Win32
+ {80523489-2881-4F64-8C3B-FAF88B60ABCD}.Release|x64.ActiveCfg = Release|x64
+ {80523489-2881-4F64-8C3B-FAF88B60ABCD}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/Samples/LibOVR_With_Samples.xcodeproj/OculusRoomTiny-Info.plist b/Samples/LibOVR_With_Samples.xcodeproj/OculusRoomTiny-Info.plist
new file mode 100644
index 0000000..1882911
--- /dev/null
+++ b/Samples/LibOVR_With_Samples.xcodeproj/OculusRoomTiny-Info.plist
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleIconFile</key>
+ <string></string>
+ <key>CFBundleIdentifier</key>
+ <string>com.oculusvr.${PRODUCT_NAME:rfc1034identifier}</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1</string>
+ <key>LSMinimumSystemVersion</key>
+ <string>${MACOSX_DEPLOYMENT_TARGET}</string>
+ <key>NSHumanReadableCopyright</key>
+ <string>Copyright © 2013 Oculus VR. All rights reserved.</string>
+ <key>NSMainNibFile</key>
+ <string>MainMenu</string>
+ <key>NSPrincipalClass</key>
+ <string>NSApplication</string>
+</dict>
+</plist>
diff --git a/Samples/LibOVR_With_Samples.xcodeproj/OculusWorldDemo-Info.plist b/Samples/LibOVR_With_Samples.xcodeproj/OculusWorldDemo-Info.plist
new file mode 100644
index 0000000..2464c75
--- /dev/null
+++ b/Samples/LibOVR_With_Samples.xcodeproj/OculusWorldDemo-Info.plist
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleIconFile</key>
+ <string>Oculus</string>
+ <key>CFBundleIdentifier</key>
+ <string>com.oculusvr.${PRODUCT_NAME:rfc1034identifier}</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1</string>
+ <key>LSMinimumSystemVersion</key>
+ <string>${MACOSX_DEPLOYMENT_TARGET}</string>
+ <key>NSHumanReadableCopyright</key>
+ <string>Copyright © 2013 Oculus VR. All rights reserved.</string>
+ <key>NSMainNibFile</key>
+ <string>MainMenu</string>
+ <key>NSPrincipalClass</key>
+ <string>NSApplication</string>
+</dict>
+</plist>
diff --git a/Samples/LibOVR_With_Samples.xcodeproj/SensorBoxTest-Info.plist b/Samples/LibOVR_With_Samples.xcodeproj/SensorBoxTest-Info.plist
new file mode 100644
index 0000000..2464c75
--- /dev/null
+++ b/Samples/LibOVR_With_Samples.xcodeproj/SensorBoxTest-Info.plist
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleIconFile</key>
+ <string>Oculus</string>
+ <key>CFBundleIdentifier</key>
+ <string>com.oculusvr.${PRODUCT_NAME:rfc1034identifier}</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1</string>
+ <key>LSMinimumSystemVersion</key>
+ <string>${MACOSX_DEPLOYMENT_TARGET}</string>
+ <key>NSHumanReadableCopyright</key>
+ <string>Copyright © 2013 Oculus VR. All rights reserved.</string>
+ <key>NSMainNibFile</key>
+ <string>MainMenu</string>
+ <key>NSPrincipalClass</key>
+ <string>NSApplication</string>
+</dict>
+</plist>
diff --git a/Samples/LibOVR_With_Samples.xcodeproj/project.pbxproj b/Samples/LibOVR_With_Samples.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..af9448d
--- /dev/null
+++ b/Samples/LibOVR_With_Samples.xcodeproj/project.pbxproj
@@ -0,0 +1,1092 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 37973B391739D78B0093BBB8 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4945072216E55A0300B9FF78 /* Cocoa.framework */; };
+ 37973B501739E1B60093BBB8 /* OculusRoomModel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 37973B4F1739E1B60093BBB8 /* OculusRoomModel.cpp */; };
+ 37973B531739E1D20093BBB8 /* RenderTiny_Device.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 37973B511739E1D20093BBB8 /* RenderTiny_Device.cpp */; };
+ 37973B561739E9230093BBB8 /* RenderTiny_GL_Device.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 37973B541739E9230093BBB8 /* RenderTiny_GL_Device.cpp */; };
+ 37973B591739E9E80093BBB8 /* OSX_OculusRoomTiny.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37973B581739E9E80093BBB8 /* OSX_OculusRoomTiny.mm */; };
+ 37973B5C1739FA620093BBB8 /* libovr.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 49A5336D16E544BE0039CB59 /* libovr.a */; };
+ 37973B5D1739FA920093BBB8 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 499ED4C516E6A179008EA2ED /* IOKit.framework */; };
+ 37973B5E1739FAB60093BBB8 /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4945071616E546F700B9FF78 /* OpenGL.framework */; };
+ 37973B5F1739FAC80093BBB8 /* ApplicationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 49DB65F01718B1E10097A8DD /* ApplicationServices.framework */; };
+ 37973B601739FAD50093BBB8 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 499ED4C716E6A187008EA2ED /* CoreFoundation.framework */; };
+ 494506DD16E5461F00B9FF78 /* OVR_Alg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5337116E544E30039CB59 /* OVR_Alg.cpp */; };
+ 494506DF16E5461F00B9FF78 /* OVR_Allocator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5337316E544E30039CB59 /* OVR_Allocator.cpp */; };
+ 494506E216E5461F00B9FF78 /* OVR_Atomic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5337616E544E30039CB59 /* OVR_Atomic.cpp */; };
+ 494506E616E5461F00B9FF78 /* OVR_File.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5337A16E544E30039CB59 /* OVR_File.cpp */; };
+ 494506E816E5461F00B9FF78 /* OVR_FileFILE.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5337C16E544E30039CB59 /* OVR_FileFILE.cpp */; };
+ 494506EC16E5461F00B9FF78 /* OVR_Log.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5338016E544E30039CB59 /* OVR_Log.cpp */; };
+ 494506EE16E5461F00B9FF78 /* OVR_Math.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5338216E544E30039CB59 /* OVR_Math.cpp */; };
+ 494506F016E5461F00B9FF78 /* OVR_RefCount.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5338416E544E30039CB59 /* OVR_RefCount.cpp */; };
+ 494506F216E5461F00B9FF78 /* OVR_Std.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5338616E544E30039CB59 /* OVR_Std.cpp */; };
+ 494506F416E5461F00B9FF78 /* OVR_String.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5338816E544E30039CB59 /* OVR_String.cpp */; };
+ 494506F616E5461F00B9FF78 /* OVR_String_FormatUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5338A16E544E30039CB59 /* OVR_String_FormatUtil.cpp */; };
+ 494506F716E5461F00B9FF78 /* OVR_String_PathUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5338B16E544E30039CB59 /* OVR_String_PathUtil.cpp */; };
+ 494506F916E5461F00B9FF78 /* OVR_SysFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5338D16E544E30039CB59 /* OVR_SysFile.cpp */; };
+ 494506FB16E5461F00B9FF78 /* OVR_System.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5338F16E544E30039CB59 /* OVR_System.cpp */; };
+ 494506FE16E5461F00B9FF78 /* OVR_ThreadsPthread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5339216E544E30039CB59 /* OVR_ThreadsPthread.cpp */; };
+ 494506FF16E5461F00B9FF78 /* OVR_Timer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5339316E544E30039CB59 /* OVR_Timer.cpp */; };
+ 4945070216E5461F00B9FF78 /* OVR_UTF8Util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5339616E544E30039CB59 /* OVR_UTF8Util.cpp */; };
+ 4945070616E5462A00B9FF78 /* OVR_DeviceHandle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5339B16E544E30039CB59 /* OVR_DeviceHandle.cpp */; };
+ 4945070816E5462A00B9FF78 /* OVR_DeviceImpl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5339D16E544E30039CB59 /* OVR_DeviceImpl.cpp */; };
+ 4945070F16E5462A00B9FF78 /* OVR_SensorFusion.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A533A416E544E30039CB59 /* OVR_SensorFusion.cpp */; };
+ 4945071116E5462A00B9FF78 /* OVR_ThreadCommandQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A533A616E544E30039CB59 /* OVR_ThreadCommandQueue.cpp */; };
+ 4985385E16ECFE92008D0727 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4945072216E55A0300B9FF78 /* Cocoa.framework */; };
+ 4985387416ECFED8008D0727 /* Render_Device.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5335016E527820039CB59 /* Render_Device.cpp */; };
+ 4985387816ECFED8008D0727 /* Render_GL_Device.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5335416E527820039CB59 /* Render_GL_Device.cpp */; };
+ 4985387A16ECFED8008D0727 /* Render_LoadTextureTGA.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5335816E527820039CB59 /* Render_LoadTextureTGA.cpp */; };
+ 4985388016ECFEE5008D0727 /* Platform.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5334216E527820039CB59 /* Platform.cpp */; };
+ 4985388616ECFF2D008D0727 /* OculusWorldDemo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4985388116ECFF23008D0727 /* OculusWorldDemo.cpp */; };
+ 4985388716ECFF2D008D0727 /* Player.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4985388216ECFF23008D0727 /* Player.cpp */; };
+ 4985388A16ECFF53008D0727 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 499ED4C716E6A187008EA2ED /* CoreFoundation.framework */; };
+ 4985388C16ECFF53008D0727 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 499ED4C516E6A179008EA2ED /* IOKit.framework */; };
+ 4985388D16ECFF53008D0727 /* IOSurface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 499ED4C916E9880D008EA2ED /* IOSurface.framework */; };
+ 4985388E16ECFF53008D0727 /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4945071616E546F700B9FF78 /* OpenGL.framework */; };
+ 4985389216ED0204008D0727 /* tinyxml2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4985389116ED0204008D0727 /* tinyxml2.cpp */; };
+ 4985389516ED0218008D0727 /* Render_LoadTextureDDS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4985389316ED0218008D0727 /* Render_LoadTextureDDS.cpp */; };
+ 4985389716ED1AA7008D0727 /* Assets in Resources */ = {isa = PBXBuildFile; fileRef = 4985389616ED1AA7008D0727 /* Assets */; };
+ 49ABA2E91718A38100E288A7 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 49ABA2E81718A38100E288A7 /* AudioToolbox.framework */; };
+ 49DB65F11718B1E10097A8DD /* ApplicationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 49DB65F01718B1E10097A8DD /* ApplicationServices.framework */; };
+ 49DB65F71726F0C30097A8DD /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4945072216E55A0300B9FF78 /* Cocoa.framework */; };
+ 49DB660F1726F0EA0097A8DD /* OSX_Platform.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9BBB8902171E2BE200563901 /* OSX_Platform.mm */; };
+ 49DB66101726F0EA0097A8DD /* Platform.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5334216E527820039CB59 /* Platform.cpp */; };
+ 49DB66111726F0F80097A8DD /* Render_LoadTextureDDS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4985389316ED0218008D0727 /* Render_LoadTextureDDS.cpp */; };
+ 49DB66121726F0F80097A8DD /* Render_Device.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5335016E527820039CB59 /* Render_Device.cpp */; };
+ 49DB66131726F0F80097A8DD /* Render_GL_Device.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5335416E527820039CB59 /* Render_GL_Device.cpp */; };
+ 49DB66141726F0F80097A8DD /* Render_LoadTextureTGA.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5335816E527820039CB59 /* Render_LoadTextureTGA.cpp */; };
+ 49DB66171726F1350097A8DD /* ApplicationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 49DB65F01718B1E10097A8DD /* ApplicationServices.framework */; };
+ 49DB66181726F13B0097A8DD /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 499ED4C516E6A179008EA2ED /* IOKit.framework */; };
+ 49DB66191726F1530097A8DD /* libovr.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 49A5336D16E544BE0039CB59 /* libovr.a */; };
+ 49DB661A1726F1650097A8DD /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4945071616E546F700B9FF78 /* OpenGL.framework */; };
+ 9B6DEF851728A6560071E76B /* SensorBoxTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9B6DEF841728A6560071E76B /* SensorBoxTest.cpp */; };
+ 9BA4DDB61721E12400CF7715 /* OSX_Platform.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9BBB8902171E2BE200563901 /* OSX_Platform.mm */; };
+ 9BA4DDB81727061100CF7715 /* Oculus.icns in Resources */ = {isa = PBXBuildFile; fileRef = 9BA4DDB71727061100CF7715 /* Oculus.icns */; };
+ 9BA4DDB91727083500CF7715 /* Oculus.icns in Resources */ = {isa = PBXBuildFile; fileRef = 9BA4DDB71727061100CF7715 /* Oculus.icns */; };
+ 9BAB70F6170E69530006FE98 /* libovr.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 49A5336D16E544BE0039CB59 /* libovr.a */; };
+ 9BB10AF1173C4918009ED618 /* OSX_Gamepad.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9BB10AEF173C4918009ED618 /* OSX_Gamepad.cpp */; };
+ 9BB86B6D1747178B0071C537 /* OSX_Gamepad.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9BB10AEF173C4918009ED618 /* OSX_Gamepad.cpp */; };
+ 9BCE53F916F0293A007A23FF /* OVR_HIDDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = 9BCE53F416F0293A007A23FF /* OVR_HIDDevice.h */; };
+ 9BCE53FA16F0293A007A23FF /* OVR_HIDDeviceBase.h in Headers */ = {isa = PBXBuildFile; fileRef = 9BCE53F516F0293A007A23FF /* OVR_HIDDeviceBase.h */; };
+ 9BCE53FB16F0293A007A23FF /* OVR_HIDDeviceImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 9BCE53F616F0293A007A23FF /* OVR_HIDDeviceImpl.h */; };
+ 9BCE53FC16F0293A007A23FF /* OVR_LatencyTestImpl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9BCE53F716F0293A007A23FF /* OVR_LatencyTestImpl.cpp */; };
+ 9BCE53FD16F0293A007A23FF /* OVR_LatencyTestImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 9BCE53F816F0293A007A23FF /* OVR_LatencyTestImpl.h */; };
+ 9BCE540016F02A56007A23FF /* OVR_SensorImpl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9BCE53FE16F02A56007A23FF /* OVR_SensorImpl.cpp */; };
+ 9BCE540116F02A56007A23FF /* OVR_SensorImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 9BCE53FF16F02A56007A23FF /* OVR_SensorImpl.h */; };
+ 9BCE541616F02ABC007A23FF /* OVR_OSX_DeviceManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9BCE53EB16F028A9007A23FF /* OVR_OSX_DeviceManager.cpp */; };
+ 9BCE541716F02AD5007A23FF /* OVR_OSX_HIDDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9BCE53ED16F028AA007A23FF /* OVR_OSX_HIDDevice.cpp */; };
+ 9BCE541816F02ADB007A23FF /* OVR_OSX_DeviceManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 9BCE53EC16F028AA007A23FF /* OVR_OSX_DeviceManager.h */; };
+ 9BCE541916F02AE6007A23FF /* OVR_OSX_HIDDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = 9BCE53EE16F028AA007A23FF /* OVR_OSX_HIDDevice.h */; };
+ 9BCE541A16F02AEB007A23FF /* OVR_OSX_HMDDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9BCE53EF16F028AA007A23FF /* OVR_OSX_HMDDevice.cpp */; };
+ 9BCE541B16F02AF1007A23FF /* OVR_OSX_HMDDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = 9BCE53F016F028AA007A23FF /* OVR_OSX_HMDDevice.h */; };
+ 9BCE541C16F02B04007A23FF /* OVR_Device.h in Headers */ = {isa = PBXBuildFile; fileRef = 499ED4B216E5703B008EA2ED /* OVR_Device.h */; };
+ 9BCE541D16F02B13007A23FF /* OVR_DeviceConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = 49A5339A16E544E30039CB59 /* OVR_DeviceConstants.h */; };
+ 9BCE541E16F02B1D007A23FF /* OVR_DeviceHandle.h in Headers */ = {isa = PBXBuildFile; fileRef = 49A5339C16E544E30039CB59 /* OVR_DeviceHandle.h */; };
+ 9BCE541F16F02B26007A23FF /* OVR_DeviceImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 49A5339E16E544E30039CB59 /* OVR_DeviceImpl.h */; };
+ 9BCE542016F02B2A007A23FF /* OVR_DeviceMessages.h in Headers */ = {isa = PBXBuildFile; fileRef = 49A5339F16E544E30039CB59 /* OVR_DeviceMessages.h */; };
+ 9BCE542216F02B88007A23FF /* OVR_SensorFusion.h in Headers */ = {isa = PBXBuildFile; fileRef = 49A533A516E544E30039CB59 /* OVR_SensorFusion.h */; };
+ 9BCE542316F02B91007A23FF /* OVR_ThreadCommandQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 49A533A716E544E30039CB59 /* OVR_ThreadCommandQueue.h */; };
+ 9BCE542516F2694F007A23FF /* OVR_OSX_SensorDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9BCE542416F2694E007A23FF /* OVR_OSX_SensorDevice.cpp */; };
+ 9BD2A642172069B300C3C389 /* Util_MagCalibration.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9BD2A640172069B300C3C389 /* Util_MagCalibration.cpp */; };
+ 9BD2A643172069B300C3C389 /* Util_MagCalibration.h in Headers */ = {isa = PBXBuildFile; fileRef = 9BD2A641172069B300C3C389 /* Util_MagCalibration.h */; };
+ 9BD2A646172069BF00C3C389 /* OVR_SensorFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9BD2A644172069BF00C3C389 /* OVR_SensorFilter.cpp */; };
+ 9BD2A647172069BF00C3C389 /* OVR_SensorFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 9BD2A645172069BF00C3C389 /* OVR_SensorFilter.h */; };
+ 9BEAD55F17187B8A00A8AA1D /* Util_LatencyTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9BEAD55B17187B8A00A8AA1D /* Util_LatencyTest.cpp */; };
+ 9BEAD56017187B8A00A8AA1D /* Util_LatencyTest.h in Headers */ = {isa = PBXBuildFile; fileRef = 9BEAD55C17187B8A00A8AA1D /* Util_LatencyTest.h */; };
+ 9BEAD56117187B8A00A8AA1D /* Util_Render_Stereo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9BEAD55D17187B8A00A8AA1D /* Util_Render_Stereo.cpp */; };
+ 9BEAD56217187B8A00A8AA1D /* Util_Render_Stereo.h in Headers */ = {isa = PBXBuildFile; fileRef = 9BEAD55E17187B8A00A8AA1D /* Util_Render_Stereo.h */; };
+ 9BEAD56517187CFF00A8AA1D /* OSX_WavPlayer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9BEAD56317187CFF00A8AA1D /* OSX_WavPlayer.cpp */; };
+ 9BEAD56717187E7500A8AA1D /* Render_XmlSceneLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9BEAD56617187E7500A8AA1D /* Render_XmlSceneLoader.cpp */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ 37973B5A1739FA100093BBB8 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 49A5332B16E527490039CB59 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 49A5336C16E544BE0039CB59;
+ remoteInfo = ovr;
+ };
+ 4985388F16ECFF5C008D0727 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 49A5332B16E527490039CB59 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 49A5336C16E544BE0039CB59;
+ remoteInfo = ovr;
+ };
+ 49DB660D1726F0CD0097A8DD /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 49A5332B16E527490039CB59 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 49A5336C16E544BE0039CB59;
+ remoteInfo = ovr;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXFileReference section */
+ 37973B381739D78B0093BBB8 /* OculusRoomTiny.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = OculusRoomTiny.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 37973B4F1739E1B60093BBB8 /* OculusRoomModel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OculusRoomModel.cpp; sourceTree = "<group>"; };
+ 37973B511739E1D20093BBB8 /* RenderTiny_Device.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RenderTiny_Device.cpp; sourceTree = "<group>"; };
+ 37973B521739E1D20093BBB8 /* RenderTiny_Device.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RenderTiny_Device.h; sourceTree = "<group>"; };
+ 37973B541739E9230093BBB8 /* RenderTiny_GL_Device.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RenderTiny_GL_Device.cpp; sourceTree = "<group>"; };
+ 37973B551739E9230093BBB8 /* RenderTiny_GL_Device.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RenderTiny_GL_Device.h; sourceTree = "<group>"; };
+ 37973B571739E9E80093BBB8 /* OSX_OculusRoomTiny.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OSX_OculusRoomTiny.h; sourceTree = "<group>"; };
+ 37973B581739E9E80093BBB8 /* OSX_OculusRoomTiny.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = OSX_OculusRoomTiny.mm; sourceTree = "<group>"; };
+ 4945071616E546F700B9FF78 /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = System/Library/Frameworks/OpenGL.framework; sourceTree = SDKROOT; };
+ 4945071816E5474000B9FF78 /* libGL.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libGL.dylib; path = ../../../../../../../opt/X11/lib/libGL.dylib; sourceTree = "<group>"; };
+ 4945071A16E5474A00B9FF78 /* libX11.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libX11.dylib; path = ../../../../../../../opt/X11/lib/libX11.dylib; sourceTree = "<group>"; };
+ 4945072216E55A0300B9FF78 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
+ 4945072516E55A0300B9FF78 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
+ 4945072616E55A0300B9FF78 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; };
+ 4945072716E55A0300B9FF78 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
+ 4985385D16ECFE92008D0727 /* OculusWorldDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = OculusWorldDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 4985388116ECFF23008D0727 /* OculusWorldDemo.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = OculusWorldDemo.cpp; path = OculusWorldDemo/OculusWorldDemo.cpp; sourceTree = "<group>"; };
+ 4985388216ECFF23008D0727 /* Player.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Player.cpp; path = OculusWorldDemo/Player.cpp; sourceTree = "<group>"; };
+ 4985388316ECFF23008D0727 /* Player.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Player.h; path = OculusWorldDemo/Player.h; sourceTree = "<group>"; };
+ 4985389116ED0204008D0727 /* tinyxml2.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = tinyxml2.cpp; path = ../3rdParty/TinyXml/tinyxml2.cpp; sourceTree = "<group>"; };
+ 4985389316ED0218008D0727 /* Render_LoadTextureDDS.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Render_LoadTextureDDS.cpp; sourceTree = "<group>"; };
+ 4985389616ED1AA7008D0727 /* Assets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = Assets; path = OculusWorldDemo/Assets; sourceTree = "<group>"; };
+ 499ED49B16E57027008EA2ED /* OVR_Alg.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OVR_Alg.h; sourceTree = "<group>"; };
+ 499ED49C16E57027008EA2ED /* OVR_Allocator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OVR_Allocator.h; sourceTree = "<group>"; };
+ 499ED49D16E57027008EA2ED /* OVR_Array.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OVR_Array.h; sourceTree = "<group>"; };
+ 499ED49E16E57027008EA2ED /* OVR_Atomic.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OVR_Atomic.h; sourceTree = "<group>"; };
+ 499ED49F16E57027008EA2ED /* OVR_Color.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OVR_Color.h; sourceTree = "<group>"; };
+ 499ED4A016E57027008EA2ED /* OVR_ContainerAllocator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OVR_ContainerAllocator.h; sourceTree = "<group>"; };
+ 499ED4A116E57027008EA2ED /* OVR_File.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OVR_File.h; sourceTree = "<group>"; };
+ 499ED4A216E57027008EA2ED /* OVR_Hash.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OVR_Hash.h; sourceTree = "<group>"; };
+ 499ED4A316E57027008EA2ED /* OVR_KeyCodes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OVR_KeyCodes.h; sourceTree = "<group>"; };
+ 499ED4A416E57027008EA2ED /* OVR_List.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OVR_List.h; sourceTree = "<group>"; };
+ 499ED4A516E57027008EA2ED /* OVR_Log.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OVR_Log.h; sourceTree = "<group>"; };
+ 499ED4A616E57027008EA2ED /* OVR_Math.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OVR_Math.h; sourceTree = "<group>"; };
+ 499ED4A716E57027008EA2ED /* OVR_RefCount.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OVR_RefCount.h; sourceTree = "<group>"; };
+ 499ED4A816E57027008EA2ED /* OVR_Std.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OVR_Std.h; sourceTree = "<group>"; };
+ 499ED4A916E57027008EA2ED /* OVR_String.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OVR_String.h; sourceTree = "<group>"; };
+ 499ED4AA16E57027008EA2ED /* OVR_StringHash.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OVR_StringHash.h; sourceTree = "<group>"; };
+ 499ED4AB16E57027008EA2ED /* OVR_SysFile.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OVR_SysFile.h; sourceTree = "<group>"; };
+ 499ED4AC16E57027008EA2ED /* OVR_System.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OVR_System.h; sourceTree = "<group>"; };
+ 499ED4AD16E57027008EA2ED /* OVR_Threads.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OVR_Threads.h; sourceTree = "<group>"; };
+ 499ED4AE16E57027008EA2ED /* OVR_ThreadsWinAPI.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_ThreadsWinAPI.cpp; sourceTree = "<group>"; };
+ 499ED4AF16E57027008EA2ED /* OVR_Timer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OVR_Timer.h; sourceTree = "<group>"; };
+ 499ED4B016E57027008EA2ED /* OVR_Types.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OVR_Types.h; sourceTree = "<group>"; };
+ 499ED4B116E57027008EA2ED /* OVR_UTF8Util.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OVR_UTF8Util.h; sourceTree = "<group>"; };
+ 499ED4B216E5703B008EA2ED /* OVR_Device.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OVR_Device.h; sourceTree = "<group>"; };
+ 499ED4C516E6A179008EA2ED /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = System/Library/Frameworks/IOKit.framework; sourceTree = SDKROOT; };
+ 499ED4C716E6A187008EA2ED /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; };
+ 499ED4C916E9880D008EA2ED /* IOSurface.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOSurface.framework; path = System/Library/Frameworks/IOSurface.framework; sourceTree = SDKROOT; };
+ 49A5334216E527820039CB59 /* Platform.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Platform.cpp; sourceTree = "<group>"; };
+ 49A5334316E527820039CB59 /* Platform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Platform.h; sourceTree = "<group>"; };
+ 49A5334416E527820039CB59 /* Platform_Default.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Platform_Default.h; sourceTree = "<group>"; };
+ 49A5335016E527820039CB59 /* Render_Device.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Render_Device.cpp; sourceTree = "<group>"; };
+ 49A5335116E527820039CB59 /* Render_Device.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Render_Device.h; sourceTree = "<group>"; };
+ 49A5335216E527820039CB59 /* Render_Font.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Render_Font.h; sourceTree = "<group>"; };
+ 49A5335316E527820039CB59 /* Render_FontEmbed_DejaVu48.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Render_FontEmbed_DejaVu48.h; sourceTree = "<group>"; };
+ 49A5335416E527820039CB59 /* Render_GL_Device.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Render_GL_Device.cpp; sourceTree = "<group>"; };
+ 49A5335516E527820039CB59 /* Render_GL_Device.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Render_GL_Device.h; sourceTree = "<group>"; };
+ 49A5335816E527820039CB59 /* Render_LoadTextureTGA.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Render_LoadTextureTGA.cpp; sourceTree = "<group>"; };
+ 49A5336D16E544BE0039CB59 /* libovr.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libovr.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ 49A5337116E544E30039CB59 /* OVR_Alg.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_Alg.cpp; sourceTree = "<group>"; };
+ 49A5337316E544E30039CB59 /* OVR_Allocator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_Allocator.cpp; sourceTree = "<group>"; };
+ 49A5337616E544E30039CB59 /* OVR_Atomic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_Atomic.cpp; sourceTree = "<group>"; };
+ 49A5337A16E544E30039CB59 /* OVR_File.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_File.cpp; sourceTree = "<group>"; };
+ 49A5337C16E544E30039CB59 /* OVR_FileFILE.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_FileFILE.cpp; sourceTree = "<group>"; };
+ 49A5338016E544E30039CB59 /* OVR_Log.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_Log.cpp; sourceTree = "<group>"; };
+ 49A5338216E544E30039CB59 /* OVR_Math.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_Math.cpp; sourceTree = "<group>"; };
+ 49A5338416E544E30039CB59 /* OVR_RefCount.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_RefCount.cpp; sourceTree = "<group>"; };
+ 49A5338616E544E30039CB59 /* OVR_Std.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_Std.cpp; sourceTree = "<group>"; };
+ 49A5338816E544E30039CB59 /* OVR_String.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_String.cpp; sourceTree = "<group>"; };
+ 49A5338A16E544E30039CB59 /* OVR_String_FormatUtil.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_String_FormatUtil.cpp; sourceTree = "<group>"; };
+ 49A5338B16E544E30039CB59 /* OVR_String_PathUtil.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_String_PathUtil.cpp; sourceTree = "<group>"; };
+ 49A5338D16E544E30039CB59 /* OVR_SysFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_SysFile.cpp; sourceTree = "<group>"; };
+ 49A5338F16E544E30039CB59 /* OVR_System.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_System.cpp; sourceTree = "<group>"; };
+ 49A5339216E544E30039CB59 /* OVR_ThreadsPthread.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_ThreadsPthread.cpp; sourceTree = "<group>"; };
+ 49A5339316E544E30039CB59 /* OVR_Timer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_Timer.cpp; sourceTree = "<group>"; };
+ 49A5339616E544E30039CB59 /* OVR_UTF8Util.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_UTF8Util.cpp; sourceTree = "<group>"; };
+ 49A5339A16E544E30039CB59 /* OVR_DeviceConstants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_DeviceConstants.h; sourceTree = "<group>"; };
+ 49A5339B16E544E30039CB59 /* OVR_DeviceHandle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_DeviceHandle.cpp; sourceTree = "<group>"; };
+ 49A5339C16E544E30039CB59 /* OVR_DeviceHandle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_DeviceHandle.h; sourceTree = "<group>"; };
+ 49A5339D16E544E30039CB59 /* OVR_DeviceImpl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_DeviceImpl.cpp; sourceTree = "<group>"; };
+ 49A5339E16E544E30039CB59 /* OVR_DeviceImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_DeviceImpl.h; sourceTree = "<group>"; };
+ 49A5339F16E544E30039CB59 /* OVR_DeviceMessages.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_DeviceMessages.h; sourceTree = "<group>"; };
+ 49A533A416E544E30039CB59 /* OVR_SensorFusion.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_SensorFusion.cpp; sourceTree = "<group>"; };
+ 49A533A516E544E30039CB59 /* OVR_SensorFusion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_SensorFusion.h; sourceTree = "<group>"; };
+ 49A533A616E544E30039CB59 /* OVR_ThreadCommandQueue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_ThreadCommandQueue.cpp; sourceTree = "<group>"; };
+ 49A533A716E544E30039CB59 /* OVR_ThreadCommandQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_ThreadCommandQueue.h; sourceTree = "<group>"; };
+ 49ABA2E81718A38100E288A7 /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = ../../../../../../../System/Library/Frameworks/AudioToolbox.framework; sourceTree = "<group>"; };
+ 49DB65F01718B1E10097A8DD /* ApplicationServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ApplicationServices.framework; path = System/Library/Frameworks/ApplicationServices.framework; sourceTree = SDKROOT; };
+ 49DB65F61726F0C30097A8DD /* SensorBoxTest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SensorBoxTest.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 9B22CBB517187F5C0046D43D /* tinyxml2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = tinyxml2.h; path = ../3rdParty/TinyXml/tinyxml2.h; sourceTree = "<group>"; };
+ 9B6DEF841728A6560071E76B /* SensorBoxTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SensorBoxTest.cpp; path = SensorBox/SensorBoxTest.cpp; sourceTree = "<group>"; };
+ 9BA4DDB71727061100CF7715 /* Oculus.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = Oculus.icns; sourceTree = "<group>"; };
+ 9BB10AEF173C4918009ED618 /* OSX_Gamepad.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OSX_Gamepad.cpp; sourceTree = "<group>"; };
+ 9BB10AF0173C4918009ED618 /* OSX_Gamepad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OSX_Gamepad.h; sourceTree = "<group>"; };
+ 9BB10AF2173C49AC009ED618 /* Gamepad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Gamepad.h; sourceTree = "<group>"; };
+ 9BBB8901171E2BE200563901 /* OSX_Platform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OSX_Platform.h; sourceTree = "<group>"; };
+ 9BBB8902171E2BE200563901 /* OSX_Platform.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = OSX_Platform.mm; sourceTree = "<group>"; };
+ 9BBB8903171E2BE200563901 /* OSX_PlatformObjc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OSX_PlatformObjc.h; sourceTree = "<group>"; };
+ 9BCE53EB16F028A9007A23FF /* OVR_OSX_DeviceManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_OSX_DeviceManager.cpp; sourceTree = "<group>"; };
+ 9BCE53EC16F028AA007A23FF /* OVR_OSX_DeviceManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_OSX_DeviceManager.h; sourceTree = "<group>"; };
+ 9BCE53ED16F028AA007A23FF /* OVR_OSX_HIDDevice.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_OSX_HIDDevice.cpp; sourceTree = "<group>"; };
+ 9BCE53EE16F028AA007A23FF /* OVR_OSX_HIDDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_OSX_HIDDevice.h; sourceTree = "<group>"; };
+ 9BCE53EF16F028AA007A23FF /* OVR_OSX_HMDDevice.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_OSX_HMDDevice.cpp; sourceTree = "<group>"; };
+ 9BCE53F016F028AA007A23FF /* OVR_OSX_HMDDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_OSX_HMDDevice.h; sourceTree = "<group>"; };
+ 9BCE53F416F0293A007A23FF /* OVR_HIDDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_HIDDevice.h; sourceTree = "<group>"; };
+ 9BCE53F516F0293A007A23FF /* OVR_HIDDeviceBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_HIDDeviceBase.h; sourceTree = "<group>"; };
+ 9BCE53F616F0293A007A23FF /* OVR_HIDDeviceImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_HIDDeviceImpl.h; sourceTree = "<group>"; };
+ 9BCE53F716F0293A007A23FF /* OVR_LatencyTestImpl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_LatencyTestImpl.cpp; sourceTree = "<group>"; };
+ 9BCE53F816F0293A007A23FF /* OVR_LatencyTestImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_LatencyTestImpl.h; sourceTree = "<group>"; };
+ 9BCE53FE16F02A56007A23FF /* OVR_SensorImpl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_SensorImpl.cpp; sourceTree = "<group>"; };
+ 9BCE53FF16F02A56007A23FF /* OVR_SensorImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_SensorImpl.h; sourceTree = "<group>"; };
+ 9BCE542416F2694E007A23FF /* OVR_OSX_SensorDevice.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_OSX_SensorDevice.cpp; sourceTree = "<group>"; };
+ 9BD2A640172069B300C3C389 /* Util_MagCalibration.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Util_MagCalibration.cpp; path = Util/Util_MagCalibration.cpp; sourceTree = "<group>"; };
+ 9BD2A641172069B300C3C389 /* Util_MagCalibration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Util_MagCalibration.h; path = Util/Util_MagCalibration.h; sourceTree = "<group>"; };
+ 9BD2A644172069BF00C3C389 /* OVR_SensorFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_SensorFilter.cpp; sourceTree = "<group>"; };
+ 9BD2A645172069BF00C3C389 /* OVR_SensorFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_SensorFilter.h; sourceTree = "<group>"; };
+ 9BEAD55B17187B8A00A8AA1D /* Util_LatencyTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Util_LatencyTest.cpp; path = Util/Util_LatencyTest.cpp; sourceTree = "<group>"; };
+ 9BEAD55C17187B8A00A8AA1D /* Util_LatencyTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Util_LatencyTest.h; path = Util/Util_LatencyTest.h; sourceTree = "<group>"; };
+ 9BEAD55D17187B8A00A8AA1D /* Util_Render_Stereo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Util_Render_Stereo.cpp; path = Util/Util_Render_Stereo.cpp; sourceTree = "<group>"; };
+ 9BEAD55E17187B8A00A8AA1D /* Util_Render_Stereo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Util_Render_Stereo.h; path = Util/Util_Render_Stereo.h; sourceTree = "<group>"; };
+ 9BEAD56317187CFF00A8AA1D /* OSX_WavPlayer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OSX_WavPlayer.cpp; sourceTree = "<group>"; };
+ 9BEAD56417187CFF00A8AA1D /* OSX_WavPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OSX_WavPlayer.h; sourceTree = "<group>"; };
+ 9BEAD56617187E7500A8AA1D /* Render_XmlSceneLoader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Render_XmlSceneLoader.cpp; sourceTree = "<group>"; };
+ 9BEAD56817187E8300A8AA1D /* Render_XmlSceneLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Render_XmlSceneLoader.h; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 37973B351739D78B0093BBB8 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 37973B601739FAD50093BBB8 /* CoreFoundation.framework in Frameworks */,
+ 37973B5F1739FAC80093BBB8 /* ApplicationServices.framework in Frameworks */,
+ 37973B5E1739FAB60093BBB8 /* OpenGL.framework in Frameworks */,
+ 37973B5D1739FA920093BBB8 /* IOKit.framework in Frameworks */,
+ 37973B391739D78B0093BBB8 /* Cocoa.framework in Frameworks */,
+ 37973B5C1739FA620093BBB8 /* libovr.a in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 4985385A16ECFE92008D0727 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 49DB65F11718B1E10097A8DD /* ApplicationServices.framework in Frameworks */,
+ 49ABA2E91718A38100E288A7 /* AudioToolbox.framework in Frameworks */,
+ 4985388A16ECFF53008D0727 /* CoreFoundation.framework in Frameworks */,
+ 4985388C16ECFF53008D0727 /* IOKit.framework in Frameworks */,
+ 4985388D16ECFF53008D0727 /* IOSurface.framework in Frameworks */,
+ 4985388E16ECFF53008D0727 /* OpenGL.framework in Frameworks */,
+ 4985385E16ECFE92008D0727 /* Cocoa.framework in Frameworks */,
+ 9BAB70F6170E69530006FE98 /* libovr.a in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 49A5336A16E544BE0039CB59 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 49DB65F31726F0C30097A8DD /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 49DB661A1726F1650097A8DD /* OpenGL.framework in Frameworks */,
+ 49DB66191726F1530097A8DD /* libovr.a in Frameworks */,
+ 49DB66181726F13B0097A8DD /* IOKit.framework in Frameworks */,
+ 49DB66171726F1350097A8DD /* ApplicationServices.framework in Frameworks */,
+ 49DB65F71726F0C30097A8DD /* Cocoa.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 37973B3A1739D78B0093BBB8 /* OculusRoomTiny */ = {
+ isa = PBXGroup;
+ children = (
+ 37973B571739E9E80093BBB8 /* OSX_OculusRoomTiny.h */,
+ 37973B581739E9E80093BBB8 /* OSX_OculusRoomTiny.mm */,
+ 37973B541739E9230093BBB8 /* RenderTiny_GL_Device.cpp */,
+ 37973B551739E9230093BBB8 /* RenderTiny_GL_Device.h */,
+ 37973B511739E1D20093BBB8 /* RenderTiny_Device.cpp */,
+ 37973B521739E1D20093BBB8 /* RenderTiny_Device.h */,
+ 37973B4F1739E1B60093BBB8 /* OculusRoomModel.cpp */,
+ );
+ path = OculusRoomTiny;
+ sourceTree = "<group>";
+ };
+ 4945072116E55A0300B9FF78 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 4945072216E55A0300B9FF78 /* Cocoa.framework */,
+ 4945072416E55A0300B9FF78 /* Other Frameworks */,
+ );
+ name = Frameworks;
+ sourceTree = "<group>";
+ };
+ 4945072416E55A0300B9FF78 /* Other Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 4945072516E55A0300B9FF78 /* AppKit.framework */,
+ 4945072616E55A0300B9FF78 /* CoreData.framework */,
+ 4945072716E55A0300B9FF78 /* Foundation.framework */,
+ );
+ name = "Other Frameworks";
+ sourceTree = "<group>";
+ };
+ 49A5332A16E527490039CB59 = {
+ isa = PBXGroup;
+ children = (
+ 9BA4DDB71727061100CF7715 /* Oculus.icns */,
+ 49DB65F01718B1E10097A8DD /* ApplicationServices.framework */,
+ 49ABA2E81718A38100E288A7 /* AudioToolbox.framework */,
+ 9B22CBB417187F380046D43D /* 3rdParty */,
+ 9B6DEF831728A60A0071E76B /* SensorBox */,
+ 9B22CBB317187EC70046D43D /* OculusWorldDemo */,
+ 4985389616ED1AA7008D0727 /* Assets */,
+ 37973B3A1739D78B0093BBB8 /* OculusRoomTiny */,
+ 49A5333416E527490039CB59 /* Products */,
+ 49A533A816E544E30039CB59 /* Src */,
+ 49A5334116E527820039CB59 /* Platform */,
+ 49A5334916E527820039CB59 /* Render */,
+ 4945071816E5474000B9FF78 /* libGL.dylib */,
+ 4945071A16E5474A00B9FF78 /* libX11.dylib */,
+ 4945072116E55A0300B9FF78 /* Frameworks */,
+ 499ED4C716E6A187008EA2ED /* CoreFoundation.framework */,
+ 4945071616E546F700B9FF78 /* OpenGL.framework */,
+ 499ED4C516E6A179008EA2ED /* IOKit.framework */,
+ 499ED4C916E9880D008EA2ED /* IOSurface.framework */,
+ );
+ sourceTree = "<group>";
+ };
+ 49A5333416E527490039CB59 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 49A5336D16E544BE0039CB59 /* libovr.a */,
+ 4985385D16ECFE92008D0727 /* OculusWorldDemo.app */,
+ 49DB65F61726F0C30097A8DD /* SensorBoxTest.app */,
+ 37973B381739D78B0093BBB8 /* OculusRoomTiny.app */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 49A5334116E527820039CB59 /* Platform */ = {
+ isa = PBXGroup;
+ children = (
+ 9BB10AF2173C49AC009ED618 /* Gamepad.h */,
+ 9BB10AEF173C4918009ED618 /* OSX_Gamepad.cpp */,
+ 9BB10AF0173C4918009ED618 /* OSX_Gamepad.h */,
+ 9BEAD56317187CFF00A8AA1D /* OSX_WavPlayer.cpp */,
+ 9BEAD56417187CFF00A8AA1D /* OSX_WavPlayer.h */,
+ 9BBB8901171E2BE200563901 /* OSX_Platform.h */,
+ 9BBB8902171E2BE200563901 /* OSX_Platform.mm */,
+ 9BBB8903171E2BE200563901 /* OSX_PlatformObjc.h */,
+ 49A5334216E527820039CB59 /* Platform.cpp */,
+ 49A5334316E527820039CB59 /* Platform.h */,
+ 49A5334416E527820039CB59 /* Platform_Default.h */,
+ );
+ name = Platform;
+ path = CommonSrc/Platform;
+ sourceTree = "<group>";
+ };
+ 49A5334916E527820039CB59 /* Render */ = {
+ isa = PBXGroup;
+ children = (
+ 9BEAD56817187E8300A8AA1D /* Render_XmlSceneLoader.h */,
+ 9BEAD56617187E7500A8AA1D /* Render_XmlSceneLoader.cpp */,
+ 4985389316ED0218008D0727 /* Render_LoadTextureDDS.cpp */,
+ 49A5335016E527820039CB59 /* Render_Device.cpp */,
+ 49A5335116E527820039CB59 /* Render_Device.h */,
+ 49A5335216E527820039CB59 /* Render_Font.h */,
+ 49A5335316E527820039CB59 /* Render_FontEmbed_DejaVu48.h */,
+ 49A5335416E527820039CB59 /* Render_GL_Device.cpp */,
+ 49A5335516E527820039CB59 /* Render_GL_Device.h */,
+ 49A5335816E527820039CB59 /* Render_LoadTextureTGA.cpp */,
+ );
+ name = Render;
+ path = CommonSrc/Render;
+ sourceTree = "<group>";
+ };
+ 49A5339816E544E30039CB59 /* Kernel */ = {
+ isa = PBXGroup;
+ children = (
+ 499ED49B16E57027008EA2ED /* OVR_Alg.h */,
+ 499ED49C16E57027008EA2ED /* OVR_Allocator.h */,
+ 499ED49D16E57027008EA2ED /* OVR_Array.h */,
+ 499ED49E16E57027008EA2ED /* OVR_Atomic.h */,
+ 499ED49F16E57027008EA2ED /* OVR_Color.h */,
+ 499ED4A016E57027008EA2ED /* OVR_ContainerAllocator.h */,
+ 499ED4A116E57027008EA2ED /* OVR_File.h */,
+ 499ED4A216E57027008EA2ED /* OVR_Hash.h */,
+ 499ED4A316E57027008EA2ED /* OVR_KeyCodes.h */,
+ 499ED4A416E57027008EA2ED /* OVR_List.h */,
+ 499ED4A516E57027008EA2ED /* OVR_Log.h */,
+ 499ED4A616E57027008EA2ED /* OVR_Math.h */,
+ 499ED4A716E57027008EA2ED /* OVR_RefCount.h */,
+ 499ED4A816E57027008EA2ED /* OVR_Std.h */,
+ 499ED4A916E57027008EA2ED /* OVR_String.h */,
+ 499ED4AA16E57027008EA2ED /* OVR_StringHash.h */,
+ 499ED4AB16E57027008EA2ED /* OVR_SysFile.h */,
+ 499ED4AC16E57027008EA2ED /* OVR_System.h */,
+ 499ED4AD16E57027008EA2ED /* OVR_Threads.h */,
+ 499ED4AE16E57027008EA2ED /* OVR_ThreadsWinAPI.cpp */,
+ 499ED4AF16E57027008EA2ED /* OVR_Timer.h */,
+ 499ED4B016E57027008EA2ED /* OVR_Types.h */,
+ 499ED4B116E57027008EA2ED /* OVR_UTF8Util.h */,
+ 49A5337116E544E30039CB59 /* OVR_Alg.cpp */,
+ 49A5337316E544E30039CB59 /* OVR_Allocator.cpp */,
+ 49A5337616E544E30039CB59 /* OVR_Atomic.cpp */,
+ 49A5337A16E544E30039CB59 /* OVR_File.cpp */,
+ 49A5337C16E544E30039CB59 /* OVR_FileFILE.cpp */,
+ 49A5338016E544E30039CB59 /* OVR_Log.cpp */,
+ 49A5338216E544E30039CB59 /* OVR_Math.cpp */,
+ 49A5338416E544E30039CB59 /* OVR_RefCount.cpp */,
+ 49A5338616E544E30039CB59 /* OVR_Std.cpp */,
+ 49A5338816E544E30039CB59 /* OVR_String.cpp */,
+ 49A5338A16E544E30039CB59 /* OVR_String_FormatUtil.cpp */,
+ 49A5338B16E544E30039CB59 /* OVR_String_PathUtil.cpp */,
+ 49A5338D16E544E30039CB59 /* OVR_SysFile.cpp */,
+ 49A5338F16E544E30039CB59 /* OVR_System.cpp */,
+ 49A5339216E544E30039CB59 /* OVR_ThreadsPthread.cpp */,
+ 49A5339316E544E30039CB59 /* OVR_Timer.cpp */,
+ 49A5339616E544E30039CB59 /* OVR_UTF8Util.cpp */,
+ );
+ path = Kernel;
+ sourceTree = "<group>";
+ };
+ 49A533A816E544E30039CB59 /* Src */ = {
+ isa = PBXGroup;
+ children = (
+ 9BD2A644172069BF00C3C389 /* OVR_SensorFilter.cpp */,
+ 9BD2A645172069BF00C3C389 /* OVR_SensorFilter.h */,
+ 9BEAD55A17187B5500A8AA1D /* Util */,
+ 49A5339816E544E30039CB59 /* Kernel */,
+ 9BCE542416F2694E007A23FF /* OVR_OSX_SensorDevice.cpp */,
+ 9BCE53EB16F028A9007A23FF /* OVR_OSX_DeviceManager.cpp */,
+ 9BCE53EC16F028AA007A23FF /* OVR_OSX_DeviceManager.h */,
+ 9BCE53ED16F028AA007A23FF /* OVR_OSX_HIDDevice.cpp */,
+ 9BCE53EE16F028AA007A23FF /* OVR_OSX_HIDDevice.h */,
+ 9BCE53EF16F028AA007A23FF /* OVR_OSX_HMDDevice.cpp */,
+ 9BCE53F016F028AA007A23FF /* OVR_OSX_HMDDevice.h */,
+ 499ED4B216E5703B008EA2ED /* OVR_Device.h */,
+ 49A5339A16E544E30039CB59 /* OVR_DeviceConstants.h */,
+ 49A5339B16E544E30039CB59 /* OVR_DeviceHandle.cpp */,
+ 49A5339C16E544E30039CB59 /* OVR_DeviceHandle.h */,
+ 49A5339D16E544E30039CB59 /* OVR_DeviceImpl.cpp */,
+ 49A5339E16E544E30039CB59 /* OVR_DeviceImpl.h */,
+ 49A5339F16E544E30039CB59 /* OVR_DeviceMessages.h */,
+ 9BCE53FE16F02A56007A23FF /* OVR_SensorImpl.cpp */,
+ 9BCE53FF16F02A56007A23FF /* OVR_SensorImpl.h */,
+ 9BCE53F416F0293A007A23FF /* OVR_HIDDevice.h */,
+ 9BCE53F516F0293A007A23FF /* OVR_HIDDeviceBase.h */,
+ 9BCE53F616F0293A007A23FF /* OVR_HIDDeviceImpl.h */,
+ 9BCE53F716F0293A007A23FF /* OVR_LatencyTestImpl.cpp */,
+ 9BCE53F816F0293A007A23FF /* OVR_LatencyTestImpl.h */,
+ 49A533A416E544E30039CB59 /* OVR_SensorFusion.cpp */,
+ 49A533A516E544E30039CB59 /* OVR_SensorFusion.h */,
+ 49A533A616E544E30039CB59 /* OVR_ThreadCommandQueue.cpp */,
+ 49A533A716E544E30039CB59 /* OVR_ThreadCommandQueue.h */,
+ );
+ name = Src;
+ path = ../LibOVR/Src;
+ sourceTree = "<group>";
+ };
+ 9B22CBB317187EC70046D43D /* OculusWorldDemo */ = {
+ isa = PBXGroup;
+ children = (
+ 4985388216ECFF23008D0727 /* Player.cpp */,
+ 4985388316ECFF23008D0727 /* Player.h */,
+ 4985388116ECFF23008D0727 /* OculusWorldDemo.cpp */,
+ );
+ name = OculusWorldDemo;
+ sourceTree = "<group>";
+ };
+ 9B22CBB417187F380046D43D /* 3rdParty */ = {
+ isa = PBXGroup;
+ children = (
+ 9B22CBB517187F5C0046D43D /* tinyxml2.h */,
+ 4985389116ED0204008D0727 /* tinyxml2.cpp */,
+ );
+ name = 3rdParty;
+ sourceTree = "<group>";
+ };
+ 9B6DEF831728A60A0071E76B /* SensorBox */ = {
+ isa = PBXGroup;
+ children = (
+ 9B6DEF841728A6560071E76B /* SensorBoxTest.cpp */,
+ );
+ name = SensorBox;
+ sourceTree = "<group>";
+ };
+ 9BEAD55A17187B5500A8AA1D /* Util */ = {
+ isa = PBXGroup;
+ children = (
+ 9BD2A640172069B300C3C389 /* Util_MagCalibration.cpp */,
+ 9BD2A641172069B300C3C389 /* Util_MagCalibration.h */,
+ 9BEAD55B17187B8A00A8AA1D /* Util_LatencyTest.cpp */,
+ 9BEAD55C17187B8A00A8AA1D /* Util_LatencyTest.h */,
+ 9BEAD55D17187B8A00A8AA1D /* Util_Render_Stereo.cpp */,
+ 9BEAD55E17187B8A00A8AA1D /* Util_Render_Stereo.h */,
+ );
+ name = Util;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXHeadersBuildPhase section */
+ 49A5336B16E544BE0039CB59 /* Headers */ = {
+ isa = PBXHeadersBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 9BCE53F916F0293A007A23FF /* OVR_HIDDevice.h in Headers */,
+ 9BCE53FA16F0293A007A23FF /* OVR_HIDDeviceBase.h in Headers */,
+ 9BCE53FB16F0293A007A23FF /* OVR_HIDDeviceImpl.h in Headers */,
+ 9BCE53FD16F0293A007A23FF /* OVR_LatencyTestImpl.h in Headers */,
+ 9BCE540116F02A56007A23FF /* OVR_SensorImpl.h in Headers */,
+ 9BCE541816F02ADB007A23FF /* OVR_OSX_DeviceManager.h in Headers */,
+ 9BCE541916F02AE6007A23FF /* OVR_OSX_HIDDevice.h in Headers */,
+ 9BCE541B16F02AF1007A23FF /* OVR_OSX_HMDDevice.h in Headers */,
+ 9BCE541C16F02B04007A23FF /* OVR_Device.h in Headers */,
+ 9BCE541D16F02B13007A23FF /* OVR_DeviceConstants.h in Headers */,
+ 9BCE541E16F02B1D007A23FF /* OVR_DeviceHandle.h in Headers */,
+ 9BCE541F16F02B26007A23FF /* OVR_DeviceImpl.h in Headers */,
+ 9BCE542016F02B2A007A23FF /* OVR_DeviceMessages.h in Headers */,
+ 9BCE542216F02B88007A23FF /* OVR_SensorFusion.h in Headers */,
+ 9BCE542316F02B91007A23FF /* OVR_ThreadCommandQueue.h in Headers */,
+ 9BEAD56017187B8A00A8AA1D /* Util_LatencyTest.h in Headers */,
+ 9BEAD56217187B8A00A8AA1D /* Util_Render_Stereo.h in Headers */,
+ 9BD2A643172069B300C3C389 /* Util_MagCalibration.h in Headers */,
+ 9BD2A647172069BF00C3C389 /* OVR_SensorFilter.h in Headers */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXHeadersBuildPhase section */
+
+/* Begin PBXNativeTarget section */
+ 37973B371739D78B0093BBB8 /* OculusRoomTiny */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 37973B4E1739D7930093BBB8 /* Build configuration list for PBXNativeTarget "OculusRoomTiny" */;
+ buildPhases = (
+ 37973B341739D78B0093BBB8 /* Sources */,
+ 37973B351739D78B0093BBB8 /* Frameworks */,
+ 37973B361739D78B0093BBB8 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 37973B5B1739FA100093BBB8 /* PBXTargetDependency */,
+ );
+ name = OculusRoomTiny;
+ productName = OculusRoomTiny;
+ productReference = 37973B381739D78B0093BBB8 /* OculusRoomTiny.app */;
+ productType = "com.apple.product-type.application";
+ };
+ 4985385C16ECFE92008D0727 /* OculusWorldDemo */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 4985387116ECFE92008D0727 /* Build configuration list for PBXNativeTarget "OculusWorldDemo" */;
+ buildPhases = (
+ 4985385916ECFE92008D0727 /* Sources */,
+ 4985385A16ECFE92008D0727 /* Frameworks */,
+ 4985385B16ECFE92008D0727 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 4985389016ECFF5C008D0727 /* PBXTargetDependency */,
+ );
+ name = OculusWorldDemo;
+ productName = OculusWorldDemo;
+ productReference = 4985385D16ECFE92008D0727 /* OculusWorldDemo.app */;
+ productType = "com.apple.product-type.application";
+ };
+ 49A5336C16E544BE0039CB59 /* ovr */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 49A5336E16E544BE0039CB59 /* Build configuration list for PBXNativeTarget "ovr" */;
+ buildPhases = (
+ 49A5336916E544BE0039CB59 /* Sources */,
+ 49A5336A16E544BE0039CB59 /* Frameworks */,
+ 49A5336B16E544BE0039CB59 /* Headers */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = ovr;
+ productName = ovr;
+ productReference = 49A5336D16E544BE0039CB59 /* libovr.a */;
+ productType = "com.apple.product-type.library.static";
+ };
+ 49DB65F51726F0C30097A8DD /* SensorBoxTest */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 49DB660C1726F0C30097A8DD /* Build configuration list for PBXNativeTarget "SensorBoxTest" */;
+ buildPhases = (
+ 49DB65F21726F0C30097A8DD /* Sources */,
+ 49DB65F31726F0C30097A8DD /* Frameworks */,
+ 49DB65F41726F0C30097A8DD /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 49DB660E1726F0CD0097A8DD /* PBXTargetDependency */,
+ );
+ name = SensorBoxTest;
+ productName = SensorBoxTest;
+ productReference = 49DB65F61726F0C30097A8DD /* SensorBoxTest.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 49A5332B16E527490039CB59 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 0460;
+ ORGANIZATIONNAME = "Oculus VR";
+ };
+ buildConfigurationList = 49A5332E16E527490039CB59 /* Build configuration list for PBXProject "LibOVR_With_Samples" */;
+ compatibilityVersion = "Xcode 3.2";
+ developmentRegion = English;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ );
+ mainGroup = 49A5332A16E527490039CB59;
+ productRefGroup = 49A5333416E527490039CB59 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 49A5336C16E544BE0039CB59 /* ovr */,
+ 4985385C16ECFE92008D0727 /* OculusWorldDemo */,
+ 49DB65F51726F0C30097A8DD /* SensorBoxTest */,
+ 37973B371739D78B0093BBB8 /* OculusRoomTiny */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 37973B361739D78B0093BBB8 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 4985385B16ECFE92008D0727 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 4985389716ED1AA7008D0727 /* Assets in Resources */,
+ 9BA4DDB81727061100CF7715 /* Oculus.icns in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 49DB65F41726F0C30097A8DD /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 9BA4DDB91727083500CF7715 /* Oculus.icns in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 37973B341739D78B0093BBB8 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 37973B501739E1B60093BBB8 /* OculusRoomModel.cpp in Sources */,
+ 37973B531739E1D20093BBB8 /* RenderTiny_Device.cpp in Sources */,
+ 37973B561739E9230093BBB8 /* RenderTiny_GL_Device.cpp in Sources */,
+ 37973B591739E9E80093BBB8 /* OSX_OculusRoomTiny.mm in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 4985385916ECFE92008D0727 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 4985388616ECFF2D008D0727 /* OculusWorldDemo.cpp in Sources */,
+ 4985388716ECFF2D008D0727 /* Player.cpp in Sources */,
+ 4985388016ECFEE5008D0727 /* Platform.cpp in Sources */,
+ 4985387416ECFED8008D0727 /* Render_Device.cpp in Sources */,
+ 4985387816ECFED8008D0727 /* Render_GL_Device.cpp in Sources */,
+ 4985387A16ECFED8008D0727 /* Render_LoadTextureTGA.cpp in Sources */,
+ 4985389216ED0204008D0727 /* tinyxml2.cpp in Sources */,
+ 4985389516ED0218008D0727 /* Render_LoadTextureDDS.cpp in Sources */,
+ 9BEAD56517187CFF00A8AA1D /* OSX_WavPlayer.cpp in Sources */,
+ 9BEAD56717187E7500A8AA1D /* Render_XmlSceneLoader.cpp in Sources */,
+ 9BA4DDB61721E12400CF7715 /* OSX_Platform.mm in Sources */,
+ 9BB10AF1173C4918009ED618 /* OSX_Gamepad.cpp in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 49A5336916E544BE0039CB59 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 4945070616E5462A00B9FF78 /* OVR_DeviceHandle.cpp in Sources */,
+ 4945070816E5462A00B9FF78 /* OVR_DeviceImpl.cpp in Sources */,
+ 4945070F16E5462A00B9FF78 /* OVR_SensorFusion.cpp in Sources */,
+ 4945071116E5462A00B9FF78 /* OVR_ThreadCommandQueue.cpp in Sources */,
+ 494506DD16E5461F00B9FF78 /* OVR_Alg.cpp in Sources */,
+ 494506DF16E5461F00B9FF78 /* OVR_Allocator.cpp in Sources */,
+ 494506E216E5461F00B9FF78 /* OVR_Atomic.cpp in Sources */,
+ 494506E616E5461F00B9FF78 /* OVR_File.cpp in Sources */,
+ 494506E816E5461F00B9FF78 /* OVR_FileFILE.cpp in Sources */,
+ 494506EC16E5461F00B9FF78 /* OVR_Log.cpp in Sources */,
+ 494506EE16E5461F00B9FF78 /* OVR_Math.cpp in Sources */,
+ 494506F016E5461F00B9FF78 /* OVR_RefCount.cpp in Sources */,
+ 494506F216E5461F00B9FF78 /* OVR_Std.cpp in Sources */,
+ 494506F416E5461F00B9FF78 /* OVR_String.cpp in Sources */,
+ 494506F616E5461F00B9FF78 /* OVR_String_FormatUtil.cpp in Sources */,
+ 494506F716E5461F00B9FF78 /* OVR_String_PathUtil.cpp in Sources */,
+ 494506F916E5461F00B9FF78 /* OVR_SysFile.cpp in Sources */,
+ 494506FB16E5461F00B9FF78 /* OVR_System.cpp in Sources */,
+ 494506FE16E5461F00B9FF78 /* OVR_ThreadsPthread.cpp in Sources */,
+ 494506FF16E5461F00B9FF78 /* OVR_Timer.cpp in Sources */,
+ 4945070216E5461F00B9FF78 /* OVR_UTF8Util.cpp in Sources */,
+ 9BCE53FC16F0293A007A23FF /* OVR_LatencyTestImpl.cpp in Sources */,
+ 9BCE540016F02A56007A23FF /* OVR_SensorImpl.cpp in Sources */,
+ 9BCE541616F02ABC007A23FF /* OVR_OSX_DeviceManager.cpp in Sources */,
+ 9BCE541716F02AD5007A23FF /* OVR_OSX_HIDDevice.cpp in Sources */,
+ 9BCE541A16F02AEB007A23FF /* OVR_OSX_HMDDevice.cpp in Sources */,
+ 9BCE542516F2694F007A23FF /* OVR_OSX_SensorDevice.cpp in Sources */,
+ 9BEAD55F17187B8A00A8AA1D /* Util_LatencyTest.cpp in Sources */,
+ 9BEAD56117187B8A00A8AA1D /* Util_Render_Stereo.cpp in Sources */,
+ 9BD2A642172069B300C3C389 /* Util_MagCalibration.cpp in Sources */,
+ 9BD2A646172069BF00C3C389 /* OVR_SensorFilter.cpp in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 49DB65F21726F0C30097A8DD /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 9BB86B6D1747178B0071C537 /* OSX_Gamepad.cpp in Sources */,
+ 49DB66111726F0F80097A8DD /* Render_LoadTextureDDS.cpp in Sources */,
+ 49DB66121726F0F80097A8DD /* Render_Device.cpp in Sources */,
+ 49DB66131726F0F80097A8DD /* Render_GL_Device.cpp in Sources */,
+ 49DB66141726F0F80097A8DD /* Render_LoadTextureTGA.cpp in Sources */,
+ 49DB660F1726F0EA0097A8DD /* OSX_Platform.mm in Sources */,
+ 49DB66101726F0EA0097A8DD /* Platform.cpp in Sources */,
+ 9B6DEF851728A6560071E76B /* SensorBoxTest.cpp in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ 37973B5B1739FA100093BBB8 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 49A5336C16E544BE0039CB59 /* ovr */;
+ targetProxy = 37973B5A1739FA100093BBB8 /* PBXContainerItemProxy */;
+ };
+ 4985389016ECFF5C008D0727 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 49A5336C16E544BE0039CB59 /* ovr */;
+ targetProxy = 4985388F16ECFF5C008D0727 /* PBXContainerItemProxy */;
+ };
+ 49DB660E1726F0CD0097A8DD /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 49A5336C16E544BE0039CB59 /* ovr */;
+ targetProxy = 49DB660D1726F0CD0097A8DD /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin XCBuildConfiguration section */
+ 37973B4C1739D78C0093BBB8 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_CXX_LIBRARY = "libstdc++";
+ COMBINE_HIDPI_IMAGES = YES;
+ GCC_PRECOMPILE_PREFIX_HEADER = NO;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ HEADER_SEARCH_PATHS = (
+ ../LibOVR/Src,
+ ../LibOVR/Include,
+ );
+ INFOPLIST_FILE = "LibOVR_With_Samples.xcodeproj/OculusRoomTiny-Info.plist";
+ MACOSX_DEPLOYMENT_TARGET = 10.6;
+ ONLY_ACTIVE_ARCH = YES;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SYMROOT = OculusRoomTiny/MacOS/Debug;
+ WRAPPER_EXTENSION = app;
+ };
+ name = Debug;
+ };
+ 37973B4D1739D78C0093BBB8 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_CXX_LIBRARY = "libstdc++";
+ COMBINE_HIDPI_IMAGES = YES;
+ GCC_PRECOMPILE_PREFIX_HEADER = NO;
+ HEADER_SEARCH_PATHS = (
+ ../LibOVR/Src,
+ ../LibOVR/Include,
+ );
+ INFOPLIST_FILE = "LibOVR_With_Samples.xcodeproj/OculusRoomTiny-Info.plist";
+ MACOSX_DEPLOYMENT_TARGET = 10.6;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SYMROOT = OculusRoomTiny/MacOS/Release;
+ WRAPPER_EXTENSION = app;
+ };
+ name = Release;
+ };
+ 4985387216ECFE92008D0727 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ARCHS = "$(ARCHS_STANDARD_64_BIT)";
+ COMBINE_HIDPI_IMAGES = YES;
+ CONFIGURATION_BUILD_DIR = OculusWorldDemo/MacOS/Debug;
+ GCC_PRECOMPILE_PREFIX_HEADER = NO;
+ GCC_PREFIX_HEADER = "";
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ "GCC_PREPROCESSOR_DEFINITIONS[arch=*]" = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ HEADER_SEARCH_PATHS = (
+ ../LibOVR/Src,
+ ../LibOVR/Include,
+ CommonSrc,
+ ../3rdParty/TinyXml,
+ );
+ INFOPLIST_FILE = "LibOVR_With_Samples.xcodeproj/OculusWorldDemo-Info.plist";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SYMROOT = OculusWorldDemo/MacOS/Debug;
+ WRAPPER_EXTENSION = app;
+ };
+ name = Debug;
+ };
+ 4985387316ECFE92008D0727 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ARCHS = "$(ARCHS_STANDARD_64_BIT)";
+ COMBINE_HIDPI_IMAGES = YES;
+ CONFIGURATION_BUILD_DIR = OculusWorldDemo/MacOS/Release;
+ GCC_PRECOMPILE_PREFIX_HEADER = NO;
+ GCC_PREFIX_HEADER = "";
+ HEADER_SEARCH_PATHS = (
+ ../LibOVR/Src,
+ ../LibOVR/Include,
+ CommonSrc,
+ ../3rdParty/TinyXml,
+ );
+ INFOPLIST_FILE = "LibOVR_With_Samples.xcodeproj/OculusWorldDemo-Info.plist";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SYMROOT = OculusWorldDemo/MacOS/Release;
+ WRAPPER_EXTENSION = app;
+ };
+ name = Release;
+ };
+ 49A5333A16E527490039CB59 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ARCHS = "$(ARCHS_STANDARD_64_BIT)";
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libstdc++";
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ CONFIGURATION_BUILD_DIR = ../LibOVR/Lib/MacOS/Debug;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_ENABLE_CPP_EXCEPTIONS = NO;
+ GCC_ENABLE_CPP_RTTI = NO;
+ GCC_ENABLE_OBJC_EXCEPTIONS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ OVR_BUILD_DEBUG,
+ "$(inherited)",
+ );
+ GCC_SYMBOLS_PRIVATE_EXTERN = NO;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ HEADER_SEARCH_PATHS = (
+ ../LibOVR/Src,
+ ../LibOVR/Include,
+ CommonSrc,
+ );
+ LIBRARY_SEARCH_PATHS = "../LibOVR/Lib/MacOS/$(CONFIGURATION)";
+ MACOSX_DEPLOYMENT_TARGET = 10.6;
+ ONLY_ACTIVE_ARCH = NO;
+ SDKROOT = macosx;
+ SUPPORTED_PLATFORMS = macosx;
+ };
+ name = Debug;
+ };
+ 49A5333B16E527490039CB59 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ARCHS = "$(ARCHS_STANDARD_64_BIT)";
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libstdc++";
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ CONFIGURATION_BUILD_DIR = ../LibOVR/Lib/MacOS/Release;
+ COPY_PHASE_STRIP = YES;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_ENABLE_CPP_EXCEPTIONS = NO;
+ GCC_ENABLE_CPP_RTTI = NO;
+ GCC_ENABLE_OBJC_EXCEPTIONS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ HEADER_SEARCH_PATHS = (
+ ../LibOVR/Src,
+ ../LibOVR/Include,
+ CommonSrc,
+ );
+ LIBRARY_SEARCH_PATHS = "../LibOVR/Lib/MacOS/$(CONFIGURATION)";
+ MACOSX_DEPLOYMENT_TARGET = 10.6;
+ ONLY_ACTIVE_ARCH = NO;
+ SDKROOT = macosx;
+ SUPPORTED_PLATFORMS = macosx;
+ };
+ name = Release;
+ };
+ 49A5336F16E544BE0039CB59 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
+ COMBINE_HIDPI_IMAGES = YES;
+ EXECUTABLE_PREFIX = lib;
+ GCC_ENABLE_CPP_EXCEPTIONS = NO;
+ GCC_ENABLE_CPP_RTTI = NO;
+ GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
+ LIBRARY_SEARCH_PATHS = "../LIbOVR/MacOS/$(CONFIGURATION)";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SYMROOT = ../LibOVR/Lib/MacOS;
+ };
+ name = Debug;
+ };
+ 49A5337016E544BE0039CB59 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
+ COMBINE_HIDPI_IMAGES = YES;
+ EXECUTABLE_PREFIX = lib;
+ GCC_ENABLE_CPP_EXCEPTIONS = NO;
+ GCC_ENABLE_CPP_RTTI = NO;
+ LIBRARY_SEARCH_PATHS = "../LIbOVR/MacOS/$(CONFIGURATION)";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SYMROOT = ../LibOVR/Lib/MacOS;
+ };
+ name = Release;
+ };
+ 49DB660A1726F0C30097A8DD /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_CXX_LIBRARY = "libstdc++";
+ COMBINE_HIDPI_IMAGES = YES;
+ CONFIGURATION_BUILD_DIR = Samples/SensorBoxTest/MacOS/Debug;
+ GCC_PRECOMPILE_PREFIX_HEADER = NO;
+ GCC_PREFIX_HEADER = "";
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ INFOPLIST_FILE = "LibOVR_With_Samples.xcodeproj/SensorBoxTest-Info.plist";
+ MACOSX_DEPLOYMENT_TARGET = 10.6;
+ ONLY_ACTIVE_ARCH = NO;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ WRAPPER_EXTENSION = app;
+ };
+ name = Debug;
+ };
+ 49DB660B1726F0C30097A8DD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_CXX_LIBRARY = "libstdc++";
+ COMBINE_HIDPI_IMAGES = YES;
+ CONFIGURATION_BUILD_DIR = Samples/SensorBoxTest/MacOS/Release;
+ GCC_PRECOMPILE_PREFIX_HEADER = NO;
+ GCC_PREFIX_HEADER = "";
+ INFOPLIST_FILE = "LibOVR_With_Samples.xcodeproj/SensorBoxTest-Info.plist";
+ MACOSX_DEPLOYMENT_TARGET = 10.6;
+ ONLY_ACTIVE_ARCH = NO;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ WRAPPER_EXTENSION = app;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 37973B4E1739D7930093BBB8 /* Build configuration list for PBXNativeTarget "OculusRoomTiny" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 37973B4C1739D78C0093BBB8 /* Debug */,
+ 37973B4D1739D78C0093BBB8 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 4985387116ECFE92008D0727 /* Build configuration list for PBXNativeTarget "OculusWorldDemo" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 4985387216ECFE92008D0727 /* Debug */,
+ 4985387316ECFE92008D0727 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 49A5332E16E527490039CB59 /* Build configuration list for PBXProject "LibOVR_With_Samples" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 49A5333A16E527490039CB59 /* Debug */,
+ 49A5333B16E527490039CB59 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 49A5336E16E544BE0039CB59 /* Build configuration list for PBXNativeTarget "ovr" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 49A5336F16E544BE0039CB59 /* Debug */,
+ 49A5337016E544BE0039CB59 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 49DB660C1726F0C30097A8DD /* Build configuration list for PBXNativeTarget "SensorBoxTest" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 49DB660A1726F0C30097A8DD /* Debug */,
+ 49DB660B1726F0C30097A8DD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 49A5332B16E527490039CB59 /* Project object */;
+}
diff --git a/Samples/LibOVR_With_Samples_Msvc2010.sln b/Samples/LibOVR_With_Samples_Msvc2010.sln
new file mode 100644
index 0000000..eb7c50d
--- /dev/null
+++ b/Samples/LibOVR_With_Samples_Msvc2010.sln
@@ -0,0 +1,67 @@
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual Studio 2010
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LibOVR", "..\LibOVR\Projects\Win32\LibOVR_Msvc2010.vcxproj", "{934B40C7-F40A-4E4C-97A7-B9659BE0A441}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OculusWorldDemo", "OculusWorldDemo\OculusWorldDemo_Msvc2010.vcxproj", "{8051B877-2992-4F64-8C3B-FAF88B6D83AA}"
+ ProjectSection(ProjectDependencies) = postProject
+ {934B40C7-F40A-4E4C-97A7-B9659BE0A441} = {934B40C7-F40A-4E4C-97A7-B9659BE0A441}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SensorBoxTest", "SensorBox\SensorBoxTest_Msvc2010.vcxproj", "{8051B837-2982-4F64-8C3B-FAF88B6D83AB}"
+ ProjectSection(ProjectDependencies) = postProject
+ {934B40C7-F40A-4E4C-97A7-B9659BE0A441} = {934B40C7-F40A-4E4C-97A7-B9659BE0A441}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OculusRoomTiny", "OculusRoomTiny\OculusRoomTiny_Msvc2010.vcxproj", "{80523489-2881-4F64-8C3B-FAF88B60ABCD}"
+ ProjectSection(ProjectDependencies) = postProject
+ {934B40C7-F40A-4E4C-97A7-B9659BE0A441} = {934B40C7-F40A-4E4C-97A7-B9659BE0A441}
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Debug|x64 = Debug|x64
+ Release|Win32 = Release|Win32
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {934B40C7-F40A-4E4C-97A7-B9659BE0A441}.Debug|Win32.ActiveCfg = Debug|Win32
+ {934B40C7-F40A-4E4C-97A7-B9659BE0A441}.Debug|Win32.Build.0 = Debug|Win32
+ {934B40C7-F40A-4E4C-97A7-B9659BE0A441}.Debug|x64.ActiveCfg = Debug|x64
+ {934B40C7-F40A-4E4C-97A7-B9659BE0A441}.Debug|x64.Build.0 = Debug|x64
+ {934B40C7-F40A-4E4C-97A7-B9659BE0A441}.Release|Win32.ActiveCfg = Release|Win32
+ {934B40C7-F40A-4E4C-97A7-B9659BE0A441}.Release|Win32.Build.0 = Release|Win32
+ {934B40C7-F40A-4E4C-97A7-B9659BE0A441}.Release|x64.ActiveCfg = Release|x64
+ {934B40C7-F40A-4E4C-97A7-B9659BE0A441}.Release|x64.Build.0 = Release|x64
+ {8051B877-2992-4F64-8C3B-FAF88B6D83AA}.Debug|Win32.ActiveCfg = Debug|Win32
+ {8051B877-2992-4F64-8C3B-FAF88B6D83AA}.Debug|Win32.Build.0 = Debug|Win32
+ {8051B877-2992-4F64-8C3B-FAF88B6D83AA}.Debug|x64.ActiveCfg = Debug|x64
+ {8051B877-2992-4F64-8C3B-FAF88B6D83AA}.Debug|x64.Build.0 = Debug|x64
+ {8051B877-2992-4F64-8C3B-FAF88B6D83AA}.Release|Win32.ActiveCfg = Release|Win32
+ {8051B877-2992-4F64-8C3B-FAF88B6D83AA}.Release|Win32.Build.0 = Release|Win32
+ {8051B877-2992-4F64-8C3B-FAF88B6D83AA}.Release|x64.ActiveCfg = Release|x64
+ {8051B837-2982-4F64-8C3B-FAF88B6D83AB}.Debug|Win32.ActiveCfg = Debug|Win32
+ {8051B837-2982-4F64-8C3B-FAF88B6D83AB}.Debug|Win32.Build.0 = Debug|Win32
+ {8051B837-2982-4F64-8C3B-FAF88B6D83AB}.Debug|x64.ActiveCfg = Debug|Win32
+ {8051B837-2982-4F64-8C3B-FAF88B6D83AB}.Release|Win32.ActiveCfg = Release|Win32
+ {8051B837-2982-4F64-8C3B-FAF88B6D83AB}.Release|Win32.Build.0 = Release|Win32
+ {8051B837-2982-4F64-8C3B-FAF88B6D83AB}.Release|x64.ActiveCfg = Release|Win32
+ {80523489-2881-4F64-8C3B-FAF88B60ABCD}.Debug|Win32.ActiveCfg = Debug|Win32
+ {80523489-2881-4F64-8C3B-FAF88B60ABCD}.Debug|Win32.Build.0 = Debug|Win32
+ {80523489-2881-4F64-8C3B-FAF88B60ABCD}.Debug|x64.ActiveCfg = Debug|x64
+ {80523489-2881-4F64-8C3B-FAF88B60ABCD}.Debug|x64.Build.0 = Debug|x64
+ {80523489-2881-4F64-8C3B-FAF88B60ABCD}.Release|Win32.ActiveCfg = Release|Win32
+ {80523489-2881-4F64-8C3B-FAF88B60ABCD}.Release|Win32.Build.0 = Release|Win32
+ {80523489-2881-4F64-8C3B-FAF88B60ABCD}.Release|x64.ActiveCfg = Release|x64
+ {80523489-2881-4F64-8C3B-FAF88B60ABCD}.Release|x64.Build.0 = Release|x64
+ {9ABD1D70-F80B-466F-B097-821EBAA00ED6}.Debug|Win32.ActiveCfg = Debug|Win32
+ {9ABD1D70-F80B-466F-B097-821EBAA00ED6}.Debug|Win32.Build.0 = Debug|Win32
+ {9ABD1D70-F80B-466F-B097-821EBAA00ED6}.Debug|x64.ActiveCfg = Debug|Win32
+ {9ABD1D70-F80B-466F-B097-821EBAA00ED6}.Release|Win32.ActiveCfg = Release|Win32
+ {9ABD1D70-F80B-466F-B097-821EBAA00ED6}.Release|Win32.Build.0 = Release|Win32
+ {9ABD1D70-F80B-466F-B097-821EBAA00ED6}.Release|x64.ActiveCfg = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/Samples/Oculus.icns b/Samples/Oculus.icns
new file mode 100644
index 0000000..955a8fe
--- /dev/null
+++ b/Samples/Oculus.icns
Binary files differ
diff --git a/Samples/OculusRoomTiny/OSX_OculusRoomTiny.h b/Samples/OculusRoomTiny/OSX_OculusRoomTiny.h
new file mode 100644
index 0000000..dc51509
--- /dev/null
+++ b/Samples/OculusRoomTiny/OSX_OculusRoomTiny.h
@@ -0,0 +1,223 @@
+/************************************************************************************
+
+ Filename : OSX_OculusRoomTiny.h
+ Content : Simplest possible first-person view test application for Oculus Rift
+ Created : May 7, 2013
+ Authors : Michael Antonov, Andrew Reisse, Artem Bolgar
+
+ Copyright : Copyright 2013 Oculus, Inc. All Rights reserved.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ *************************************************************************************/
+#ifndef INC_OSX_OculusRoomTiny_h
+#define INC_OSX_OculusRoomTiny_h
+
+#import <Cocoa/Cocoa.h>
+
+#import <CoreGraphics/CoreGraphics.h>
+#import <CoreGraphics/CGDirectDisplay.h>
+
+
+#include "OVR.h"
+#include "Util/Util_Render_Stereo.h"
+#include "../../LibOVR/Src/Kernel/OVR_Timer.h"
+#include "RenderTiny_GL_Device.h"
+
+using namespace OVR;
+using namespace OVR::RenderTiny;
+
+class OculusRoomTinyApp;
+
+@interface OVRApp : NSApplication
+
+@property (assign) IBOutlet NSWindow* win;
+@property (assign) OculusRoomTinyApp* App;
+
+-(void) run;
+
+@end
+
+@interface OVRView : NSOpenGLView <NSWindowDelegate>
+
+//@property (assign) OVR::Platform::OSX::PlatformCore* Platform;
+@property (assign) OculusRoomTinyApp* App;
+@property unsigned long Modifiers;
+
+-(void)ProcessMouse:(NSEvent*)event;
+-(void)warpMouseToCenter;
+
++(CGDirectDisplayID) displayFromScreen:(NSScreen*)s;
+
+@end
+
+//-------------------------------------------------------------------------------------
+// ***** OculusRoomTiny Description
+
+// This app renders a simple flat-shaded room allowing the user to move along the
+// floor and look around with an HMD, mouse, keyboard and gamepad.
+// By default, the application will start full-screen on Oculus Rift.
+//
+// The following keys work:
+//
+// 'W', 'S', 'A', 'D' - Move forward, back; strafe left/right.
+// F1 - No stereo, no distortion.
+// F2 - Stereo, no distortion.
+// F3 - Stereo and distortion.
+//
+
+// The world RHS coordinate system is defines as follows (as seen in perspective view):
+// Y - Up
+// Z - Back
+// X - Right
+const Vector3f UpVector(0.0f, 1.0f, 0.0f);
+const Vector3f ForwardVector(0.0f, 0.0f, -1.0f);
+const Vector3f RightVector(1.0f, 0.0f, 0.0f);
+
+// We start out looking in the positive Z (180 degree rotation).
+const float YawInitial = 3.141592f;
+const float Sensitivity = 1.0f;
+const float MoveSpeed = 3.0f; // m/s
+
+namespace OSX
+{
+ class RenderDevice : public GL::RenderDevice
+ {
+ public:
+ void* Context; // NSOpenGLContext
+
+ // osview = NSView*
+ RenderDevice(const RendererParams& p, void* osview, void* context);
+
+ virtual void Shutdown();
+ virtual void Present();
+
+ virtual bool SetFullscreen(DisplayMode fullscreen);
+
+ // osview = NSView*
+ static RenderDevice* CreateDevice(const RendererParams& rp, void* osview);
+ };
+}
+
+//-------------------------------------------------------------------------------------
+// ***** OculusRoomTiny Application class
+
+// An instance of this class is created on application startup (main/WinMain).
+//
+// It then works as follows:
+//
+// OnStartup - Window, graphics and HMD setup is done here.
+// This function will initialize OVR::DeviceManager and HMD,
+// creating SensorDevice and attaching it to SensorFusion.
+// This needs to be done before obtaining sensor data.
+//
+// OnIdle - Does per-frame processing, processing SensorFusion and
+// movement input and rendering the frame.
+
+class OculusRoomTinyApp : public MessageHandler
+{
+ friend class OSX::RenderDevice;
+public:
+ OculusRoomTinyApp(OVRApp* nsapp);
+ ~OculusRoomTinyApp();
+
+ // Initializes graphics, Rift input and creates world model.
+ virtual int OnStartup(const char* args);
+ // Called per frame to sample SensorFucion and render the world.
+ virtual void OnIdle();
+
+ // Installed for Oculus device messages. Optional.
+ virtual void OnMessage(const Message& msg);
+
+ // Handle input events for movement.
+ virtual void OnMouseMove(int x, int y, int modifiers);
+ virtual void OnKey(unsigned vk, bool down);
+
+ // Render the view for one eye.
+ void Render(const StereoEyeParams& stereo);
+
+ // Main application loop.
+ int Run();
+ void Exit();
+
+ // Return amount of time passed since application started in seconds.
+ double GetAppTime() const
+ {
+ return (OVR::Timer::GetTicks() - StartupTicks) * (1.0 / (double)OVR::Timer::MksPerSecond);
+ }
+ bool IsQuiting() const { return Quit; }
+
+ int GetWidth() const { return Width; }
+ int GetHeight() const { return Height; }
+
+ bool SetFullscreen(const RendererParams& rp, int fullscreen);
+
+protected:
+ bool setupWindow();
+ void destroyWindow();
+
+ NSView* View;
+ NSWindow* Win;
+ OVRApp* NsApp;
+
+ static OculusRoomTinyApp* pApp;
+
+ // *** Rendering Variables
+ Ptr<OSX::RenderDevice> pRender;
+ RendererParams RenderParams;
+ int Width, Height;
+
+ bool Quit;
+
+ // *** Oculus HMD Variables
+
+ Ptr<DeviceManager> pManager;
+ Ptr<SensorDevice> pSensor;
+ Ptr<HMDDevice> pHMD;
+ SensorFusion SFusion;
+ OVR::HMDInfo HMDInfo;
+
+ // Last update seconds, used for move speed timing.
+ double LastUpdate;
+ OVR::UInt64 StartupTicks;
+
+ // Position and look. The following apply:
+ Vector3f EyePos;
+ float EyeYaw; // Rotation around Y, CCW positive when looking at RHS (X,Z) plane.
+ float EyePitch; // Pitch. If sensor is plugged in, only read from sensor.
+ float EyeRoll; // Roll, only accessible from Sensor.
+ float LastSensorYaw; // Stores previous Yaw value from to support computing delta.
+
+ // Movement state; different bits may be set based on the state of keys.
+ UByte MoveForward;
+ UByte MoveBack;
+ UByte MoveLeft;
+ UByte MoveRight;
+
+ Matrix4f ViewMat;
+ RenderTiny::Scene Scene;
+
+ // Stereo view parameters.
+ StereoConfig SConfig;
+ PostProcessType PostProcess;
+
+ // Shift accelerates movement/adjustment velocity.
+ bool ShiftDown;
+ bool ControlDown;
+};
+
+// Adds sample models and lights to the argument scene.
+void PopulateRoomScene(Scene* scene, RenderDevice* render);
+
+
+#endif
diff --git a/Samples/OculusRoomTiny/OSX_OculusRoomTiny.mm b/Samples/OculusRoomTiny/OSX_OculusRoomTiny.mm
new file mode 100644
index 0000000..0a59afd
--- /dev/null
+++ b/Samples/OculusRoomTiny/OSX_OculusRoomTiny.mm
@@ -0,0 +1,861 @@
+/************************************************************************************
+
+ Filename : OSX_OculusRoomTiny.mm
+ Content : Simplest possible first-person view test application for Oculus Rift
+ Created : May 7, 2013
+ Authors : Michael Antonov, Andrew Reisse, Artem Bolgar
+
+ Copyright : Copyright 2013 Oculus, Inc. All Rights reserved.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ *************************************************************************************/
+
+#import "OSX_OculusRoomTiny.h"
+#include "RenderTiny_GL_Device.h"
+
+#include "Kernel/OVR_KeyCodes.h"
+
+using namespace OVR;
+
+//-------------------------------------------------------------------------------------
+// ***** OculusRoomTiny Class
+
+// Static pApp simplifies routing the window function.
+OculusRoomTinyApp* OculusRoomTinyApp::pApp = 0;
+
+
+OculusRoomTinyApp::OculusRoomTinyApp(OVRApp* nsapp)
+ : pRender(0),
+ LastUpdate(0),
+ NsApp(nsapp),
+ Quit(0),
+
+ // Initial location
+ EyePos(0.0f, 1.6f, -5.0f),
+ EyeYaw(YawInitial), EyePitch(0), EyeRoll(0),
+ LastSensorYaw(0),
+ SConfig(),
+ PostProcess(PostProcess_Distortion),
+ ShiftDown(false),
+ ControlDown(false)
+{
+ pApp = this;
+
+ Width = 1280;
+ Height = 800;
+
+ StartupTicks = OVR::Timer::GetTicks();
+
+ MoveForward = MoveBack = MoveLeft = MoveRight = 0;
+}
+
+OculusRoomTinyApp::~OculusRoomTinyApp()
+{
+ RemoveHandlerFromDevices();
+ pSensor.Clear();
+ pHMD.Clear();
+ destroyWindow();
+ pApp = 0;
+}
+
+
+int OculusRoomTinyApp::OnStartup(const char* args)
+{
+ OVR_UNUSED(args);
+
+
+ // *** Oculus HMD & Sensor Initialization
+
+ // Create DeviceManager and first available HMDDevice from it.
+ // Sensor object is created from the HMD, to ensure that it is on the
+ // correct device.
+
+ pManager = *DeviceManager::Create();
+
+ // We'll handle it's messages in this case.
+ pManager->SetMessageHandler(this);
+
+ CFOptionFlags detectionResult;
+ const char* detectionMessage;
+
+ do
+ {
+ // Release Sensor/HMD in case this is a retry.
+ pSensor.Clear();
+ pHMD.Clear();
+ RenderParams.MonitorName.Clear();
+
+ pHMD = *pManager->EnumerateDevices<HMDDevice>().CreateDevice();
+ if (pHMD)
+ {
+ pSensor = *pHMD->GetSensor();
+
+ // This will initialize HMDInfo with information about configured IPD,
+ // screen size and other variables needed for correct projection.
+ // We pass HMD DisplayDeviceName into the renderer to select the
+ // correct monitor in full-screen mode.
+ if (pHMD->GetDeviceInfo(&HMDInfo))
+ {
+ RenderParams.MonitorName = HMDInfo.DisplayDeviceName;
+ RenderParams.DisplayId = HMDInfo.DisplayId;
+ SConfig.SetHMDInfo(HMDInfo);
+ }
+ }
+ else
+ {
+ // If we didn't detect an HMD, try to create the sensor directly.
+ // This is useful for debugging sensor interaction; it is not needed in
+ // a shipping app.
+ pSensor = *pManager->EnumerateDevices<SensorDevice>().CreateDevice();
+ }
+
+
+ // If there was a problem detecting the Rift, display appropriate message.
+ detectionResult = kCFUserNotificationAlternateResponse;
+
+ if (!pHMD && !pSensor)
+ detectionMessage = "Oculus Rift not detected.";
+ else if (!pHMD)
+ detectionMessage = "Oculus Sensor detected; HMD Display not detected.";
+ else if (!pSensor)
+ detectionMessage = "Oculus HMD Display detected; Sensor not detected.";
+ else if (HMDInfo.DisplayDeviceName[0] == '\0')
+ detectionMessage = "Oculus Sensor detected; HMD display EDID not detected.";
+ else
+ detectionMessage = 0;
+
+ if (detectionMessage)
+ {
+ String messageText(detectionMessage);
+ messageText += "\n\n"
+ "Press 'Try Again' to run retry detection.\n"
+ "Press 'Continue' to run full-screen anyway.";
+
+ CFStringRef headerStrRef = CFStringCreateWithCString(NULL, "Oculus Rift Detection", kCFStringEncodingMacRoman);
+ CFStringRef messageStrRef = CFStringCreateWithCString(NULL, messageText, kCFStringEncodingMacRoman);
+
+ //launch the message box
+ CFUserNotificationDisplayAlert(0,
+ kCFUserNotificationNoteAlertLevel,
+ NULL, NULL, NULL,
+ headerStrRef, // header text
+ messageStrRef, // message text
+ CFSTR("Try again"),
+ CFSTR("Continue"),
+ CFSTR("Cancel"),
+ &detectionResult);
+
+ //Clean up the strings
+ CFRelease(headerStrRef);
+ CFRelease(messageStrRef);
+
+ if (detectionResult == kCFUserNotificationCancelResponse ||
+ detectionResult == kCFUserNotificationOtherResponse)
+ return 1;
+ }
+
+ } while (detectionResult != kCFUserNotificationAlternateResponse);
+
+
+ if (HMDInfo.HResolution > 0)
+ {
+ Width = HMDInfo.HResolution;
+ Height = HMDInfo.VResolution;
+ }
+
+
+ if (!setupWindow())
+ return 1;
+
+ if (pSensor)
+ {
+ // We need to attach sensor to SensorFusion object for it to receive
+ // body frame messages and update orientation. SFusion.GetOrientation()
+ // is used in OnIdle() to orient the view.
+ SFusion.AttachToSensor(pSensor);
+ SFusion.SetDelegateMessageHandler(this);
+ SFusion.SetPredictionEnabled(true);
+ }
+
+
+ // *** Initialize Rendering
+
+ // Enable multi-sampling by default.
+ RenderParams.Multisample = 4;
+ RenderParams.Fullscreen = false;//true; //?
+
+ // Setup Graphics.
+ pRender = *OSX::RenderDevice::CreateDevice(RenderParams, (void*)View);
+ if (!pRender)
+ return 1;
+
+
+ // *** Configure Stereo settings.
+
+ SConfig.SetFullViewport(Viewport(0,0, Width, Height));
+ SConfig.SetStereoMode(Stereo_LeftRight_Multipass);
+
+ // Configure proper Distortion Fit.
+ // For 7" screen, fit to touch left side of the view, leaving a bit of invisible
+ // screen on the top (saves on rendering cost).
+ // For smaller screens (5.5"), fit to the top.
+ if (HMDInfo.HScreenSize > 0.0f)
+ {
+ if (HMDInfo.HScreenSize > 0.140f) // 7"
+ SConfig.SetDistortionFitPointVP(-1.0f, 0.0f);
+ else
+ SConfig.SetDistortionFitPointVP(0.0f, 1.0f);
+ }
+
+ pRender->SetSceneRenderScale(SConfig.GetDistortionScale());
+
+ SConfig.Set2DAreaFov(DegreeToRad(85.0f));
+
+
+ // *** Populate Room Scene
+
+ // This creates lights and models.
+ PopulateRoomScene(&Scene, pRender);
+
+
+ LastUpdate = GetAppTime();
+ return 0;
+}
+
+void OculusRoomTinyApp::OnMessage(const Message& msg)
+{
+ if (msg.Type == Message_DeviceAdded && msg.pDevice == pManager)
+ {
+ LogText("DeviceManager reported device added.\n");
+ }
+ else if (msg.Type == Message_DeviceRemoved && msg.pDevice == pManager)
+ {
+ LogText("DeviceManager reported device removed.\n");
+ }
+ else if (msg.Type == Message_DeviceAdded && msg.pDevice == pSensor)
+ {
+ LogText("Sensor reported device added.\n");
+ }
+ else if (msg.Type == Message_DeviceRemoved && msg.pDevice == pSensor)
+ {
+ LogText("Sensor reported device removed.\n");
+ }
+}
+
+bool OculusRoomTinyApp::setupWindow()
+{
+ NSRect winrect;
+ winrect.origin.x = 0;
+ winrect.origin.y = 1000;
+ winrect.size.width = Width;
+ winrect.size.height = Height;
+ NSWindow* win = [[NSWindow alloc] initWithContentRect:winrect styleMask:NSTitledWindowMask|NSClosableWindowMask backing:NSBackingStoreBuffered defer:NO];
+
+ OVRView* view = [[OVRView alloc] initWithFrame:winrect];
+ [win setContentView:view];
+ [win setAcceptsMouseMovedEvents:YES];
+ [win setDelegate:view];
+ [view setApp:pApp];
+ Win = win;
+ View = view;
+
+ const char* title = "OculusRoomTiny";
+ [((NSWindow*)Win) setTitle:[[NSString alloc] initWithBytes:title length:strlen(title) encoding:NSUTF8StringEncoding]];
+
+ [NSCursor hide];
+ [view warpMouseToCenter];
+ CGAssociateMouseAndMouseCursorPosition(false);
+
+ SetFullscreen(RenderParams, true);
+ return true;
+}
+
+void OculusRoomTinyApp::destroyWindow()
+{
+ SetFullscreen(RenderParams, false);
+ [((NSWindow*)Win) close];
+}
+
+void OculusRoomTinyApp::OnMouseMove(int x, int y, int modifiers)
+{
+ OVR_UNUSED(modifiers);
+
+ // Mouse motion here is always relative.
+ int dx = x, dy = y;
+ const float maxPitch = ((3.1415f/2)*0.98f);
+
+ // Apply to rotation. Subtract for right body frame rotation,
+ // since yaw rotation is positive CCW when looking down on XZ plane.
+ EyeYaw -= (Sensitivity * dx)/ 360.0f;
+
+ if (!pSensor)
+ {
+ EyePitch -= (Sensitivity * dy)/ 360.0f;
+
+ if (EyePitch > maxPitch)
+ EyePitch = maxPitch;
+ if (EyePitch < -maxPitch)
+ EyePitch = -maxPitch;
+ }
+}
+
+
+void OculusRoomTinyApp::OnKey(unsigned vk, bool down)
+{
+ switch (vk)
+ {
+ case 'Q':
+ if (down && ControlDown)
+ Exit();;
+ break;
+ case Key_Escape:
+ if (!down)
+ Exit();
+ break;
+
+ // Handle player movement keys.
+ // We just update movement state here, while the actual translation is done in OnIdle()
+ // based on time.
+ case 'W': MoveForward = down ? (MoveForward | 1) : (MoveForward & ~1); break;
+ case 'S': MoveBack = down ? (MoveBack | 1) : (MoveBack & ~1); break;
+ case 'A': MoveLeft = down ? (MoveLeft | 1) : (MoveLeft & ~1); break;
+ case 'D': MoveRight = down ? (MoveRight | 1) : (MoveRight & ~1); break;
+ case Key_Up: MoveForward = down ? (MoveForward | 2) : (MoveForward & ~2); break;
+ case Key_Down: MoveBack = down ? (MoveBack | 2) : (MoveBack & ~2); break;
+
+ case 'R':
+ SFusion.Reset();
+ break;
+
+ case 'P':
+ if (down)
+ {
+ // Toggle chromatic aberration correction on/off.
+ RenderDevice::PostProcessShader shader = pRender->GetPostProcessShader();
+
+ if (shader == RenderDevice::PostProcessShader_Distortion)
+ {
+ pRender->SetPostProcessShader(RenderDevice::PostProcessShader_DistortionAndChromAb);
+ }
+ else if (shader == RenderDevice::PostProcessShader_DistortionAndChromAb)
+ {
+ pRender->SetPostProcessShader(RenderDevice::PostProcessShader_Distortion);
+ }
+ else
+ OVR_ASSERT(false);
+ }
+ break;
+
+ // Switch rendering modes/distortion.
+ case Key_F1:
+ SConfig.SetStereoMode(Stereo_None);
+ PostProcess = PostProcess_None;
+ break;
+ case Key_F2:
+ SConfig.SetStereoMode(Stereo_LeftRight_Multipass);
+ PostProcess = PostProcess_None;
+ break;
+ case Key_F3:
+ SConfig.SetStereoMode(Stereo_LeftRight_Multipass);
+ PostProcess = PostProcess_Distortion;
+ break;
+
+ // Stereo IPD adjustments, in meter (default IPD is 64mm).
+ case '+':
+ case '=':
+ if (down)
+ SConfig.SetIPD(SConfig.GetIPD() + 0.0005f * (ShiftDown ? 5.0f : 1.0f));
+ break;
+ case '-':
+ case '_':
+ if (down)
+ SConfig.SetIPD(SConfig.GetIPD() - 0.0005f * (ShiftDown ? 5.0f : 1.0f));
+ break;
+
+ // Holding down Shift key accelerates adjustment velocity.
+ case Key_Shift:
+ ShiftDown = down;
+ break;
+ case Key_Meta:
+ ControlDown = down;
+ break;
+ }
+}
+
+
+void OculusRoomTinyApp::OnIdle()
+{
+ double curtime = GetAppTime();
+ float dt = float(curtime - LastUpdate);
+ LastUpdate = curtime;
+
+
+ // Handle Sensor motion.
+ // We extract Yaw, Pitch, Roll instead of directly using the orientation
+ // to allow "additional" yaw manipulation with mouse/controller.
+ if (pSensor)
+ {
+ Quatf hmdOrient = SFusion.GetOrientation();
+ float yaw = 0.0f;
+
+ hmdOrient.GetEulerAngles<Axis_Y, Axis_X, Axis_Z>(&yaw, &EyePitch, &EyeRoll);
+
+ EyeYaw += (yaw - LastSensorYaw);
+ LastSensorYaw = yaw;
+ }
+
+
+ if (!pSensor)
+ {
+ const float maxPitch = ((3.1415f/2)*0.98f);
+ if (EyePitch > maxPitch)
+ EyePitch = maxPitch;
+ if (EyePitch < -maxPitch)
+ EyePitch = -maxPitch;
+ }
+
+ // Handle keyboard movement.
+ // This translates EyePos based on Yaw vector direction and keys pressed.
+ // Note that Pitch and Roll do not affect movement (they only affect view).
+ if (MoveForward || MoveBack || MoveLeft || MoveRight)
+ {
+ Vector3f localMoveVector(0,0,0);
+ Matrix4f yawRotate = Matrix4f::RotationY(EyeYaw);
+
+ if (MoveForward)
+ localMoveVector = ForwardVector;
+ else if (MoveBack)
+ localMoveVector = -ForwardVector;
+
+ if (MoveRight)
+ localMoveVector += RightVector;
+ else if (MoveLeft)
+ localMoveVector -= RightVector;
+
+ // Normalize vector so we don't move faster diagonally.
+ localMoveVector.Normalize();
+ Vector3f orientationVector = yawRotate.Transform(localMoveVector);
+ orientationVector *= MoveSpeed * dt * (ShiftDown ? 3.0f : 1.0f);
+
+ EyePos += orientationVector;
+ }
+
+ // Rotate and position View Camera, using YawPitchRoll in BodyFrame coordinates.
+ //
+ Matrix4f rollPitchYaw = Matrix4f::RotationY(EyeYaw) * Matrix4f::RotationX(EyePitch) *
+ Matrix4f::RotationZ(EyeRoll);
+ Vector3f up = rollPitchYaw.Transform(UpVector);
+ Vector3f forward = rollPitchYaw.Transform(ForwardVector);
+
+
+ // Minimal head modelling.
+ float headBaseToEyeHeight = 0.15f; // Vertical height of eye from base of head
+ float headBaseToEyeProtrusion = 0.09f; // Distance forward of eye from base of head
+
+ Vector3f eyeCenterInHeadFrame(0.0f, headBaseToEyeHeight, -headBaseToEyeProtrusion);
+ Vector3f shiftedEyePos = EyePos + rollPitchYaw.Transform(eyeCenterInHeadFrame);
+ shiftedEyePos.y -= eyeCenterInHeadFrame.y; // Bring the head back down to original height
+
+ ViewMat = Matrix4f::LookAtRH(shiftedEyePos, shiftedEyePos + forward, up);
+
+ // This is what transformation would be without head modeling.
+ // View = Matrix4f::LookAtRH(EyePos, EyePos + forward, up);
+
+ switch(SConfig.GetStereoMode())
+ {
+ case Stereo_None:
+ Render(SConfig.GetEyeRenderParams(StereoEye_Center));
+ break;
+
+ case Stereo_LeftRight_Multipass:
+ Render(SConfig.GetEyeRenderParams(StereoEye_Left));
+ Render(SConfig.GetEyeRenderParams(StereoEye_Right));
+ break;
+ }
+
+ pRender->Present();
+ // Force GPU to flush the scene, resulting in the lowest possible latency.
+ pRender->ForceFlushGPU();
+}
+
+
+// Render the scene for one eye.
+void OculusRoomTinyApp::Render(const StereoEyeParams& stereo)
+{
+ pRender->BeginScene(PostProcess);
+
+ // Apply Viewport/Projection for the eye.
+ pRender->ApplyStereoParams(stereo);
+ pRender->Clear();
+ pRender->SetDepthMode(true, true);
+
+ Scene.Render(pRender, stereo.ViewAdjust * ViewMat);
+
+ pRender->FinishScene();
+}
+
+void OculusRoomTinyApp::Exit()
+{
+ [NsApp stop:nil];
+ Quit = true;
+}
+
+bool OculusRoomTinyApp::SetFullscreen(const RenderTiny::RendererParams& rp, int fullscreen)
+{
+ if (fullscreen == RenderTiny::Display_Window)
+ [(OVRView*)View exitFullScreenModeWithOptions:nil];
+ else
+ {
+ NSScreen* usescreen = [NSScreen mainScreen];
+ NSArray* screens = [NSScreen screens];
+ for (int i = 0; i < [screens count]; i++)
+ {
+ NSScreen* s = (NSScreen*)[screens objectAtIndex:i];
+ CGDirectDisplayID disp = [OVRView displayFromScreen:s];
+
+ if (disp == rp.DisplayId)
+ usescreen = s;
+ }
+
+ [(OVRView*)View enterFullScreenMode:usescreen withOptions:nil];
+ }
+
+ if (pRender)
+ pRender->SetFullscreen((RenderTiny::DisplayMode)fullscreen);
+ return 1;
+}
+
+//-------------------------------------------------------------------------------------
+// ***** OS X-Specific Logic
+
+@implementation OVRApp
+
+- (void)dealloc
+{
+ [super dealloc];
+}
+
+- (void)run
+{
+ NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+ _running = YES;
+ OculusRoomTinyApp* app;
+
+ // Initializes LibOVR. This LogMask_All enables maximum logging.
+ // Custom allocator can also be specified here.
+ OVR::System::Init(OVR::Log::ConfigureDefaultLog(OVR::LogMask_All));
+
+ int exitCode = 0;
+ do
+ {
+ {
+ using namespace OVR;
+
+ // CreateApplication must be the first call since it does OVR::System::Initialize.
+ app = new OculusRoomTinyApp(self);
+ // The platform attached to an app will be deleted by DestroyApplication.
+
+ [self setApp:app];
+
+ const char* argv[] = {"OVRApp"};
+ exitCode = app->OnStartup(argv[0]);
+ if (exitCode)
+ break;
+ }
+ [self finishLaunching];
+ [pool drain];
+
+ while ([self isRunning])
+ {
+ pool = [[NSAutoreleasePool alloc] init];
+ NSEvent* event = [self nextEventMatchingMask:NSAnyEventMask untilDate:nil inMode:NSDefaultRunLoopMode dequeue:YES];
+ if (event)
+ {
+ [self sendEvent:event];
+ }
+ _App->OnIdle();
+ [pool drain];
+ }
+ } while(0);
+
+ delete app;
+
+ // No OVR functions involving memory are allowed after this.
+ OVR::System::Destroy();
+}
+
+@end
+
+static int KeyMap[][2] =
+{
+ { NSDeleteFunctionKey, OVR::Key_Delete },
+ { '\t', OVR::Key_Tab },
+ { '\n', OVR::Key_Return },
+ { NSPauseFunctionKey, OVR::Key_Pause },
+ { 27, OVR::Key_Escape },
+ { 127, OVR::Key_Backspace },
+ { ' ', OVR::Key_Space },
+ { NSPageUpFunctionKey, OVR::Key_PageUp },
+ { NSPageDownFunctionKey, OVR::Key_PageDown },
+ { NSNextFunctionKey, OVR::Key_PageDown },
+ { NSEndFunctionKey, OVR::Key_End },
+ { NSHomeFunctionKey, OVR::Key_Home },
+ { NSLeftArrowFunctionKey, OVR::Key_Left },
+ { NSUpArrowFunctionKey, OVR::Key_Up },
+ { NSRightArrowFunctionKey, OVR::Key_Right },
+ { NSDownArrowFunctionKey, OVR::Key_Down },
+ { NSInsertFunctionKey, OVR::Key_Insert },
+ { NSDeleteFunctionKey, OVR::Key_Delete },
+ { NSHelpFunctionKey, OVR::Key_Insert },
+};
+
+
+static KeyCode MapToKeyCode(wchar_t vk)
+{
+ unsigned key = Key_None;
+
+ if ((vk >= 'a') && (vk <= 'z'))
+ {
+ key = vk - 'a' + Key_A;
+ }
+ else if ((vk >= ' ') && (vk <= '~'))
+ {
+ key = vk;
+ }
+ else if ((vk >= '0') && (vk <= '9'))
+ {
+ key = vk - '0' + Key_Num0;
+ }
+ else if ((vk >= NSF1FunctionKey) && (vk <= NSF15FunctionKey))
+ {
+ key = vk - NSF1FunctionKey + Key_F1;
+ }
+ else
+ {
+ for (unsigned i = 0; i< (sizeof(KeyMap) / sizeof(KeyMap[1])); i++)
+ {
+ if (vk == KeyMap[i][0])
+ {
+ key = KeyMap[i][1];
+ break;
+ }
+ }
+ }
+
+ return (KeyCode)key;
+}
+
+@implementation OVRView
+
+-(BOOL) acceptsFirstResponder
+{
+ return YES;
+}
+-(BOOL) acceptsFirstMouse:(NSEvent *)ev
+{
+ return YES;
+}
+
++(CGDirectDisplayID) displayFromScreen:(NSScreen *)s
+{
+ NSNumber* didref = (NSNumber*)[[s deviceDescription] objectForKey:@"NSScreenNumber"];
+ CGDirectDisplayID disp = (CGDirectDisplayID)[didref longValue];
+ return disp;
+}
+
+-(void) warpMouseToCenter
+{
+ NSPoint w;
+ w.x = _App->GetWidth()/2.0f;
+ w.y = _App->GetHeight()/2.0f;
+ w = [[self window] convertBaseToScreen:w];
+ CGDirectDisplayID disp = [OVRView displayFromScreen:[[self window] screen]];
+ CGPoint p = {w.x, CGDisplayPixelsHigh(disp)-w.y};
+ CGDisplayMoveCursorToPoint(disp, p);
+}
+
+-(void) keyDown:(NSEvent*)ev
+{
+ NSString* chars = [ev charactersIgnoringModifiers];
+ if ([chars length])
+ {
+ wchar_t ch = [chars characterAtIndex:0];
+ OVR::KeyCode key = MapToKeyCode(ch);
+ _App->OnKey(key, true);
+ }
+}
+-(void) keyUp:(NSEvent*)ev
+{
+ NSString* chars = [ev charactersIgnoringModifiers];
+ if ([chars length])
+ {
+ wchar_t ch = [chars characterAtIndex:0];
+ OVR::KeyCode key = MapToKeyCode(ch);
+ _App->OnKey(key, false);
+
+ }
+}
+
+
+-(void)flagsChanged:(NSEvent *)ev
+{
+ static const OVR::KeyCode ModifierKeys[] = {OVR::Key_None, OVR::Key_Shift, OVR::Key_Control, OVR::Key_Alt, OVR::Key_Meta};
+
+ unsigned long cmods = [ev modifierFlags];
+ if ((cmods & 0xffff0000) != _Modifiers)
+ {
+ uint32_t mods = 0;
+ if (cmods & NSShiftKeyMask)
+ mods |= 0x01;
+ if (cmods & NSControlKeyMask)
+ mods |= 0x02;
+ if (cmods & NSAlternateKeyMask)
+ mods |= 0x04;
+ if (cmods & NSCommandKeyMask)
+ mods |= 0x08;
+
+ for (int i = 1; i <= 4; i++)
+ {
+ unsigned long m = (1 << (16+i));
+ if ((cmods & m) != (_Modifiers & m))
+ {
+ if (cmods & m)
+ _App->OnKey(ModifierKeys[i], true);
+ else
+ _App->OnKey(ModifierKeys[i], false);
+ }
+ }
+ _Modifiers = cmods & 0xffff0000;
+ }
+}
+
+-(void)ProcessMouse:(NSEvent*)ev
+{
+ switch ([ev type])
+ {
+ case NSLeftMouseDragged:
+ case NSRightMouseDragged:
+ case NSOtherMouseDragged:
+ case NSMouseMoved:
+ {
+ int dx = [ev deltaX];
+ int dy = [ev deltaY];
+
+ if (dx != 0 || dy != 0)
+ {
+ _App->OnMouseMove(dx, dy, 0);
+ [self warpMouseToCenter];
+ }
+ }
+ break;
+ case NSLeftMouseDown:
+ case NSRightMouseDown:
+ case NSOtherMouseDown:
+ break;
+ }
+}
+
+-(void) mouseMoved:(NSEvent*)ev
+{
+ [self ProcessMouse:ev];
+}
+-(void) mouseDragged:(NSEvent*)ev
+{
+ [self ProcessMouse:ev];
+}
+
+-(void) mouseDown:(NSEvent*)ev
+{
+ [self warpMouseToCenter];
+ CGAssociateMouseAndMouseCursorPosition(false);
+ [NSCursor hide];
+}
+
+//-(void)
+
+-(id) initWithFrame:(NSRect)frameRect
+{
+ NSOpenGLPixelFormatAttribute attr[] =
+ {NSOpenGLPFAWindow, NSOpenGLPFADoubleBuffer, NSOpenGLPFADepthSize, 24, nil};
+
+ NSOpenGLPixelFormat *pf = [[[NSOpenGLPixelFormat alloc] initWithAttributes:attr] autorelease];
+
+ self = [super initWithFrame:frameRect pixelFormat:pf];
+ GLint swap = 0;
+ [[self openGLContext] setValues:&swap forParameter:NSOpenGLCPSwapInterval];
+ //[self setWantsBestResolutionOpenGLSurface:YES];
+ return self;
+}
+
+-(BOOL)windowShouldClose:(id)sender
+{
+ _App->Exit();
+ return 1;
+}
+
+@end
+
+// GL OSX-specific logic
+namespace OSX {
+
+ RenderDevice::RenderDevice(const RendererParams& p, void* osview, void* context)
+ : GL::RenderDevice(p), Context(context)
+ {
+ OVRView* view = (OVRView*)osview;
+ NSRect bounds = [view bounds];
+ WindowWidth = bounds.size.width;
+ WindowHeight= bounds.size.height;
+
+ }
+
+ RenderDevice* RenderDevice::CreateDevice(const RendererParams& rp, void* osview)
+ {
+ OVRView* view = (OVRView*)osview;
+ NSOpenGLContext *context = [view openGLContext];
+ if (!context)
+ return NULL;
+
+ [context makeCurrentContext];
+ [[view window] makeKeyAndOrderFront:nil];
+
+ return new OSX::RenderDevice(rp, osview, context);
+ }
+
+ void RenderDevice::Present()
+ {
+ NSOpenGLContext *context = (NSOpenGLContext*)Context;
+ [context flushBuffer];
+ }
+
+ void RenderDevice::Shutdown()
+ {
+ Context = NULL;
+ }
+
+ bool RenderDevice::SetFullscreen(DisplayMode fullscreen)
+ {
+ Params.Fullscreen = fullscreen;
+ return 1;
+ }
+
+}
+
+
+int main(int argc, char *argv[])
+{
+ NSApplication* nsapp = [OVRApp sharedApplication];
+ [nsapp run];
+ return 0;
+}
+
diff --git a/Samples/OculusRoomTiny/OculusRoomModel.cpp b/Samples/OculusRoomTiny/OculusRoomModel.cpp
new file mode 100644
index 0000000..74e60cf
--- /dev/null
+++ b/Samples/OculusRoomTiny/OculusRoomModel.cpp
@@ -0,0 +1,260 @@
+/************************************************************************************
+
+Filename : OculusRoomModel.cpp
+Content : Creates a simple room scene from hard-coded geometry
+Created : October 4, 2012
+
+Copyright : Copyright 2012-2013 Oculus, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+*************************************************************************************/
+
+#include "RenderTiny_Device.h"
+
+using namespace OVR;
+using namespace OVR::RenderTiny;
+
+
+//-------------------------------------------------------------------------------------
+// ***** Room Model
+
+// This model is hard-coded out of axis-aligned solid-colored slabs.
+// Room unit dimensions are in meters. Player starts in the middle.
+//
+
+enum BuiltinTexture
+{
+ Tex_None,
+ Tex_Checker,
+ Tex_Block,
+ Tex_Panel,
+ Tex_Count
+};
+
+struct Slab
+{
+ float x1, y1, z1;
+ float x2, y2, z2;
+ Color c;
+};
+
+struct SlabModel
+{
+ int Count;
+ Slab* pSlabs;
+ BuiltinTexture tex;
+};
+
+Slab FloorSlabs[] =
+{
+ // Floor
+ { -10.0f, -0.1f, -20.0f, 10.0f, 0.0f, 20.1f, Color(128,128,128) }
+};
+
+SlabModel Floor = {sizeof(FloorSlabs)/sizeof(Slab), FloorSlabs, Tex_Checker};
+
+Slab CeilingSlabs[] =
+{
+ { -10.0f, 4.0f, -20.0f, 10.0f, 4.1f, 20.1f, Color(128,128,128) }
+};
+
+SlabModel Ceiling = {sizeof(FloorSlabs)/sizeof(Slab), CeilingSlabs, Tex_Panel};
+
+Slab RoomSlabs[] =
+{
+ // Left Wall
+ { -10.1f, 0.0f, -20.0f, -10.0f, 4.0f, 20.0f, Color(128,128,128) },
+ // Back Wall
+ { -10.0f, -0.1f, -20.1f, 10.0f, 4.0f, -20.0f, Color(128,128,128) },
+
+ // Right Wall
+ { 10.0f, -0.1f, -20.0f, 10.1f, 4.0f, 20.0f, Color(128,128,128) },
+};
+
+SlabModel Room = {sizeof(RoomSlabs)/sizeof(Slab), RoomSlabs, Tex_Block};
+
+Slab FixtureSlabs[] =
+{
+ // Right side shelf
+ { 9.5f, 0.75f, 3.0f, 10.1f, 2.5f, 3.1f, Color(128,128,128) }, // Verticals
+ { 9.5f, 0.95f, 3.7f, 10.1f, 2.75f, 3.8f, Color(128,128,128) },
+ { 9.5f, 1.20f, 2.5f, 10.1f, 1.30f, 3.8f, Color(128,128,128) }, // Horizontals
+ { 9.5f, 2.00f, 3.0f, 10.1f, 2.10f, 4.2f, Color(128,128,128) },
+
+ // Right railing
+ { 5.0f, 1.1f, 20.0f, 10.0f, 1.2f, 20.1f, Color(128,128,128) },
+ // Bars
+ { 9.0f, 1.1f, 20.0f, 9.1f, 0.0f, 20.1f, Color(128,128,128) },
+ { 8.0f, 1.1f, 20.0f, 8.1f, 0.0f, 20.1f, Color(128,128,128) },
+ { 7.0f, 1.1f, 20.0f, 7.1f, 0.0f, 20.1f, Color(128,128,128) },
+ { 6.0f, 1.1f, 20.0f, 6.1f, 0.0f, 20.1f, Color(128,128,128) },
+ { 5.0f, 1.1f, 20.0f, 5.1f, 0.0f, 20.1f, Color(128,128,128) },
+
+ // Left railing
+ { -10.0f, 1.1f, 20.0f, -5.0f, 1.2f, 20.1f, Color(128,128,128) },
+ // Bars
+ { -9.0f, 1.1f, 20.0f, -9.1f, 0.0f, 20.1f, Color(128,128,128) },
+ { -8.0f, 1.1f, 20.0f, -8.1f, 0.0f, 20.1f, Color(128,128,128) },
+ { -7.0f, 1.1f, 20.0f, -7.1f, 0.0f, 20.1f, Color(128,128,128) },
+ { -6.0f, 1.1f, 20.0f, -6.1f, 0.0f, 20.1f, Color(128,128,128) },
+ { -5.0f, 1.1f, 20.0f, -5.1f, 0.0f, 20.1f, Color(128,128,128) },
+
+ // Bottom Floor 2
+ { -15.0f, -6.1f, 18.0f, 15.0f, -6.0f, 30.0f, Color(128,128,128) },
+};
+
+SlabModel Fixtures = {sizeof(FixtureSlabs)/sizeof(Slab), FixtureSlabs};
+
+Slab FurnitureSlabs[] =
+{
+ // Table
+ { -1.8f, 0.7f, 1.0f, 0.0f, 0.8f, 0.0f, Color(128,128,88) },
+ { -1.8f, 0.7f, 0.0f, -1.8f+0.1f, 0.0f, 0.0f+0.1f, Color(128,128,88) }, // Leg 1
+ { -1.8f, 0.7f, 1.0f, -1.8f+0.1f, 0.0f, 1.0f-0.1f, Color(128,128,88) }, // Leg 2
+ { 0.0f, 0.7f, 1.0f, 0.0f-0.1f, 0.0f, 1.0f-0.1f, Color(128,128,88) }, // Leg 2
+ { 0.0f, 0.7f, 0.0f, 0.0f-0.1f, 0.0f, 0.0f+0.1f, Color(128,128,88) }, // Leg 2
+
+ // Chair
+ { -1.4f, 0.5f, -1.1f, -0.8f, 0.55f, -0.5f, Color(88,88,128) }, // Set
+ { -1.4f, 1.0f, -1.1f, -1.4f+0.06f, 0.0f, -1.1f+0.06f, Color(88,88,128) }, // Leg 1
+ { -1.4f, 0.5f, -0.5f, -1.4f+0.06f, 0.0f, -0.5f-0.06f, Color(88,88,128) }, // Leg 2
+ { -0.8f, 0.5f, -0.5f, -0.8f-0.06f, 0.0f, -0.5f-0.06f, Color(88,88,128) }, // Leg 2
+ { -0.8f, 1.0f, -1.1f, -0.8f-0.06f, 0.0f, -1.1f+0.06f, Color(88,88,128) }, // Leg 2
+ { -1.4f, 0.97f,-1.05f,-0.8f, 0.92f, -1.10f, Color(88,88,128) }, // Back high bar
+};
+
+SlabModel Furniture = {sizeof(FurnitureSlabs)/sizeof(Slab), FurnitureSlabs};
+
+Slab PostsSlabs[] =
+{
+ // Posts
+ { 0, 0.0f, 0.0f, 0.1f, 1.3f, 0.1f, Color(128,128,128) },
+ { 0, 0.0f, 0.4f, 0.1f, 1.3f, 0.5f, Color(128,128,128) },
+ { 0, 0.0f, 0.8f, 0.1f, 1.3f, 0.9f, Color(128,128,128) },
+ { 0, 0.0f, 1.2f, 0.1f, 1.3f, 1.3f, Color(128,128,128) },
+ { 0, 0.0f, 1.6f, 0.1f, 1.3f, 1.7f, Color(128,128,128) },
+ { 0, 0.0f, 2.0f, 0.1f, 1.3f, 2.1f, Color(128,128,128) },
+ { 0, 0.0f, 2.4f, 0.1f, 1.3f, 2.5f, Color(128,128,128) },
+ { 0, 0.0f, 2.8f, 0.1f, 1.3f, 2.9f, Color(128,128,128) },
+ { 0, 0.0f, 3.2f, 0.1f, 1.3f, 3.3f, Color(128,128,128) },
+ { 0, 0.0f, 3.6f, 0.1f, 1.3f, 3.7f, Color(128,128,128) },
+};
+
+SlabModel Posts = {sizeof(PostsSlabs)/sizeof(Slab), PostsSlabs};
+
+
+// Temporary helper class used to initialize fills used by model.
+class FillCollection
+{
+public:
+ Ptr<ShaderFill> LitSolid;
+ Ptr<ShaderFill> LitTextures[4];
+
+ FillCollection(RenderDevice* render);
+
+};
+
+FillCollection::FillCollection(RenderDevice* render)
+{
+ Ptr<Texture> builtinTextures[Tex_Count];
+
+ // Create floor checkerboard texture.
+ {
+ Color checker[256*256];
+ for (int j = 0; j < 256; j++)
+ for (int i = 0; i < 256; i++)
+ checker[j*256+i] = (((i/4 >> 5) ^ (j/4 >> 5)) & 1) ?
+ Color(180,180,180,255) : Color(80,80,80,255);
+ builtinTextures[Tex_Checker] = *render->CreateTexture(Texture_RGBA|Texture_GenMipmaps, 256, 256, checker);
+ builtinTextures[Tex_Checker]->SetSampleMode(Sample_Anisotropic|Sample_Repeat);
+ }
+
+ // Ceiling panel texture.
+ {
+ Color panel[256*256];
+ for (int j = 0; j < 256; j++)
+ for (int i = 0; i < 256; i++)
+ panel[j*256+i] = (i/4 == 0 || j/4 == 0) ?
+ Color(80,80,80,255) : Color(180,180,180,255);
+ builtinTextures[Tex_Panel] = *render->CreateTexture(Texture_RGBA|Texture_GenMipmaps, 256, 256, panel);
+ builtinTextures[Tex_Panel]->SetSampleMode(Sample_Anisotropic|Sample_Repeat);
+ }
+
+ // Wall brick textures.
+ {
+ Color block[256*256];
+ for (int j = 0; j < 256; j++)
+ for (int i = 0; i < 256; i++)
+ block[j*256+i] = (((j/4 & 15) == 0) || (((i/4 & 15) == 0) && ((((i/4 & 31) == 0) ^ ((j/4 >> 4) & 1)) == 0))) ?
+ Color(60,60,60,255) : Color(180,180,180,255);
+ builtinTextures[Tex_Block] = *render->CreateTexture(Texture_RGBA|Texture_GenMipmaps, 256, 256, block);
+ builtinTextures[Tex_Block]->SetSampleMode(Sample_Anisotropic|Sample_Repeat);
+ }
+
+ LitSolid = *new ShaderFill(*render->CreateShaderSet());
+ LitSolid->GetShaders()->SetShader(render->LoadBuiltinShader(Shader_Vertex, VShader_MVP));
+ LitSolid->GetShaders()->SetShader(render->LoadBuiltinShader(Shader_Fragment, FShader_LitGouraud));
+
+ for (int i = 1; i < Tex_Count; i++)
+ {
+ LitTextures[i] = *new ShaderFill(*render->CreateShaderSet());
+ LitTextures[i]->GetShaders()->SetShader(render->LoadBuiltinShader(Shader_Vertex, VShader_MVP));
+ LitTextures[i]->GetShaders()->SetShader(render->LoadBuiltinShader(Shader_Fragment, FShader_LitTexture));
+ LitTextures[i]->SetTexture(0, builtinTextures[i]);
+ }
+
+}
+
+
+
+// Helper function to create a model out of Slab arrays.
+Model* CreateModel(Vector3f pos, SlabModel* sm, const FillCollection& fills)
+{
+ Model* m = new Model(Prim_Triangles);
+ m->SetPosition(pos);
+
+ for(int i=0; i< sm->Count; i++)
+ {
+ Slab &s = sm->pSlabs[i];
+ m->AddSolidColorBox(s.x1, s.y1, s.z1, s.x2, s.y2, s.z2, s.c);
+ }
+
+ if (sm->tex > 0)
+ m->Fill = fills.LitTextures[sm->tex];
+ else
+ m->Fill = fills.LitSolid;
+ return m;
+}
+
+
+// Adds sample models and lights to the argument scene.
+void PopulateRoomScene(Scene* scene, RenderDevice* render)
+{
+ FillCollection fills(render);
+
+ scene->World.Add(Ptr<Model>(*CreateModel(Vector3f(0,0,0), &Room, fills)));
+ scene->World.Add(Ptr<Model>(*CreateModel(Vector3f(0,0,0), &Floor, fills)));
+ scene->World.Add(Ptr<Model>(*CreateModel(Vector3f(0,0,0), &Ceiling, fills)));
+ scene->World.Add(Ptr<Model>(*CreateModel(Vector3f(0,0,0), &Fixtures, fills)));
+ scene->World.Add(Ptr<Model>(*CreateModel(Vector3f(0,0,0), &Furniture, fills)));
+ scene->World.Add(Ptr<Model>(*CreateModel(Vector3f(0,0,4), &Furniture, fills)));
+ scene->World.Add(Ptr<Model>(*CreateModel(Vector3f(-3,0,3), &Posts, fills)));
+
+
+ scene->SetAmbient(Vector4f(0.65f,0.65f,0.65f,1));
+ scene->AddLight(Vector3f(-2,4,-2), Vector4f(8,8,8,1));
+ scene->AddLight(Vector3f(3,4,-3), Vector4f(2,1,1,1));
+ scene->AddLight(Vector3f(-4,3,25), Vector4f(3,6,3,1));
+}
+
diff --git a/Samples/OculusRoomTiny/OculusRoomTiny.rc b/Samples/OculusRoomTiny/OculusRoomTiny.rc
new file mode 100644
index 0000000..49a6a5a
--- /dev/null
+++ b/Samples/OculusRoomTiny/OculusRoomTiny.rc
Binary files differ
diff --git a/Samples/OculusRoomTiny/OculusRoomTiny_Msvc2010.vcxproj b/Samples/OculusRoomTiny/OculusRoomTiny_Msvc2010.vcxproj
new file mode 100644
index 0000000..c031772
--- /dev/null
+++ b/Samples/OculusRoomTiny/OculusRoomTiny_Msvc2010.vcxproj
@@ -0,0 +1,187 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{80523489-2881-4F64-8C3B-FAF88B60ABCD}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>OculusRoomTiny</RootNamespace>
+ <ProjectName>OculusRoomTiny</ProjectName>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <IntDir>$(Configuration)\Obj\</IntDir>
+ <OutDir>$(ProjectDir)$(Configuration)\</OutDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <IntDir>$(Configuration)\Obj\</IntDir>
+ <OutDir>$(ProjectDir)$(Configuration)\</OutDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <IntDir>$(Configuration)\Obj\</IntDir>
+ <OutDir>$(ProjectDir)$(Configuration)\</OutDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <IntDir>$(Configuration)\Obj\</IntDir>
+ <OutDir>$(ProjectDir)$(Configuration)\</OutDir>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level4</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>OVR_BUILD_DEBUG;WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>../../LibOVR/Include;../../LibOVR/Src;../../3rdParty/glext;$(DXSDK_DIR)/Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <DebugInformationFormat>OldStyle</DebugInformationFormat>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <MinimalRebuild>false</MinimalRebuild>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>libovrd.lib;dxgi.lib;d3d10.lib;d3d11.lib;d3dcompiler.lib;opengl32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalLibraryDirectories>../../LibOVR/Lib/Win32;$(DXSDK_DIR)/Lib/x86;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level4</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>OVR_BUILD_DEBUG;WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>../../LibOVR/Include;../../LibOVR/Src;../../3rdParty/glext;$(DXSDK_DIR)/Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <DebugInformationFormat>OldStyle</DebugInformationFormat>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <MinimalRebuild>false</MinimalRebuild>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>libovr64d.lib;dxgi.lib;d3d10.lib;d3d11.lib;d3dcompiler.lib;opengl32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalLibraryDirectories>../../LibOVR/Lib/x64;$(DXSDK_DIR)/Lib/x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level4</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>../../LibOVR/Include;../../LibOVR/Src;../../3rdParty/glext;$(DXSDK_DIR)/Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <DebugInformationFormat>OldStyle</DebugInformationFormat>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <AdditionalLibraryDirectories>../../LibOVR/Lib/Win32;$(DXSDK_DIR)/Lib/x86;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>libovr.lib;dxgi.lib;d3d10.lib;d3d11.lib;d3dcompiler.lib;opengl32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level4</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>../../LibOVR/Include;../../LibOVR/Src;../../3rdParty/glext;$(DXSDK_DIR)/Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <DebugInformationFormat>OldStyle</DebugInformationFormat>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <AdditionalLibraryDirectories>../../LibOVR/Lib/x64;$(DXSDK_DIR)/Lib/x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>libovr64.lib;dxgi.lib;d3d10.lib;d3d11.lib;d3dcompiler.lib;opengl32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="OculusRoomModel.cpp" />
+ <ClCompile Include="RenderTiny_D3D1X_Device.cpp" />
+ <ClCompile Include="RenderTiny_Device.cpp" />
+ <ClCompile Include="Win32_OculusRoomTiny.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="RenderTiny_D3D1X_Device.h" />
+ <ClInclude Include="RenderTiny_Device.h" />
+ <ClInclude Include="Win32_OculusRoomTiny.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="OculusRoomTiny.rc" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/Samples/OculusRoomTiny/OculusRoomTiny_Msvc2010.vcxproj.filters b/Samples/OculusRoomTiny/OculusRoomTiny_Msvc2010.vcxproj.filters
new file mode 100644
index 0000000..f7ec303
--- /dev/null
+++ b/Samples/OculusRoomTiny/OculusRoomTiny_Msvc2010.vcxproj.filters
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClCompile Include="OculusRoomModel.cpp" />
+ <ClCompile Include="RenderTiny_D3D1X_Device.cpp" />
+ <ClCompile Include="RenderTiny_Device.cpp" />
+ <ClCompile Include="Win32_OculusRoomTiny.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="RenderTiny_Device.h" />
+ <ClInclude Include="RenderTiny_D3D1X_Device.h" />
+ <ClInclude Include="Win32_OculusRoomTiny.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="OculusRoomTiny.rc" />
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/Samples/OculusRoomTiny/RenderTiny_D3D1X_Device.cpp b/Samples/OculusRoomTiny/RenderTiny_D3D1X_Device.cpp
new file mode 100644
index 0000000..64887dd
--- /dev/null
+++ b/Samples/OculusRoomTiny/RenderTiny_D3D1X_Device.cpp
@@ -0,0 +1,1311 @@
+/************************************************************************************
+
+Filename : RenderTiny_D3D1x.cpp
+Content : RenderDevice implementation for D3DX10/11.
+Created : September 10, 2012
+Authors : Andrew Reisse
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#include "Kernel/OVR_Log.h"
+#include "Kernel/OVR_Std.h"
+
+#include "RenderTiny_D3D1X_Device.h"
+
+#include <d3dcompiler.h>
+
+namespace OVR { namespace RenderTiny { namespace D3D10 {
+
+
+//-------------------------------------------------------------------------------------
+// Vertex format
+static D3D1x_(INPUT_ELEMENT_DESC) ModelVertexDesc[] =
+{
+ {"Position", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, offsetof(Vertex, Pos), D3D1x_(INPUT_PER_VERTEX_DATA), 0},
+ {"Color", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, offsetof(Vertex, C), D3D1x_(INPUT_PER_VERTEX_DATA), 0},
+ {"TexCoord", 0, DXGI_FORMAT_R32G32_FLOAT, 0, offsetof(Vertex, U), D3D1x_(INPUT_PER_VERTEX_DATA), 0},
+ {"Normal", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, offsetof(Vertex, Norm), D3D1x_(INPUT_PER_VERTEX_DATA), 0},
+};
+
+// These shaders are used to render the world, including lit vertex-colored and textured geometry.
+
+// Used for world geometry; has projection matrix.
+static const char* StdVertexShaderSrc =
+ "float4x4 Proj;\n"
+ "float4x4 View;\n"
+ "struct Varyings\n"
+ "{\n"
+ " float4 Position : SV_Position;\n"
+ " float4 Color : COLOR0;\n"
+ " float2 TexCoord : TEXCOORD0;\n"
+ " float3 Normal : NORMAL;\n"
+ " float3 VPos : TEXCOORD4;\n"
+ "};\n"
+ "void main(in float4 Position : POSITION, in float4 Color : COLOR0, in float2 TexCoord : TEXCOORD0,"
+ " in float3 Normal : NORMAL,\n"
+ " out Varyings ov)\n"
+ "{\n"
+ " ov.Position = mul(Proj, mul(View, Position));\n"
+ " ov.Normal = mul(View, Normal);\n"
+ " ov.VPos = mul(View, Position);\n"
+ " ov.TexCoord = TexCoord;\n"
+ " ov.Color = Color;\n"
+ "}\n";
+
+// Used for text/clearing; no projection.
+static const char* DirectVertexShaderSrc =
+ "float4x4 View : register(c4);\n"
+ "void main(in float4 Position : POSITION, in float4 Color : COLOR0,\n"
+ " in float2 TexCoord : TEXCOORD0, in float3 Normal : NORMAL,\n"
+ " out float4 oPosition : SV_Position, out float4 oColor : COLOR,\n"
+ " out float2 oTexCoord : TEXCOORD0,"
+ " out float3 oNormal : NORMAL)\n"
+ "{\n"
+ " oPosition = mul(View, Position);\n"
+ " oTexCoord = TexCoord;\n"
+ " oColor = Color;\n"
+ " oNormal = mul(View, Normal);\n"
+ "}\n";
+
+static const char* SolidPixelShaderSrc =
+ "float4 Color;\n"
+ "struct Varyings\n"
+ "{\n"
+ " float4 Position : SV_Position;\n"
+ " float4 Color : COLOR0;\n"
+ " float2 TexCoord : TEXCOORD0;\n"
+ "};\n"
+ "float4 main(in Varyings ov) : SV_Target\n"
+ "{\n"
+ " return Color;\n"
+ "}\n";
+
+static const char* GouraudPixelShaderSrc =
+ "struct Varyings\n"
+ "{\n"
+ " float4 Position : SV_Position;\n"
+ " float4 Color : COLOR0;\n"
+ " float2 TexCoord : TEXCOORD0;\n"
+ "};\n"
+ "float4 main(in Varyings ov) : SV_Target\n"
+ "{\n"
+ " return ov.Color;\n"
+ "}\n";
+
+static const char* TexturePixelShaderSrc =
+ "Texture2D Texture : register(t0);\n"
+ "SamplerState Linear : register(s0);\n"
+ "struct Varyings\n"
+ "{\n"
+ " float4 Position : SV_Position;\n"
+ " float4 Color : COLOR0;\n"
+ " float2 TexCoord : TEXCOORD0;\n"
+ "};\n"
+ "float4 main(in Varyings ov) : SV_Target\n"
+ "{\n"
+ " float4 color2 = ov.Color * Texture.Sample(Linear, ov.TexCoord);\n"
+ " if (color2.a <= 0.4)\n"
+ " discard;\n"
+ " return color2;\n"
+ "}\n";
+
+
+#define LIGHTING_COMMON \
+ "cbuffer Lighting : register(b1)\n" \
+ "{\n" \
+ " float3 Ambient;\n" \
+ " float3 LightPos[8];\n" \
+ " float4 LightColor[8];\n" \
+ " float LightCount;\n" \
+ "};\n" \
+ "struct Varyings\n" \
+ "{\n" \
+ " float4 Position : SV_Position;\n" \
+ " float4 Color : COLOR0;\n" \
+ " float2 TexCoord : TEXCOORD0;\n" \
+ " float3 Normal : NORMAL;\n" \
+ " float3 VPos : TEXCOORD4;\n" \
+ "};\n" \
+ "float4 DoLight(Varyings v)\n" \
+ "{\n" \
+ " float3 norm = normalize(v.Normal);\n" \
+ " float3 light = Ambient;\n" \
+ " for (uint i = 0; i < LightCount; i++)\n"\
+ " {\n" \
+ " float3 ltp = (LightPos[i] - v.VPos);\n" \
+ " float ldist = dot(ltp,ltp);\n" \
+ " ltp = normalize(ltp);\n" \
+ " light += saturate(LightColor[i] * v.Color.rgb * dot(norm, ltp) / sqrt(ldist));\n"\
+ " }\n" \
+ " return float4(light, v.Color.a);\n" \
+ "}\n"
+
+static const char* LitSolidPixelShaderSrc =
+ LIGHTING_COMMON
+ "float4 main(in Varyings ov) : SV_Target\n"
+ "{\n"
+ " return DoLight(ov) * ov.Color;\n"
+ "}\n";
+
+static const char* LitTexturePixelShaderSrc =
+ "Texture2D Texture : register(t0);\n"
+ "SamplerState Linear : register(s0);\n"
+ LIGHTING_COMMON
+ "float4 main(in Varyings ov) : SV_Target\n"
+ "{\n"
+ " return DoLight(ov) * Texture.Sample(Linear, ov.TexCoord);\n"
+ "}\n";
+
+
+//-------------------------------------------------------------------------------------
+// ***** Distortion Post-process Shaders
+
+static const char* PostProcessVertexShaderSrc =
+ "float4x4 View : register(c4);\n"
+ "float4x4 Texm : register(c8);\n"
+ "void main(in float4 Position : POSITION, in float4 Color : COLOR0, in float2 TexCoord : TEXCOORD0,\n"
+ " out float4 oPosition : SV_Position, out float4 oColor : COLOR, out float2 oTexCoord : TEXCOORD0)\n"
+ "{\n"
+ " oPosition = mul(View, Position);\n"
+ " oTexCoord = mul(Texm, float4(TexCoord,0,1));\n"
+ " oColor = Color;\n"
+ "}\n";
+
+// Shader with just lens distortion correction.
+static const char* PostProcessPixelShaderSrc =
+ "Texture2D Texture : register(t0);\n"
+ "SamplerState Linear : register(s0);\n"
+ "float2 LensCenter;\n"
+ "float2 ScreenCenter;\n"
+ "float2 Scale;\n"
+ "float2 ScaleIn;\n"
+ "float4 HmdWarpParam;\n"
+ "\n"
+
+ // Scales input texture coordinates for distortion.
+ // ScaleIn maps texture coordinates to Scales to ([-1, 1]), although top/bottom will be
+ // larger due to aspect ratio.
+ "float2 HmdWarp(float2 in01)\n"
+ "{\n"
+ " float2 theta = (in01 - LensCenter) * ScaleIn;\n" // Scales to [-1, 1]
+ " float rSq = theta.x * theta.x + theta.y * theta.y;\n"
+ " float2 theta1 = theta * (HmdWarpParam.x + HmdWarpParam.y * rSq + "
+ " HmdWarpParam.z * rSq * rSq + HmdWarpParam.w * rSq * rSq * rSq);\n"
+ " return LensCenter + Scale * theta1;\n"
+ "}\n"
+
+ "float4 main(in float4 oPosition : SV_Position, in float4 oColor : COLOR,\n"
+ " in float2 oTexCoord : TEXCOORD0) : SV_Target\n"
+ "{\n"
+ " float2 tc = HmdWarp(oTexCoord);\n"
+ " if (any(clamp(tc, ScreenCenter-float2(0.25,0.5), ScreenCenter+float2(0.25, 0.5)) - tc))\n"
+ " return 0;\n"
+ " return Texture.Sample(Linear, tc);\n"
+ "}\n";
+
+// Shader with lens distortion and chromatic aberration correction.
+static const char* PostProcessPixelShaderWithChromAbSrc =
+ "Texture2D Texture : register(t0);\n"
+ "SamplerState Linear : register(s0);\n"
+ "float2 LensCenter;\n"
+ "float2 ScreenCenter;\n"
+ "float2 Scale;\n"
+ "float2 ScaleIn;\n"
+ "float4 HmdWarpParam;\n"
+ "float4 ChromAbParam;\n"
+ "\n"
+
+ // Scales input texture coordinates for distortion.
+ // ScaleIn maps texture coordinates to Scales to ([-1, 1]), although top/bottom will be
+ // larger due to aspect ratio.
+ "float4 main(in float4 oPosition : SV_Position, in float4 oColor : COLOR,\n"
+ " in float2 oTexCoord : TEXCOORD0) : SV_Target\n"
+ "{\n"
+ " float2 theta = (oTexCoord - LensCenter) * ScaleIn;\n" // Scales to [-1, 1]
+ " float rSq= theta.x * theta.x + theta.y * theta.y;\n"
+ " float2 theta1 = theta * (HmdWarpParam.x + HmdWarpParam.y * rSq + "
+ " HmdWarpParam.z * rSq * rSq + HmdWarpParam.w * rSq * rSq * rSq);\n"
+ " \n"
+ " // Detect whether blue texture coordinates are out of range since these will scaled out the furthest.\n"
+ " float2 thetaBlue = theta1 * (ChromAbParam.z + ChromAbParam.w * rSq);\n"
+ " float2 tcBlue = LensCenter + Scale * thetaBlue;\n"
+ " if (any(clamp(tcBlue, ScreenCenter-float2(0.25,0.5), ScreenCenter+float2(0.25, 0.5)) - tcBlue))\n"
+ " return 0;\n"
+ " \n"
+ " // Now do blue texture lookup.\n"
+ " float blue = Texture.Sample(Linear, tcBlue).b;\n"
+ " \n"
+ " // Do green lookup (no scaling).\n"
+ " float2 tcGreen = LensCenter + Scale * theta1;\n"
+ " float green = Texture.Sample(Linear, tcGreen).g;\n"
+ " \n"
+ " // Do red scale and lookup.\n"
+ " float2 thetaRed = theta1 * (ChromAbParam.x + ChromAbParam.y * rSq);\n"
+ " float2 tcRed = LensCenter + Scale * thetaRed;\n"
+ " float red = Texture.Sample(Linear, tcRed).r;\n"
+ " \n"
+ " return float4(red, green, blue, 1);\n"
+ "}\n";
+
+
+static const char* VShaderSrcs[VShader_Count] =
+{
+ DirectVertexShaderSrc,
+ StdVertexShaderSrc,
+ PostProcessVertexShaderSrc
+};
+static const char* FShaderSrcs[FShader_Count] =
+{
+ SolidPixelShaderSrc,
+ GouraudPixelShaderSrc,
+ TexturePixelShaderSrc,
+ PostProcessPixelShaderSrc,
+ PostProcessPixelShaderWithChromAbSrc,
+ LitSolidPixelShaderSrc,
+ LitTexturePixelShaderSrc
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** Buffer
+
+Buffer::~Buffer()
+{
+}
+
+bool Buffer::Data(int use, const void *buffer, size_t size)
+{
+ if (D3DBuffer && Size >= size)
+ {
+ if (Dynamic)
+ {
+ if (!buffer)
+ return true;
+
+ void* v = Map(0, size, Map_Discard);
+ if (v)
+ {
+ memcpy(v, buffer, size);
+ Unmap(v);
+ return true;
+ }
+ }
+ else
+ {
+ Ren->Context->UpdateSubresource(D3DBuffer, 0, NULL, buffer, 0, 0);
+ return true;
+ }
+ }
+ if (D3DBuffer)
+ {
+ D3DBuffer = NULL;
+ Size = 0;
+ Use = 0;
+ Dynamic = 0;
+ }
+
+ D3D1x_(BUFFER_DESC) desc;
+ memset(&desc, 0, sizeof(desc));
+ if (use & Buffer_ReadOnly)
+ {
+ desc.Usage = D3D1x_(USAGE_IMMUTABLE);
+ desc.CPUAccessFlags = 0;
+ }
+ else
+ {
+ desc.Usage = D3D1x_(USAGE_DYNAMIC);
+ desc.CPUAccessFlags = D3D1x_(CPU_ACCESS_WRITE);
+ Dynamic = 1;
+ }
+
+ switch(use & Buffer_TypeMask)
+ {
+ case Buffer_Vertex: desc.BindFlags = D3D1x_(BIND_VERTEX_BUFFER); break;
+ case Buffer_Index: desc.BindFlags = D3D1x_(BIND_INDEX_BUFFER); break;
+ case Buffer_Uniform:
+ desc.BindFlags = D3D1x_(BIND_CONSTANT_BUFFER);
+ size += ((size + 15) & ~15) - size;
+ break;
+ }
+
+ desc.ByteWidth = (unsigned)size;
+
+ D3D1x_(SUBRESOURCE_DATA) sr;
+ sr.pSysMem = buffer;
+ sr.SysMemPitch = 0;
+ sr.SysMemSlicePitch = 0;
+
+ HRESULT hr = Ren->Device->CreateBuffer(&desc, buffer ? &sr : NULL, &D3DBuffer.GetRawRef());
+ if (SUCCEEDED(hr))
+ {
+ Use = use;
+ Size = desc.ByteWidth;
+ return 1;
+ }
+ return 0;
+}
+
+void* Buffer::Map(size_t start, size_t size, int flags)
+{
+ OVR_UNUSED(size);
+
+ D3D1x_(MAP) mapFlags = D3D1x_(MAP_WRITE);
+ if (flags & Map_Discard)
+ mapFlags = D3D1x_(MAP_WRITE_DISCARD);
+ if (flags & Map_Unsynchronized)
+ mapFlags = D3D1x_(MAP_WRITE_NO_OVERWRITE);
+
+ void* map = 0;
+ if (SUCCEEDED(D3DBuffer->Map(mapFlags, 0, &map)))
+ return ((char*)map) + start;
+ return NULL;
+}
+
+bool Buffer::Unmap(void *m)
+{
+ OVR_UNUSED(m);
+
+ D3DBuffer->Unmap();
+ return true;
+}
+
+
+//-------------------------------------------------------------------------------------
+// Shaders
+
+template<> bool Shader<RenderTiny::Shader_Vertex, ID3D10VertexShader>::Load(void* shader, size_t size)
+{
+ return SUCCEEDED(Ren->Device->CreateVertexShader(shader, size, &D3DShader));
+}
+template<> bool Shader<RenderTiny::Shader_Pixel, ID3D10PixelShader>::Load(void* shader, size_t size)
+{
+ return SUCCEEDED(Ren->Device->CreatePixelShader(shader, size, &D3DShader));
+}
+
+template<> void Shader<RenderTiny::Shader_Vertex, ID3D10VertexShader>::Set(PrimitiveType) const
+{
+ Ren->Context->VSSetShader(D3DShader);
+}
+template<> void Shader<RenderTiny::Shader_Pixel, ID3D10PixelShader>::Set(PrimitiveType) const
+{
+ Ren->Context->PSSetShader(D3DShader);
+}
+
+template<> void Shader<RenderTiny::Shader_Vertex, ID3D1xVertexShader>::SetUniformBuffer(RenderTiny::Buffer* buffer, int i)
+{
+ Ren->Context->VSSetConstantBuffers(i, 1, &((Buffer*)buffer)->D3DBuffer.GetRawRef());
+}
+template<> void Shader<RenderTiny::Shader_Pixel, ID3D1xPixelShader>::SetUniformBuffer(RenderTiny::Buffer* buffer, int i)
+{
+ Ren->Context->PSSetConstantBuffers(i, 1, &((Buffer*)buffer)->D3DBuffer.GetRawRef());
+}
+
+
+//-------------------------------------------------------------------------------------
+// ***** Shader Base
+
+ShaderBase::ShaderBase(RenderDevice* r, ShaderStage stage)
+ : RenderTiny::Shader(stage), Ren(r), UniformData(0)
+{
+}
+ShaderBase::~ShaderBase()
+{
+ if (UniformData)
+ OVR_FREE(UniformData);
+}
+
+bool ShaderBase::SetUniform(const char* name, int n, const float* v)
+{
+ for(unsigned i = 0; i < UniformInfo.GetSize(); i++)
+ {
+ if (!strcmp(UniformInfo[i].Name.ToCStr(), name))
+ {
+ memcpy(UniformData + UniformInfo[i].Offset, v, n * sizeof(float));
+ return 1;
+ }
+ }
+ return 0;
+}
+
+void ShaderBase::InitUniforms(ID3D10Blob* s)
+{
+ ID3D10ShaderReflection* ref = NULL;
+ D3D10ReflectShader(s->GetBufferPointer(), s->GetBufferSize(), &ref);
+ ID3D10ShaderReflectionConstantBuffer* buf = ref->GetConstantBufferByIndex(0);
+ D3D10_SHADER_BUFFER_DESC bufd;
+ if (FAILED(buf->GetDesc(&bufd)))
+ {
+ UniformsSize = 0;
+ if (UniformData)
+ {
+ OVR_FREE(UniformData);
+ UniformData = 0;
+ }
+ return;
+ }
+
+ for(unsigned i = 0; i < bufd.Variables; i++)
+ {
+ ID3D10ShaderReflectionVariable* var = buf->GetVariableByIndex(i);
+ if (var)
+ {
+ D3D10_SHADER_VARIABLE_DESC vd;
+ if (SUCCEEDED(var->GetDesc(&vd)))
+ {
+ Uniform u;
+ u.Name = vd.Name;
+ u.Offset = vd.StartOffset;
+ u.Size = vd.Size;
+ UniformInfo.PushBack(u);
+ }
+ }
+ }
+
+ UniformsSize = bufd.Size;
+ UniformData = (unsigned char*)OVR_ALLOC(bufd.Size);
+}
+
+void ShaderBase::UpdateBuffer(Buffer* buf)
+{
+ if (UniformsSize)
+ {
+ buf->Data(Buffer_Uniform, UniformData, UniformsSize);
+ }
+}
+
+
+//-------------------------------------------------------------------------------------
+// ***** Texture
+//
+Texture::Texture(RenderDevice* ren, int fmt, int w, int h)
+ : Ren(ren), Tex(NULL), TexSv(NULL), TexRtv(NULL), TexDsv(NULL), Width(w), Height(h)
+{
+ OVR_UNUSED(fmt);
+ Sampler = Ren->GetSamplerState(0);
+}
+
+Texture::~Texture()
+{
+}
+
+void Texture::Set(int slot, RenderTiny::ShaderStage stage) const
+{
+ Ren->SetTexture(stage, slot, this);
+}
+
+void Texture::SetSampleMode(int sm)
+{
+ Sampler = Ren->GetSamplerState(sm);
+}
+
+
+
+//-------------------------------------------------------------------------------------
+// ***** Render Device
+
+RenderDevice::RenderDevice(const RendererParams& p, HWND window)
+{
+ RECT rc;
+ GetClientRect(window, &rc);
+ UINT width = rc.right - rc.left;
+ UINT height = rc.bottom - rc.top;
+ WindowWidth = width;
+ WindowHeight = height;
+ Window = window;
+ Params = p;
+
+ HRESULT hr = CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)(&DXGIFactory.GetRawRef()));
+ if (FAILED(hr))
+ return;
+
+ // Find the adapter & output (monitor) to use for fullscreen, based on the reported name of the HMD's monitor.
+ if (Params.MonitorName.GetLength() > 0)
+ {
+ for(UINT AdapterIndex = 0; ; AdapterIndex++)
+ {
+ HRESULT hr = DXGIFactory->EnumAdapters(AdapterIndex, &Adapter.GetRawRef());
+ if (hr == DXGI_ERROR_NOT_FOUND)
+ break;
+
+ DXGI_ADAPTER_DESC Desc;
+ Adapter->GetDesc(&Desc);
+
+ UpdateMonitorOutputs();
+
+ if (FullscreenOutput)
+ break;
+ }
+
+ if (!FullscreenOutput)
+ Adapter = NULL;
+ }
+
+ if (!Adapter)
+ {
+ DXGIFactory->EnumAdapters(0, &Adapter.GetRawRef());
+ }
+
+ int flags = 0;
+
+ hr = D3D10CreateDevice(Adapter, D3D10_DRIVER_TYPE_HARDWARE, NULL, flags, D3D1x_(SDK_VERSION),
+ &Device.GetRawRef());
+ Context = Device;
+ Context->AddRef();
+
+ if (FAILED(hr))
+ return;
+
+ if (!RecreateSwapChain())
+ return;
+
+ if (Params.Fullscreen)
+ SwapChain->SetFullscreenState(1, FullscreenOutput);
+
+ CurRenderTarget = NULL;
+ for(int i = 0; i < Shader_Count; i++)
+ {
+ UniformBuffers[i] = *CreateBuffer();
+ MaxTextureSet[i] = 0;
+ }
+
+ ID3D10Blob* vsData = CompileShader("vs_4_0", DirectVertexShaderSrc);
+ VertexShaders[VShader_MV] = *new VertexShader(this, vsData);
+ for(int i = 1; i < VShader_Count; i++)
+ {
+ VertexShaders[i] = *new VertexShader(this, CompileShader("vs_4_0", VShaderSrcs[i]));
+ }
+
+ for(int i = 0; i < FShader_Count; i++)
+ {
+ PixelShaders[i] = *new PixelShader(this, CompileShader("ps_4_0", FShaderSrcs[i]));
+ }
+
+ SPInt bufferSize = vsData->GetBufferSize();
+ const void* buffer = vsData->GetBufferPointer();
+ ID3D1xInputLayout** objRef = &ModelVertexIL.GetRawRef();
+
+ HRESULT validate = Device->CreateInputLayout(ModelVertexDesc, sizeof(ModelVertexDesc)/sizeof(D3D1x_(INPUT_ELEMENT_DESC)),
+ buffer, bufferSize, objRef);
+ OVR_UNUSED(validate);
+
+ Ptr<ShaderSet> gouraudShaders = *new ShaderSet();
+ gouraudShaders->SetShader(VertexShaders[VShader_MVP]);
+ gouraudShaders->SetShader(PixelShaders[FShader_Gouraud]);
+ DefaultFill = *new ShaderFill(gouraudShaders);
+
+ D3D1x_(BLEND_DESC) bm;
+ memset(&bm, 0, sizeof(bm));
+ bm.BlendEnable[0] = true;
+ bm.BlendOp = bm.BlendOpAlpha = D3D1x_(BLEND_OP_ADD);
+ bm.SrcBlend = bm.SrcBlendAlpha = D3D1x_(BLEND_SRC_ALPHA);
+ bm.DestBlend = bm.DestBlendAlpha = D3D1x_(BLEND_INV_SRC_ALPHA);
+ bm.RenderTargetWriteMask[0] = D3D1x_(COLOR_WRITE_ENABLE_ALL);
+ Device->CreateBlendState(&bm, &BlendState.GetRawRef());
+
+ D3D1x_(RASTERIZER_DESC) rs;
+ memset(&rs, 0, sizeof(rs));
+ rs.AntialiasedLineEnable = true;
+ rs.CullMode = D3D1x_(CULL_BACK);
+ rs.DepthClipEnable = true;
+ rs.FillMode = D3D1x_(FILL_SOLID);
+ Device->CreateRasterizerState(&rs, &Rasterizer.GetRawRef());
+
+ QuadVertexBuffer = *CreateBuffer();
+ const RenderTiny::Vertex QuadVertices[] =
+ { Vertex(Vector3f(0, 1, 0)), Vertex(Vector3f(1, 1, 0)),
+ Vertex(Vector3f(0, 0, 0)), Vertex(Vector3f(1, 0, 0)) };
+ QuadVertexBuffer->Data(Buffer_Vertex, QuadVertices, sizeof(QuadVertices));
+
+ SetDepthMode(0, 0);
+}
+
+RenderDevice::~RenderDevice()
+{
+ if (SwapChain && Params.Fullscreen)
+ {
+ SwapChain->SetFullscreenState(false, NULL);
+ }
+}
+
+
+// Implement static initializer function to create this class.
+RenderTiny::RenderDevice* RenderDevice::CreateDevice(const RendererParams& rp, void* oswnd)
+{
+ return new RenderDevice(rp, (HWND)oswnd);
+}
+
+
+// Fallback monitor enumeration in case newly plugged in monitor wasn't detected.
+// Added originally for the FactoryTest app.
+// New Outputs don't seem to be detected unless adapter is re-created, but that would also
+// require us to re-initialize D3D10 (recreating objects, etc). This bypasses that for "fake"
+// fullscreen modes.
+BOOL CALLBACK MonitorEnumFunc(HMONITOR hMonitor, HDC, LPRECT, LPARAM dwData)
+{
+ RenderDevice* renderer = (RenderDevice*)dwData;
+
+ MONITORINFOEX monitor;
+ monitor.cbSize = sizeof(monitor);
+
+ if (::GetMonitorInfo(hMonitor, &monitor) && monitor.szDevice[0])
+ {
+ DISPLAY_DEVICE dispDev;
+ memset(&dispDev, 0, sizeof(dispDev));
+ dispDev.cb = sizeof(dispDev);
+
+ if (::EnumDisplayDevices(monitor.szDevice, 0, &dispDev, 0))
+ {
+ if (strstr(String(dispDev.DeviceName).ToCStr(), renderer->GetParams().MonitorName.ToCStr()))
+ {
+ renderer->FSDesktopX = monitor.rcMonitor.left;
+ renderer->FSDesktopY = monitor.rcMonitor.top;
+ return FALSE;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+
+void RenderDevice::UpdateMonitorOutputs()
+{
+ HRESULT hr;
+
+ bool deviceNameFound = false;
+
+ for(UINT OutputIndex = 0; ; OutputIndex++)
+ {
+ Ptr<IDXGIOutput> Output;
+ hr = Adapter->EnumOutputs(OutputIndex, &Output.GetRawRef());
+ if (hr == DXGI_ERROR_NOT_FOUND)
+ {
+ break;
+ }
+
+ DXGI_OUTPUT_DESC OutDesc;
+ Output->GetDesc(&OutDesc);
+
+ MONITORINFOEX monitor;
+ monitor.cbSize = sizeof(monitor);
+ if (::GetMonitorInfo(OutDesc.Monitor, &monitor) && monitor.szDevice[0])
+ {
+ DISPLAY_DEVICE dispDev;
+ memset(&dispDev, 0, sizeof(dispDev));
+ dispDev.cb = sizeof(dispDev);
+
+ if (::EnumDisplayDevices(monitor.szDevice, 0, &dispDev, 0))
+ {
+ if (strstr(String(dispDev.DeviceName).ToCStr(), Params.MonitorName.ToCStr()))
+ {
+ deviceNameFound = true;
+ FullscreenOutput = Output;
+ FSDesktopX = monitor.rcMonitor.left;
+ FSDesktopY = monitor.rcMonitor.top;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!deviceNameFound && !Params.MonitorName.IsEmpty())
+ {
+ EnumDisplayMonitors(0, 0, MonitorEnumFunc, (LPARAM)this);
+ }
+}
+
+bool RenderDevice::RecreateSwapChain()
+{
+ DXGI_SWAP_CHAIN_DESC scDesc;
+ memset(&scDesc, 0, sizeof(scDesc));
+ scDesc.BufferCount = 1;
+ scDesc.BufferDesc.Width = WindowWidth;
+ scDesc.BufferDesc.Height = WindowHeight;
+ scDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+ scDesc.BufferDesc.RefreshRate.Numerator = 60;
+ scDesc.BufferDesc.RefreshRate.Denominator = 1;
+ scDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+ scDesc.OutputWindow = Window;
+ scDesc.SampleDesc.Count = Params.Multisample;
+ scDesc.SampleDesc.Quality = 0;
+ scDesc.Windowed = (Params.Fullscreen != Display_Fullscreen);
+ scDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
+
+ if (SwapChain)
+ {
+ SwapChain->SetFullscreenState(FALSE, NULL);
+ SwapChain->Release();
+ SwapChain = NULL;
+ }
+
+ Ptr<IDXGISwapChain> newSC;
+ if (FAILED(DXGIFactory->CreateSwapChain(Device, &scDesc, &newSC.GetRawRef())))
+ return false;
+ SwapChain = newSC;
+
+ BackBuffer = NULL;
+ BackBufferRT = NULL;
+ HRESULT hr = SwapChain->GetBuffer(0, __uuidof(ID3D1xTexture2D), (void**)&BackBuffer.GetRawRef());
+ if (FAILED(hr))
+ return false;
+
+ hr = Device->CreateRenderTargetView(BackBuffer, NULL, &BackBufferRT.GetRawRef());
+ if (FAILED(hr))
+ return false;
+
+ Texture* depthBuffer = GetDepthBuffer(WindowWidth, WindowHeight, Params.Multisample);
+ CurDepthBuffer = depthBuffer;
+ if (CurRenderTarget == NULL)
+ {
+ Context->OMSetRenderTargets(1, &BackBufferRT.GetRawRef(), depthBuffer->TexDsv);
+ }
+ return true;
+}
+
+bool RenderDevice::SetParams(const RendererParams& newParams)
+{
+ String oldMonitor = Params.MonitorName;
+
+ Params = newParams;
+ if (newParams.MonitorName != oldMonitor)
+ {
+ UpdateMonitorOutputs();
+ }
+
+ // Cause this to be recreated with the new multisample mode.
+ pSceneColorTex = NULL;
+ return RecreateSwapChain();
+}
+
+
+bool RenderDevice::SetFullscreen(DisplayMode fullscreen)
+{
+ if (fullscreen == Params.Fullscreen)
+ return true;
+
+ HRESULT hr = SwapChain->SetFullscreenState(fullscreen, fullscreen ? FullscreenOutput : NULL);
+ if (FAILED(hr))
+ {
+ return false;
+ }
+
+ Params.Fullscreen = fullscreen;
+ return true;
+}
+
+void RenderDevice::SetRealViewport(const Viewport& vp)
+{
+ D3DViewport.Width = vp.w;
+ D3DViewport.Height = vp.h;
+ D3DViewport.MinDepth = 0;
+ D3DViewport.MaxDepth = 1;
+ D3DViewport.TopLeftX = vp.x;
+ D3DViewport.TopLeftY = vp.y;
+ Context->RSSetViewports(1, &D3DViewport);
+}
+
+static int GetDepthStateIndex(bool enable, bool write, RenderDevice::CompareFunc func)
+{
+ if (!enable)
+ return 0;
+ return 1 + int(func) * 2 + write;
+}
+
+void RenderDevice::SetDepthMode(bool enable, bool write, CompareFunc func)
+{
+ int index = GetDepthStateIndex(enable, write, func);
+ if (DepthStates[index])
+ {
+ CurDepthState = DepthStates[index];
+ Context->OMSetDepthStencilState(DepthStates[index], 0);
+ return;
+ }
+
+ D3D1x_(DEPTH_STENCIL_DESC) dss;
+ memset(&dss, 0, sizeof(dss));
+ dss.DepthEnable = enable;
+ switch(func)
+ {
+ case Compare_Always: dss.DepthFunc = D3D1x_(COMPARISON_ALWAYS); break;
+ case Compare_Less: dss.DepthFunc = D3D1x_(COMPARISON_LESS); break;
+ case Compare_Greater: dss.DepthFunc = D3D1x_(COMPARISON_GREATER); break;
+ default:
+ assert(0);
+ }
+ dss.DepthWriteMask = write ? D3D1x_(DEPTH_WRITE_MASK_ALL) : D3D1x_(DEPTH_WRITE_MASK_ZERO);
+ Device->CreateDepthStencilState(&dss, &DepthStates[index].GetRawRef());
+ Context->OMSetDepthStencilState(DepthStates[index], 0);
+ CurDepthState = DepthStates[index];
+}
+
+Texture* RenderDevice::GetDepthBuffer(int w, int h, int ms)
+{
+ for(unsigned i = 0; i < DepthBuffers.GetSize(); i++)
+ {
+ if (w == DepthBuffers[i]->Width && h == DepthBuffers[i]->Height &&
+ ms == DepthBuffers[i]->Samples)
+ return DepthBuffers[i];
+ }
+
+ Ptr<Texture> newDepth = *CreateTexture(Texture_Depth | Texture_RenderTarget | ms, w, h, NULL);
+ if (newDepth == NULL)
+ {
+ OVR_DEBUG_LOG(("Failed to get depth buffer."));
+ return NULL;
+ }
+
+ DepthBuffers.PushBack(newDepth);
+ return newDepth.GetPtr();
+}
+
+void RenderDevice::Clear(float r, float g, float b, float a, float depth)
+{
+ const float color[] = {r, g, b, a};
+
+ // Needed for each eye to do its own clear, since ClearRenderTargetView doesn't honor viewport.
+
+ // Save state that is affected by clearing this way
+ ID3D1xDepthStencilState* oldDepthState = CurDepthState;
+ StandardUniformData clearUniforms;
+
+ SetDepthMode(true, true, Compare_Always);
+
+ Context->IASetInputLayout(ModelVertexIL);
+ Context->GSSetShader(NULL);
+
+ ID3D1xShaderResourceView* sv[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+ if (MaxTextureSet[Shader_Fragment])
+ {
+ Context->PSSetShaderResources(0, MaxTextureSet[Shader_Fragment], sv);
+ }
+
+ ID3D1xBuffer* vertexBuffer = QuadVertexBuffer->GetBuffer();
+ UINT vertexStride = sizeof(Vertex);
+ UINT vertexOffset = 0;
+ Context->IASetVertexBuffers(0, 1, &vertexBuffer, &vertexStride, &vertexOffset);
+
+ clearUniforms.View = Matrix4f(2, 0, 0, 0,
+ 0, 2, 0, 0,
+ 0, 0, 0, 0,
+ -1, -1, depth, 1);
+ UniformBuffers[Shader_Vertex]->Data(Buffer_Uniform, &clearUniforms, sizeof(clearUniforms));
+
+ ID3D1xBuffer* vertexConstants = UniformBuffers[Shader_Vertex]->GetBuffer();
+ Context->VSSetConstantBuffers(0, 1, &vertexConstants);
+ Context->IASetPrimitiveTopology(D3D1x_(PRIMITIVE_TOPOLOGY_TRIANGLESTRIP));
+ VertexShaders[VShader_MV]->Set(Prim_TriangleStrip);
+ PixelShaders[FShader_Solid]->Set(Prim_TriangleStrip);
+
+ UniformBuffers[Shader_Pixel]->Data(Buffer_Uniform, color, sizeof(color));
+ PixelShaders[FShader_Solid]->SetUniformBuffer(UniformBuffers[Shader_Pixel]);
+
+ // Clear Viewport
+ Context->OMSetBlendState(NULL, NULL, 0xffffffff);
+ Context->Draw(4, 0);
+
+ // reset
+ CurDepthState = oldDepthState;
+ Context->OMSetDepthStencilState(CurDepthState, 0);
+}
+
+// Buffers
+
+Buffer* RenderDevice::CreateBuffer()
+{
+ return new Buffer(this);
+}
+
+
+ID3D10Blob* RenderDevice::CompileShader(const char* profile, const char* src, const char* mainName)
+{
+ ID3D10Blob* shader;
+ ID3D10Blob* errors;
+ HRESULT hr = D3DCompile(src, strlen(src), NULL, NULL, NULL, mainName, profile,
+ 0, 0, &shader, &errors);
+ if (FAILED(hr))
+ {
+ OVR_DEBUG_LOG(("Compiling D3D shader for %s failed\n%s\n\n%s",
+ profile, src, errors->GetBufferPointer()));
+ OutputDebugStringA((char*)errors->GetBufferPointer());
+ return NULL;
+ }
+ if (errors)
+ {
+ errors->Release();
+ }
+ return shader;
+}
+
+void RenderDevice::SetCommonUniformBuffer(int i, RenderTiny::Buffer* buffer)
+{
+ CommonUniforms[i] = (Buffer*)buffer;
+
+ Context->PSSetConstantBuffers(1, 1, &CommonUniforms[1]->D3DBuffer.GetRawRef());
+ Context->VSSetConstantBuffers(1, 1, &CommonUniforms[1]->D3DBuffer.GetRawRef());
+}
+
+RenderTiny::Shader *RenderDevice::LoadBuiltinShader(ShaderStage stage, int shader)
+{
+ switch(stage)
+ {
+ case Shader_Vertex:
+ return VertexShaders[shader];
+ case Shader_Pixel:
+ return PixelShaders[shader];
+ default:
+ return NULL;
+ }
+}
+
+
+ID3D1xSamplerState* RenderDevice::GetSamplerState(int sm)
+{
+ if (SamplerStates[sm])
+ return SamplerStates[sm];
+
+ D3D1x_(SAMPLER_DESC) ss;
+ memset(&ss, 0, sizeof(ss));
+ if (sm & Sample_Clamp)
+ ss.AddressU = ss.AddressV = ss.AddressW = D3D1x_(TEXTURE_ADDRESS_CLAMP);
+ else if (sm & Sample_ClampBorder)
+ ss.AddressU = ss.AddressV = ss.AddressW = D3D1x_(TEXTURE_ADDRESS_BORDER);
+ else
+ ss.AddressU = ss.AddressV = ss.AddressW = D3D1x_(TEXTURE_ADDRESS_WRAP);
+
+ if (sm & Sample_Nearest)
+ {
+ ss.Filter = D3D1x_(FILTER_MIN_MAG_MIP_POINT);
+ }
+ else if (sm & Sample_Anisotropic)
+ {
+ ss.Filter = D3D1x_(FILTER_ANISOTROPIC);
+ ss.MaxAnisotropy = 8;
+ }
+ else
+ {
+ ss.Filter = D3D1x_(FILTER_MIN_MAG_MIP_LINEAR);
+ }
+ ss.MaxLOD = 15;
+ Device->CreateSamplerState(&ss, &SamplerStates[sm].GetRawRef());
+ return SamplerStates[sm];
+}
+
+
+void RenderDevice::SetTexture(RenderTiny::ShaderStage stage, int slot, const Texture* t)
+{
+ if (MaxTextureSet[stage] <= slot)
+ MaxTextureSet[stage] = slot + 1;
+
+ ID3D1xShaderResourceView* sv = t ? t->TexSv : NULL;
+ switch(stage)
+ {
+ case Shader_Fragment:
+ Context->PSSetShaderResources(slot, 1, &sv);
+ if (t)
+ {
+ Context->PSSetSamplers(slot, 1, &t->Sampler.GetRawRef());
+ }
+ break;
+
+ case Shader_Vertex:
+ Context->VSSetShaderResources(slot, 1, &sv);
+ break;
+ }
+}
+
+Texture* RenderDevice::CreateTexture(int format, int width, int height, const void* data, int mipcount)
+{
+ OVR_UNUSED(mipcount);
+
+ DXGI_FORMAT d3dformat;
+ int bpp;
+ switch(format & Texture_TypeMask)
+ {
+ case Texture_RGBA:
+ bpp = 4;
+ d3dformat = DXGI_FORMAT_R8G8B8A8_UNORM;
+ break;
+ case Texture_Depth:
+ bpp = 0;
+ d3dformat = DXGI_FORMAT_D32_FLOAT;
+ break;
+ default:
+ return NULL;
+ }
+
+ int samples = (format & Texture_SamplesMask);
+ if (samples < 1)
+ {
+ samples = 1;
+ }
+
+ Texture* NewTex = new Texture(this, format, width, height);
+ NewTex->Samples = samples;
+
+ D3D1x_(TEXTURE2D_DESC) dsDesc;
+ dsDesc.Width = width;
+ dsDesc.Height = height;
+ dsDesc.MipLevels = (format == (Texture_RGBA | Texture_GenMipmaps) && data) ? GetNumMipLevels(width, height) : 1;
+ dsDesc.ArraySize = 1;
+ dsDesc.Format = d3dformat;
+ dsDesc.SampleDesc.Count = samples;
+ dsDesc.SampleDesc.Quality = 0;
+ dsDesc.Usage = D3D1x_(USAGE_DEFAULT);
+ dsDesc.BindFlags = D3D1x_(BIND_SHADER_RESOURCE);
+ dsDesc.CPUAccessFlags = 0;
+ dsDesc.MiscFlags = 0;
+
+ if (format & Texture_RenderTarget)
+ {
+ if ((format & Texture_TypeMask) == Texture_Depth)
+ { // We don't use depth textures, and creating them in d3d10 requires different options.
+ dsDesc.BindFlags = D3D1x_(BIND_DEPTH_STENCIL);
+ }
+ else
+ {
+ dsDesc.BindFlags |= D3D1x_(BIND_RENDER_TARGET);
+ }
+ }
+
+ HRESULT hr = Device->CreateTexture2D(&dsDesc, NULL, &NewTex->Tex.GetRawRef());
+ if (FAILED(hr))
+ {
+ OVR_DEBUG_LOG_TEXT(("Failed to create 2D D3D texture."));
+ NewTex->Release();
+ return NULL;
+ }
+ if (dsDesc.BindFlags & D3D1x_(BIND_SHADER_RESOURCE))
+ {
+ Device->CreateShaderResourceView(NewTex->Tex, NULL, &NewTex->TexSv.GetRawRef());
+ }
+
+ if (data)
+ {
+ Context->UpdateSubresource(NewTex->Tex, 0, NULL, data, width * bpp, width * height * bpp);
+ if (format == (Texture_RGBA | Texture_GenMipmaps))
+ {
+ int srcw = width, srch = height;
+ int level = 0;
+ UByte* mipmaps = NULL;
+ do
+ {
+ level++;
+ int mipw = srcw >> 1;
+ if (mipw < 1)
+ {
+ mipw = 1;
+ }
+ int miph = srch >> 1;
+ if (miph < 1)
+ {
+ miph = 1;
+ }
+ if (mipmaps == NULL)
+ {
+ mipmaps = (UByte*)OVR_ALLOC(mipw * miph * 4);
+ }
+ FilterRgba2x2(level == 1 ? (const UByte*)data : mipmaps, srcw, srch, mipmaps);
+ Context->UpdateSubresource(NewTex->Tex, level, NULL, mipmaps, mipw * bpp, miph * bpp);
+ srcw = mipw;
+ srch = miph;
+ }
+ while(srcw > 1 || srch > 1);
+
+ if (mipmaps != NULL)
+ {
+ OVR_FREE(mipmaps);
+ }
+ }
+ }
+
+ if (format & Texture_RenderTarget)
+ {
+ if ((format & Texture_TypeMask) == Texture_Depth)
+ {
+ Device->CreateDepthStencilView(NewTex->Tex, NULL, &NewTex->TexDsv.GetRawRef());
+ }
+ else
+ {
+ Device->CreateRenderTargetView(NewTex->Tex, NULL, &NewTex->TexRtv.GetRawRef());
+ }
+ }
+
+ return NewTex;
+}
+
+
+// Rendering
+
+void RenderDevice::BeginRendering()
+{
+ Context->RSSetState(Rasterizer);
+}
+
+void RenderDevice::SetRenderTarget(RenderTiny::Texture* colorTex,
+ RenderTiny::Texture* depth, RenderTiny::Texture* stencil)
+{
+ OVR_UNUSED(stencil);
+
+ CurRenderTarget = (Texture*)colorTex;
+ if (colorTex == NULL)
+ {
+ Texture* newDepthBuffer = GetDepthBuffer(WindowWidth, WindowHeight, Params.Multisample);
+ if (newDepthBuffer == NULL)
+ {
+ OVR_DEBUG_LOG(("New depth buffer creation failed."));
+ }
+ if (newDepthBuffer != NULL)
+ {
+ CurDepthBuffer = GetDepthBuffer(WindowWidth, WindowHeight, Params.Multisample);
+ Context->OMSetRenderTargets(1, &BackBufferRT.GetRawRef(), CurDepthBuffer->TexDsv);
+ }
+ return;
+ }
+ if (depth == NULL)
+ {
+ depth = GetDepthBuffer(colorTex->GetWidth(), colorTex->GetHeight(), CurRenderTarget->Samples);
+ }
+
+ ID3D1xShaderResourceView* sv[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+ if (MaxTextureSet[Shader_Fragment])
+ {
+ Context->PSSetShaderResources(0, MaxTextureSet[Shader_Fragment], sv);
+ }
+ memset(MaxTextureSet, 0, sizeof(MaxTextureSet));
+
+ CurDepthBuffer = (Texture*)depth;
+ Context->OMSetRenderTargets(1, &((Texture*)colorTex)->TexRtv.GetRawRef(), ((Texture*)depth)->TexDsv);
+}
+
+
+void RenderDevice::SetWorldUniforms(const Matrix4f& proj)
+{
+ StdUniforms.Proj = proj.Transposed();
+ // Shader constant buffers cannot be partially updated.
+}
+
+
+void RenderDevice::Render(const Matrix4f& matrix, Model* model)
+{
+ // Store data in buffers if not already
+ if (!model->VertexBuffer)
+ {
+ Ptr<Buffer> vb = *CreateBuffer();
+ vb->Data(Buffer_Vertex, &model->Vertices[0], model->Vertices.GetSize() * sizeof(Vertex));
+ model->VertexBuffer = vb;
+ }
+ if (!model->IndexBuffer)
+ {
+ Ptr<Buffer> ib = *CreateBuffer();
+ ib->Data(Buffer_Index, &model->Indices[0], model->Indices.GetSize() * 2);
+ model->IndexBuffer = ib;
+ }
+
+ Render(model->Fill ? model->Fill : DefaultFill,
+ model->VertexBuffer, model->IndexBuffer,
+ matrix, 0, (unsigned)model->Indices.GetSize(), model->GetPrimType());
+}
+
+void RenderDevice::Render(const ShaderFill* fill, RenderTiny::Buffer* vertices, RenderTiny::Buffer* indices,
+ const Matrix4f& matrix, int offset, int count, PrimitiveType rprim)
+{
+ Context->IASetInputLayout(ModelVertexIL);
+ if (indices)
+ {
+ Context->IASetIndexBuffer(((Buffer*)indices)->GetBuffer(), DXGI_FORMAT_R16_UINT, 0);
+ }
+
+ ID3D1xBuffer* vertexBuffer = ((Buffer*)vertices)->GetBuffer();
+ UINT vertexStride = sizeof(Vertex);
+ UINT vertexOffset = offset;
+ Context->IASetVertexBuffers(0, 1, &vertexBuffer, &vertexStride, &vertexOffset);
+
+ ShaderSet* shaders = ((ShaderFill*)fill)->GetShaders();
+
+ ShaderBase* vshader = ((ShaderBase*)shaders->GetShader(Shader_Vertex));
+ unsigned char* vertexData = vshader->UniformData;
+ if (vertexData)
+ {
+ StandardUniformData* stdUniforms = (StandardUniformData*) vertexData;
+ stdUniforms->View = matrix.Transposed();
+ stdUniforms->Proj = StdUniforms.Proj;
+ UniformBuffers[Shader_Vertex]->Data(Buffer_Uniform, vertexData, vshader->UniformsSize);
+ vshader->SetUniformBuffer(UniformBuffers[Shader_Vertex]);
+ }
+
+ for(int i = Shader_Vertex + 1; i < Shader_Count; i++)
+ if (shaders->GetShader(i))
+ {
+ ((ShaderBase*)shaders->GetShader(i))->UpdateBuffer(UniformBuffers[i]);
+ ((ShaderBase*)shaders->GetShader(i))->SetUniformBuffer(UniformBuffers[i]);
+ }
+
+ D3D1x_(PRIMITIVE_TOPOLOGY) prim;
+ switch(rprim)
+ {
+ case Prim_Triangles:
+ prim = D3D1x_(PRIMITIVE_TOPOLOGY_TRIANGLELIST);
+ break;
+ case Prim_Lines:
+ prim = D3D1x_(PRIMITIVE_TOPOLOGY_LINELIST);
+ break;
+ case Prim_TriangleStrip:
+ prim = D3D1x_(PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
+ break;
+ default:
+ assert(0);
+ return;
+ }
+ Context->IASetPrimitiveTopology(prim);
+
+ fill->Set(rprim);
+
+ if (indices)
+ {
+ Context->DrawIndexed(count, 0, 0);
+ }
+ else
+ {
+ Context->Draw(count, 0);
+ }
+}
+
+
+void RenderDevice::Present()
+{
+ SwapChain->Present(0, 0);
+}
+
+void RenderDevice::ForceFlushGPU()
+{
+ D3D1x_QUERY_DESC queryDesc = { D3D1x_(QUERY_EVENT), 0 };
+ Ptr<ID3D1xQuery> query;
+ BOOL done = FALSE;
+
+ if (Device->CreateQuery(&queryDesc, &query.GetRawRef()) == S_OK)
+ {
+ // Begin() not used for EVENT query.
+ query->End();
+ // GetData will returns S_OK for both done == TRUE or FALSE.
+ // Exit on failure to avoid infinite loop.
+ do { }
+ while(!done && !FAILED(query->GetData(&done, sizeof(BOOL), 0)));
+ }
+}
+
+}}}
+
diff --git a/Samples/OculusRoomTiny/RenderTiny_D3D1X_Device.h b/Samples/OculusRoomTiny/RenderTiny_D3D1X_Device.h
new file mode 100644
index 0000000..e608498
--- /dev/null
+++ b/Samples/OculusRoomTiny/RenderTiny_D3D1X_Device.h
@@ -0,0 +1,273 @@
+/************************************************************************************
+
+Filename : RenderTiny_D3D1X_Device.h
+Content : RenderDevice implementation header for D3DX10.
+Created : September 10, 2012
+Authors : Andrew Reisse
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#ifndef INC_RenderTiny_D3D1X_Device_h
+#define INC_RenderTiny_D3D1X_Device_h
+
+#include "Kernel/OVR_String.h"
+#include "Kernel/OVR_Array.h"
+
+#include "RenderTiny_Device.h"
+#include <Windows.h>
+
+#define _OVR_RENDERER_D3D10
+#include <d3d10.h>
+
+namespace OVR { namespace RenderTiny { namespace D3D10 {
+
+class RenderDevice;
+class Buffer;
+
+typedef ID3D10Device ID3D1xDevice;
+typedef ID3D10Device ID3D1xDeviceContext;
+typedef ID3D10RenderTargetView ID3D1xRenderTargetView;
+typedef ID3D10Texture2D ID3D1xTexture2D;
+typedef ID3D10ShaderResourceView ID3D1xShaderResourceView;
+typedef ID3D10DepthStencilView ID3D1xDepthStencilView;
+typedef ID3D10DepthStencilState ID3D1xDepthStencilState;
+typedef ID3D10InputLayout ID3D1xInputLayout;
+typedef ID3D10Buffer ID3D1xBuffer;
+typedef ID3D10VertexShader ID3D1xVertexShader;
+typedef ID3D10PixelShader ID3D1xPixelShader;
+typedef ID3D10GeometryShader ID3D1xGeometryShader;
+typedef ID3D10BlendState ID3D1xBlendState;
+typedef ID3D10RasterizerState ID3D1xRasterizerState;
+typedef ID3D10SamplerState ID3D1xSamplerState;
+typedef ID3D10Query ID3D1xQuery;
+typedef ID3D10Blob ID3D1xBlob;
+typedef D3D10_VIEWPORT D3D1x_VIEWPORT;
+typedef D3D10_QUERY_DESC D3D1x_QUERY_DESC;
+#define D3D1x_(x) D3D10_##x
+#define ID3D1x(x) ID3D10##x
+
+
+
+class ShaderBase : public RenderTiny::Shader
+{
+public:
+ RenderDevice* Ren;
+ unsigned char* UniformData;
+ int UniformsSize;
+
+ struct Uniform
+ {
+ String Name;
+ int Offset, Size;
+ };
+ Array<Uniform> UniformInfo;
+
+ ShaderBase(RenderDevice* r, ShaderStage stage);
+ ~ShaderBase();
+
+ void InitUniforms(ID3D10Blob* s);
+ bool SetUniform(const char* name, int n, const float* v);
+
+ void UpdateBuffer(Buffer* b);
+};
+
+template<RenderTiny::ShaderStage SStage, class D3DShaderType>
+class Shader : public ShaderBase
+{
+public:
+ D3DShaderType* D3DShader;
+
+ Shader(RenderDevice* r, D3DShaderType* s) : ShaderBase(r, SStage), D3DShader(s) {}
+ Shader(RenderDevice* r, ID3D1xBlob* s) : ShaderBase(r, SStage)
+ {
+ Load(s);
+ InitUniforms(s);
+ }
+ ~Shader()
+ {
+ if (D3DShader)
+ D3DShader->Release();
+ }
+ bool Load(ID3D1xBlob* shader)
+ {
+ return Load(shader->GetBufferPointer(), shader->GetBufferSize());
+ }
+
+ // These functions have specializations.
+ bool Load(void* shader, size_t size);
+ void Set(PrimitiveType prim) const;
+ void SetUniformBuffer(RenderTiny::Buffer* buffers, int i = 0);
+};
+
+typedef Shader<RenderTiny::Shader_Vertex, ID3D1xVertexShader> VertexShader;
+typedef Shader<RenderTiny::Shader_Fragment, ID3D1xPixelShader> PixelShader;
+
+
+class Buffer : public RenderTiny::Buffer
+{
+public:
+ RenderDevice* Ren;
+ Ptr<ID3D1xBuffer> D3DBuffer;
+ size_t Size;
+ int Use;
+ bool Dynamic;
+
+public:
+ Buffer(RenderDevice* r) : Ren(r), Size(0), Use(0) {}
+ ~Buffer();
+
+ ID3D1xBuffer* GetBuffer()
+ {
+ return D3DBuffer;
+ }
+
+ virtual size_t GetSize()
+ {
+ return Size;
+ }
+ virtual void* Map(size_t start, size_t size, int flags = 0);
+ virtual bool Unmap(void *m);
+ virtual bool Data(int use, const void* buffer, size_t size);
+};
+
+class Texture : public RenderTiny::Texture
+{
+public:
+ RenderDevice* Ren;
+ Ptr<ID3D1xTexture2D> Tex;
+ Ptr<ID3D1xShaderResourceView> TexSv;
+ Ptr<ID3D1xRenderTargetView> TexRtv;
+ Ptr<ID3D1xDepthStencilView> TexDsv;
+ mutable Ptr<ID3D1xSamplerState> Sampler;
+ int Width, Height;
+ int Samples;
+
+ Texture(RenderDevice* r, int fmt, int w, int h);
+ ~Texture();
+
+ virtual int GetWidth() const
+ {
+ return Width;
+ }
+ virtual int GetHeight() const
+ {
+ return Height;
+ }
+ virtual int GetSamples() const
+ {
+ return Samples;
+ }
+
+ virtual void SetSampleMode(int sm);
+
+ virtual void Set(int slot, RenderTiny::ShaderStage stage = RenderTiny::Shader_Fragment) const;
+};
+
+class RenderDevice : public RenderTiny::RenderDevice
+{
+public:
+ Ptr<IDXGIFactory> DXGIFactory;
+ HWND Window;
+
+ Ptr<ID3D1xDevice> Device;
+ Ptr<ID3D1xDeviceContext> Context;
+ Ptr<IDXGISwapChain> SwapChain;
+ Ptr<IDXGIAdapter> Adapter;
+ Ptr<IDXGIOutput> FullscreenOutput;
+ int FSDesktopX, FSDesktopY;
+
+ Ptr<ID3D1xTexture2D> BackBuffer;
+ Ptr<ID3D1xRenderTargetView> BackBufferRT;
+ Ptr<Texture> CurRenderTarget;
+ Ptr<Texture> CurDepthBuffer;
+ Ptr<ID3D1xRasterizerState> Rasterizer;
+ Ptr<ID3D1xBlendState> BlendState;
+ D3D1x_VIEWPORT D3DViewport;
+
+ Ptr<ID3D1xDepthStencilState> DepthStates[1 + 2 * Compare_Count];
+ Ptr<ID3D1xDepthStencilState> CurDepthState;
+ Ptr<ID3D1xInputLayout> ModelVertexIL;
+
+ Ptr<ID3D1xSamplerState> SamplerStates[Sample_Count];
+
+ struct StandardUniformData
+ {
+ Matrix4f Proj;
+ Matrix4f View;
+ } StdUniforms;
+ Ptr<Buffer> UniformBuffers[Shader_Count];
+ int MaxTextureSet[Shader_Count];
+
+ Ptr<VertexShader> VertexShaders[VShader_Count];
+ Ptr<PixelShader> PixelShaders[FShader_Count];
+ Ptr<Buffer> CommonUniforms[8];
+ Ptr<ShaderFill> DefaultFill;
+
+ Ptr<Buffer> QuadVertexBuffer;
+
+ Array<Ptr<Texture> > DepthBuffers;
+
+public:
+ RenderDevice(const RendererParams& p, HWND window);
+ ~RenderDevice();
+
+ // Implement static initializer function to create this class.
+ static RenderTiny::RenderDevice* CreateDevice(const RendererParams& rp, void* oswnd);
+
+ void UpdateMonitorOutputs();
+
+ virtual void SetRealViewport(const Viewport& vp);
+ virtual bool SetParams(const RendererParams& newParams);
+
+ virtual void Present();
+ virtual void ForceFlushGPU();
+
+ virtual bool SetFullscreen(DisplayMode fullscreen);
+
+ virtual void Clear(float r = 0, float g = 0, float b = 0, float a = 1, float depth = 1);
+
+ virtual Buffer* CreateBuffer();
+ virtual Texture* CreateTexture(int format, int width, int height, const void* data, int mipcount=1);
+
+ Texture* GetDepthBuffer(int w, int h, int ms);
+
+ virtual void BeginRendering();
+ virtual void SetRenderTarget(RenderTiny::Texture* color,
+ RenderTiny::Texture* depth = NULL, RenderTiny::Texture* stencil = NULL);
+ virtual void SetDepthMode(bool enable, bool write, CompareFunc func = Compare_Less);
+ virtual void SetWorldUniforms(const Matrix4f& proj);
+ virtual void SetCommonUniformBuffer(int i, RenderTiny::Buffer* buffer);
+
+ virtual void Render(const Matrix4f& matrix, Model* model);
+ virtual void Render(const ShaderFill* fill, RenderTiny::Buffer* vertices, RenderTiny::Buffer* indices,
+ const Matrix4f& matrix, int offset, int count, PrimitiveType prim = Prim_Triangles);
+
+ virtual ShaderFill *CreateSimpleFill() { return DefaultFill; }
+
+ virtual RenderTiny::Shader *LoadBuiltinShader(ShaderStage stage, int shader);
+
+ bool RecreateSwapChain();
+ virtual ID3D10Blob* CompileShader(const char* profile, const char* src, const char* mainName = "main");
+
+ ID3D1xSamplerState* GetSamplerState(int sm);
+
+ void SetTexture(RenderTiny::ShaderStage stage, int slot, const Texture* t);
+};
+
+}}} // Render::D3D10
+
+#endif
diff --git a/Samples/OculusRoomTiny/RenderTiny_Device.cpp b/Samples/OculusRoomTiny/RenderTiny_Device.cpp
new file mode 100644
index 0000000..1d6cdde
--- /dev/null
+++ b/Samples/OculusRoomTiny/RenderTiny_Device.cpp
@@ -0,0 +1,442 @@
+/************************************************************************************
+
+Filename : RenderTiny_Device.cpp
+Content : Platform renderer for simple scene graph - implementation
+Created : September 6, 2012
+Authors : Andrew Reisse
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#include "RenderTiny_Device.h"
+
+#include "Kernel/OVR_Log.h"
+
+namespace OVR { namespace RenderTiny {
+
+void Model::Render(const Matrix4f& ltw, RenderDevice* ren)
+{
+ if (Visible)
+ {
+ Matrix4f m = ltw * GetMatrix();
+ ren->Render(m, this);
+ }
+}
+
+void Container::Render(const Matrix4f& ltw, RenderDevice* ren)
+{
+ Matrix4f m = ltw * GetMatrix();
+ for(unsigned i = 0; i < Nodes.GetSize(); i++)
+ {
+ Nodes[i]->Render(m, ren);
+ }
+}
+
+void Scene::Render(RenderDevice* ren, const Matrix4f& view)
+{
+ Lighting.Update(view, LightPos);
+
+ ren->SetLighting(&Lighting);
+
+ World.Render(view, ren);
+}
+
+
+
+UInt16 CubeIndices[] =
+{
+ 0, 1, 3,
+ 3, 1, 2,
+
+ 5, 4, 6,
+ 6, 4, 7,
+
+ 8, 9, 11,
+ 11, 9, 10,
+
+ 13, 12, 14,
+ 14, 12, 15,
+
+ 16, 17, 19,
+ 19, 17, 18,
+
+ 21, 20, 22,
+ 22, 20, 23
+};
+
+
+void Model::AddSolidColorBox(float x1, float y1, float z1,
+ float x2, float y2, float z2,
+ Color c)
+{
+ float t;
+
+ if(x1 > x2)
+ {
+ t = x1;
+ x1 = x2;
+ x2 = t;
+ }
+ if(y1 > y2)
+ {
+ t = y1;
+ y1 = y2;
+ y2 = t;
+ }
+ if(z1 > z2)
+ {
+ t = z1;
+ z1 = z2;
+ z2 = t;
+ }
+
+ // Cube vertices and their normals.
+ Vector3f CubeVertices[][3] =
+ {
+ Vector3f(x1, y2, z1), Vector3f(z1, x1), Vector3f(0.0f, 1.0f, 0.0f),
+ Vector3f(x2, y2, z1), Vector3f(z1, x2), Vector3f(0.0f, 1.0f, 0.0f),
+ Vector3f(x2, y2, z2), Vector3f(z2, x2), Vector3f(0.0f, 1.0f, 0.0f),
+ Vector3f(x1, y2, z2), Vector3f(z2, x1), Vector3f(0.0f, 1.0f, 0.0f),
+
+ Vector3f(x1, y1, z1), Vector3f(z1, x1), Vector3f(0.0f, -1.0f, 0.0f),
+ Vector3f(x2, y1, z1), Vector3f(z1, x2), Vector3f(0.0f, -1.0f, 0.0f),
+ Vector3f(x2, y1, z2), Vector3f(z2, x2), Vector3f(0.0f, -1.0f, 0.0f),
+ Vector3f(x1, y1, z2), Vector3f(z2, x1), Vector3f(0.0f, -1.0f, 0.0f),
+
+ Vector3f(x1, y1, z2), Vector3f(z2, y1), Vector3f(-1.0f, 0.0f, 0.0f),
+ Vector3f(x1, y1, z1), Vector3f(z1, y1), Vector3f(-1.0f, 0.0f, 0.0f),
+ Vector3f(x1, y2, z1), Vector3f(z1, y2), Vector3f(-1.0f, 0.0f, 0.0f),
+ Vector3f(x1, y2, z2), Vector3f(z2, y2), Vector3f(-1.0f, 0.0f, 0.0f),
+
+ Vector3f(x2, y1, z2), Vector3f(z2, y1), Vector3f(1.0f, 0.0f, 0.0f),
+ Vector3f(x2, y1, z1), Vector3f(z1, y1), Vector3f(1.0f, 0.0f, 0.0f),
+ Vector3f(x2, y2, z1), Vector3f(z1, y2), Vector3f(1.0f, 0.0f, 0.0f),
+ Vector3f(x2, y2, z2), Vector3f(z2, y2), Vector3f(1.0f, 0.0f, 0.0f),
+
+ Vector3f(x1, y1, z1), Vector3f(x1, y1), Vector3f(0.0f, 0.0f, -1.0f),
+ Vector3f(x2, y1, z1), Vector3f(x2, y1), Vector3f(0.0f, 0.0f, -1.0f),
+ Vector3f(x2, y2, z1), Vector3f(x2, y2), Vector3f(0.0f, 0.0f, -1.0f),
+ Vector3f(x1, y2, z1), Vector3f(x1, y2), Vector3f(0.0f, 0.0f, -1.0f),
+
+ Vector3f(x1, y1, z2), Vector3f(x1, y1), Vector3f(0.0f, 0.0f, 1.0f),
+ Vector3f(x2, y1, z2), Vector3f(x2, y1), Vector3f(0.0f, 0.0f, 1.0f),
+ Vector3f(x2, y2, z2), Vector3f(x2, y2), Vector3f(0.0f, 0.0f, 1.0f),
+ Vector3f(x1, y2, z2), Vector3f(x1, y2), Vector3f(0.0f, 0.0f, 1.0f)
+ };
+
+
+ UInt16 startIndex = GetNextVertexIndex();
+
+ enum
+ {
+ CubeVertexCount = sizeof(CubeVertices) / sizeof(CubeVertices[0]),
+ CubeIndexCount = sizeof(CubeIndices) / sizeof(CubeIndices[0])
+ };
+
+ for(int v = 0; v < CubeVertexCount; v++)
+ {
+ AddVertex(Vertex(CubeVertices[v][0], c, CubeVertices[v][1].x, CubeVertices[v][1].y, CubeVertices[v][2]));
+ }
+
+ // Renumber indices
+ for(int i = 0; i < CubeIndexCount / 3; i++)
+ {
+ AddTriangle(CubeIndices[i * 3] + startIndex,
+ CubeIndices[i * 3 + 1] + startIndex,
+ CubeIndices[i * 3 + 2] + startIndex);
+ }
+}
+
+
+//-------------------------------------------------------------------------------------
+
+
+void ShaderFill::Set(PrimitiveType prim) const
+{
+ Shaders->Set(prim);
+ for(int i = 0; i < 8; i++)
+ {
+ if(Textures[i])
+ {
+ Textures[i]->Set(i);
+ }
+ }
+}
+
+
+
+//-------------------------------------------------------------------------------------
+// ***** Rendering
+
+
+RenderDevice::RenderDevice()
+ : CurPostProcess(PostProcess_None),
+ SceneColorTexW(0), SceneColorTexH(0),
+ SceneRenderScale(1),
+ Distortion(1.0f, 0.18f, 0.115f),
+ PostProcessShaderActive(PostProcessShader_DistortionAndChromAb)
+{
+ PostProcessShaderRequested = PostProcessShaderActive;
+}
+
+ShaderFill* RenderDevice::CreateTextureFill(RenderTiny::Texture* t)
+{
+ ShaderSet* shaders = CreateShaderSet();
+ shaders->SetShader(LoadBuiltinShader(Shader_Vertex, VShader_MVP));
+ shaders->SetShader(LoadBuiltinShader(Shader_Fragment, FShader_Texture));
+ ShaderFill* f = new ShaderFill(*shaders);
+ f->SetTexture(0, t);
+ return f;
+}
+
+void RenderDevice::SetLighting(const LightingParams* lt)
+{
+ if (!LightingBuffer)
+ LightingBuffer = *CreateBuffer();
+
+ LightingBuffer->Data(Buffer_Uniform, lt, sizeof(LightingParams));
+ SetCommonUniformBuffer(1, LightingBuffer);
+}
+
+
+
+void RenderDevice::SetSceneRenderScale(float ss)
+{
+ SceneRenderScale = ss;
+ pSceneColorTex = NULL;
+}
+
+void RenderDevice::SetViewport(const Viewport& vp)
+{
+ VP = vp;
+
+ if (CurPostProcess == PostProcess_Distortion)
+ {
+ Viewport svp = vp;
+ svp.w = (int)ceil(SceneRenderScale * vp.w);
+ svp.h = (int)ceil(SceneRenderScale * vp.h);
+ svp.x = (int)ceil(SceneRenderScale * vp.x);
+ svp.y = (int)ceil(SceneRenderScale * vp.y);
+ SetRealViewport(svp);
+ }
+ else
+ {
+ SetRealViewport(vp);
+ }
+}
+
+
+bool RenderDevice::initPostProcessSupport(PostProcessType pptype)
+{
+ if (pptype != PostProcess_Distortion)
+ return true;
+
+
+ if (PostProcessShaderRequested != PostProcessShaderActive)
+ {
+ pPostProcessShader.Clear();
+ PostProcessShaderActive = PostProcessShaderRequested;
+ }
+
+ if (!pPostProcessShader)
+ {
+ Shader *vs = LoadBuiltinShader(Shader_Vertex, VShader_PostProcess);
+
+ Shader *ppfs = NULL;
+ if (PostProcessShaderActive == PostProcessShader_Distortion)
+ {
+ ppfs = LoadBuiltinShader(Shader_Fragment, FShader_PostProcess);
+ }
+ else if (PostProcessShaderActive == PostProcessShader_DistortionAndChromAb)
+ {
+ ppfs = LoadBuiltinShader(Shader_Fragment, FShader_PostProcessWithChromAb);
+ }
+ else
+ OVR_ASSERT(false);
+
+ pPostProcessShader = *CreateShaderSet();
+ pPostProcessShader->SetShader(vs);
+ pPostProcessShader->SetShader(ppfs);
+ }
+
+
+ int texw = (int)ceil(SceneRenderScale * WindowWidth),
+ texh = (int)ceil(SceneRenderScale * WindowHeight);
+
+ // If pSceneColorTex is already created and is of correct size, we are done.
+ // It's important to check width/height in case window size changed.
+ if (pSceneColorTex && (texw == SceneColorTexW) && (texh == SceneColorTexH))
+ {
+ return true;
+ }
+
+ pSceneColorTex = *CreateTexture(Texture_RGBA | Texture_RenderTarget | Params.Multisample,
+ texw, texh, NULL);
+ if (!pSceneColorTex)
+ {
+ return false;
+ }
+ SceneColorTexW = texw;
+ SceneColorTexH = texh;
+ pSceneColorTex->SetSampleMode(Sample_ClampBorder | Sample_Linear);
+
+
+ if (!pFullScreenVertexBuffer)
+ {
+ pFullScreenVertexBuffer = *CreateBuffer();
+ const RenderTiny::Vertex QuadVertices[] =
+ {
+ Vertex(Vector3f(0, 1, 0), Color(1, 1, 1, 1), 0, 0),
+ Vertex(Vector3f(1, 1, 0), Color(1, 1, 1, 1), 1, 0),
+ Vertex(Vector3f(0, 0, 0), Color(1, 1, 1, 1), 0, 1),
+ Vertex(Vector3f(1, 0, 0), Color(1, 1, 1, 1), 1, 1)
+ };
+ pFullScreenVertexBuffer->Data(Buffer_Vertex, QuadVertices, sizeof(QuadVertices));
+ }
+ return true;
+}
+
+void RenderDevice::SetProjection(const Matrix4f& proj)
+{
+ Proj = proj;
+ SetWorldUniforms(proj);
+}
+
+void RenderDevice::BeginScene(PostProcessType pptype)
+{
+ BeginRendering();
+
+ if ((pptype != PostProcess_None) && initPostProcessSupport(pptype))
+ {
+ CurPostProcess = pptype;
+ }
+ else
+ {
+ CurPostProcess = PostProcess_None;
+ }
+
+ if (CurPostProcess == PostProcess_Distortion)
+ {
+ SetRenderTarget(pSceneColorTex);
+ SetViewport(VP);
+ }
+ else
+ {
+ SetRenderTarget(0);
+ }
+
+ SetWorldUniforms(Proj);
+}
+
+void RenderDevice::FinishScene()
+{
+ if (CurPostProcess == PostProcess_None)
+ return;
+
+ SetRenderTarget(0);
+ SetRealViewport(VP);
+ FinishScene1();
+
+ CurPostProcess = PostProcess_None;
+}
+
+
+
+void RenderDevice::FinishScene1()
+{
+ // Clear with black
+ Clear(0.0f, 0.0f, 0.0f, 1.0f);
+
+ float w = float(VP.w) / float(WindowWidth),
+ h = float(VP.h) / float(WindowHeight),
+ x = float(VP.x) / float(WindowWidth),
+ y = float(VP.y) / float(WindowHeight);
+
+ float as = float(VP.w) / float(VP.h);
+
+ // We are using 1/4 of DistortionCenter offset value here, since it is
+ // relative to [-1,1] range that gets mapped to [0, 0.5].
+ pPostProcessShader->SetUniform2f("LensCenter",
+ x + (w + Distortion.XCenterOffset * 0.5f)*0.5f, y + h*0.5f);
+ pPostProcessShader->SetUniform2f("ScreenCenter", x + w*0.5f, y + h*0.5f);
+
+ // MA: This is more correct but we would need higher-res texture vertically; we should adopt this
+ // once we have asymmetric input texture scale.
+ float scaleFactor = 1.0f / Distortion.Scale;
+
+ pPostProcessShader->SetUniform2f("Scale", (w/2) * scaleFactor, (h/2) * scaleFactor * as);
+ pPostProcessShader->SetUniform2f("ScaleIn", (2/w), (2/h) / as);
+
+ pPostProcessShader->SetUniform4f("HmdWarpParam",
+ Distortion.K[0], Distortion.K[1], Distortion.K[2], Distortion.K[3]);
+
+ if (PostProcessShaderRequested == PostProcessShader_DistortionAndChromAb)
+ {
+ pPostProcessShader->SetUniform4f("ChromAbParam",
+ Distortion.ChromaticAberration[0],
+ Distortion.ChromaticAberration[1],
+ Distortion.ChromaticAberration[2],
+ Distortion.ChromaticAberration[3]);
+ }
+
+ Matrix4f texm(w, 0, 0, x,
+ 0, h, 0, y,
+ 0, 0, 0, 0,
+ 0, 0, 0, 1);
+ pPostProcessShader->SetUniform4x4f("Texm", texm);
+
+ Matrix4f view(2, 0, 0, -1,
+ 0, 2, 0, -1,
+ 0, 0, 0, 0,
+ 0, 0, 0, 1);
+
+ ShaderFill fill(pPostProcessShader);
+ fill.SetTexture(0, pSceneColorTex);
+ Render(&fill, pFullScreenVertexBuffer, NULL, view, 0, 4, Prim_TriangleStrip);
+}
+
+
+int GetNumMipLevels(int w, int h)
+{
+ int n = 1;
+ while(w > 1 || h > 1)
+ {
+ w >>= 1;
+ h >>= 1;
+ n++;
+ }
+ return n;
+}
+
+void FilterRgba2x2(const UByte* src, int w, int h, UByte* dest)
+{
+ for(int j = 0; j < (h & ~1); j += 2)
+ {
+ const UByte* psrc = src + (w * j * 4);
+ UByte* pdest = dest + ((w >> 1) * (j >> 1) * 4);
+
+ for(int i = 0; i < w >> 1; i++, psrc += 8, pdest += 4)
+ {
+ pdest[0] = (((int)psrc[0]) + psrc[4] + psrc[w * 4 + 0] + psrc[w * 4 + 4]) >> 2;
+ pdest[1] = (((int)psrc[1]) + psrc[5] + psrc[w * 4 + 1] + psrc[w * 4 + 5]) >> 2;
+ pdest[2] = (((int)psrc[2]) + psrc[6] + psrc[w * 4 + 2] + psrc[w * 4 + 6]) >> 2;
+ pdest[3] = (((int)psrc[3]) + psrc[7] + psrc[w * 4 + 3] + psrc[w * 4 + 7]) >> 2;
+ }
+ }
+}
+
+
+}}
diff --git a/Samples/OculusRoomTiny/RenderTiny_Device.h b/Samples/OculusRoomTiny/RenderTiny_Device.h
new file mode 100644
index 0000000..74ad7ec
--- /dev/null
+++ b/Samples/OculusRoomTiny/RenderTiny_Device.h
@@ -0,0 +1,724 @@
+/************************************************************************************
+
+Filename : RenderTiny_Device.h
+Content : Minimal possible renderer for RoomTiny sample
+Created : September 6, 2012
+Authors : Andrew Reisse, Michael Antonov
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#ifndef OVR_RenderTiny_Device_h
+#define OVR_RenderTiny_Device_h
+
+#include "Kernel/OVR_Math.h"
+#include "Kernel/OVR_Array.h"
+#include "Kernel/OVR_RefCount.h"
+#include "Kernel/OVR_String.h"
+#include "Kernel/OVR_File.h"
+#include "Kernel/OVR_Color.h"
+
+#include "Util/Util_Render_Stereo.h"
+
+namespace OVR { namespace RenderTiny {
+
+using namespace OVR::Util::Render;
+
+class RenderDevice;
+
+
+//-----------------------------------------------------------------------------------
+
+// Rendering primitive type used to render Model.
+enum PrimitiveType
+{
+ Prim_Triangles,
+ Prim_Lines,
+ Prim_TriangleStrip,
+ Prim_Unknown,
+ Prim_Count
+};
+
+// Types of shaders taht can be stored together in a ShaderSet.
+enum ShaderStage
+{
+ Shader_Vertex = 0,
+ Shader_Fragment = 2,
+ Shader_Pixel = 2,
+ Shader_Count = 3,
+};
+
+// Built-in shader types; used by LoadBuiltinShader.
+enum BuiltinShaders
+{
+ VShader_MV = 0,
+ VShader_MVP = 1,
+ VShader_PostProcess = 2,
+ VShader_Count = 3,
+
+ FShader_Solid = 0,
+ FShader_Gouraud = 1,
+ FShader_Texture = 2,
+ FShader_PostProcess = 3,
+ FShader_PostProcessWithChromAb = 4,
+ FShader_LitGouraud = 5,
+ FShader_LitTexture = 6,
+ FShader_Count
+};
+
+
+enum MapFlags
+{
+ Map_Discard = 1,
+ Map_Read = 2, // do not use
+ Map_Unsynchronized = 4, // like D3D11_MAP_NO_OVERWRITE
+};
+
+// Buffer types used for uploading geometry & constants.
+enum BufferUsage
+{
+ Buffer_Unknown = 0,
+ Buffer_Vertex = 1,
+ Buffer_Index = 2,
+ Buffer_Uniform = 4,
+ Buffer_TypeMask = 0xff,
+ Buffer_ReadOnly = 0x100, // Buffer must be created with Data().
+};
+
+enum TextureFormat
+{
+ Texture_RGBA = 0x0100,
+ Texture_Depth = 0x8000,
+ Texture_TypeMask = 0xff00,
+ Texture_SamplesMask = 0x00ff,
+ Texture_RenderTarget = 0x10000,
+ Texture_GenMipmaps = 0x20000,
+};
+
+// Texture sampling modes.
+enum SampleMode
+{
+ Sample_Linear = 0,
+ Sample_Nearest = 1,
+ Sample_Anisotropic = 2,
+ Sample_FilterMask = 3,
+
+ Sample_Repeat = 0,
+ Sample_Clamp = 4,
+ Sample_ClampBorder = 8, // If unsupported Clamp is used instead.
+ Sample_AddressMask =12,
+
+ Sample_Count =13,
+};
+
+// A vector with a dummy w component for alignment in uniform buffers (and for float colors).
+// The w component is not used in any calculations.
+struct Vector4f : public Vector3f
+{
+ float w;
+
+ Vector4f() : w(1) {}
+ Vector4f(const Vector3f& v) : Vector3f(v), w(1) {}
+ Vector4f(float r, float g, float b, float a) : Vector3f(r,g,b), w(a) {}
+};
+
+
+// Base class for vertex and pixel shaders. Stored in ShaderSet.
+class Shader : public RefCountBase<Shader>
+{
+ friend class ShaderSet;
+
+protected:
+ ShaderStage Stage;
+
+public:
+ Shader(ShaderStage s) : Stage(s) {}
+ virtual ~Shader() {}
+
+ ShaderStage GetStage() const { return Stage; }
+
+ virtual void Set(PrimitiveType) const { }
+ virtual void SetUniformBuffer(class Buffer* buffers, int i = 0) { OVR_UNUSED2(buffers, i); }
+
+protected:
+ virtual bool SetUniform(const char* name, int n, const float* v) { OVR_UNUSED3(name, n, v); return false; }
+};
+
+
+// A group of shaders, one per stage.
+// A ShaderSet is applied to a RenderDevice for rendering with a given fill.
+class ShaderSet : public RefCountBase<ShaderSet>
+{
+ protected:
+ Ptr<Shader> Shaders[Shader_Count];
+
+public:
+ ShaderSet() { }
+ ~ShaderSet() { }
+
+ virtual void SetShader(Shader *s)
+ {
+ Shaders[s->GetStage()] = s;
+ }
+ virtual void UnsetShader(int stage)
+ {
+ Shaders[stage] = NULL;
+ }
+ Shader* GetShader(int stage) { return Shaders[stage]; }
+
+ virtual void Set(PrimitiveType prim) const
+ {
+ for (int i = 0; i < Shader_Count; i++)
+ if (Shaders[i])
+ Shaders[i]->Set(prim);
+ }
+
+ // Set a uniform (other than the standard matrices). It is undefined whether the
+ // uniforms from one shader occupy the same space as those in other shaders
+ // (unless a buffer is used, then each buffer is independent).
+ virtual bool SetUniform(const char* name, int n, const float* v)
+ {
+ bool result = 0;
+ for (int i = 0; i < Shader_Count; i++)
+ if (Shaders[i])
+ result |= Shaders[i]->SetUniform(name, n, v);
+
+ return result;
+ }
+ bool SetUniform1f(const char* name, float x)
+ {
+ const float v[] = {x};
+ return SetUniform(name, 1, v);
+ }
+ bool SetUniform2f(const char* name, float x, float y)
+ {
+ const float v[] = {x,y};
+ return SetUniform(name, 2, v);
+ }
+ bool SetUniform4f(const char* name, float x, float y, float z, float w = 1)
+ {
+ const float v[] = {x,y,z,w};
+ return SetUniform(name, 4, v);
+ }
+ bool SetUniformv(const char* name, const Vector3f& v)
+ {
+ const float a[] = {v.x,v.y,v.z,1};
+ return SetUniform(name, 4, a);
+ }
+ bool SetUniform4fv(const char* name, int n, const Vector4f* v)
+ {
+ return SetUniform(name, 4*n, &v[0].x);
+ }
+ virtual bool SetUniform4x4f(const char* name, const Matrix4f& m)
+ {
+ Matrix4f mt = m.Transposed();
+ return SetUniform(name, 16, &mt.M[0][0]);
+ }
+};
+
+
+// Fill combines a ShaderSet (vertex, pixel) with textures, if any.
+// Every model has a fill.
+class ShaderFill : public RefCountBase<ShaderFill>
+{
+ Ptr<ShaderSet> Shaders;
+ Ptr<class Texture> Textures[8];
+
+public:
+ ShaderFill(ShaderSet* sh) : Shaders(sh) { }
+ ShaderFill(ShaderSet& sh) : Shaders(sh) { }
+
+ ShaderSet* GetShaders() { return Shaders; }
+
+ virtual void Set(PrimitiveType prim = Prim_Unknown) const;
+ virtual void SetTexture(int i, class Texture* tex) { if (i < 8) Textures[i] = tex; }
+};
+
+
+// Buffer for vertex or index data. Some renderers require separate buffers, so that
+// is recommended. Some renderers cannot have high-performance buffers which are readable,
+// so reading in Map should not be relied on.
+//
+// Constraints on buffers, such as ReadOnly, are not enforced by the API but may result in
+// rendering-system dependent undesirable behavior, such as terrible performance or unreported failure.
+//
+// Use of a buffer inconsistent with usage is also not checked by the API, but it may result in bad
+// performance or even failure.
+//
+// Use the Data() function to set buffer data the first time, if possible (it may be faster).
+
+class Buffer : public RefCountBase<Buffer>
+{
+public:
+ virtual ~Buffer() {}
+
+ virtual size_t GetSize() = 0;
+ virtual void* Map(size_t start, size_t size, int flags = 0) = 0;
+ virtual bool Unmap(void *m) = 0;
+
+ // Allocates a buffer, optionally filling it with data.
+ virtual bool Data(int use, const void* buffer, size_t size) = 0;
+};
+
+class Texture : public RefCountBase<Texture>
+{
+public:
+ virtual ~Texture() {}
+
+ virtual int GetWidth() const = 0;
+ virtual int GetHeight() const = 0;
+ virtual int GetSamples() const { return 1; }
+
+ virtual void SetSampleMode(int sm) = 0;
+ virtual void Set(int slot, ShaderStage stage = Shader_Fragment) const = 0;
+};
+
+
+
+//-----------------------------------------------------------------------------------
+
+// Node is a base class for geometry in a Scene, it contains base position
+// and orientation data.
+// Model and Container both derive from it.
+//
+class Node : public RefCountBase<Node>
+{
+ Vector3f Pos;
+ Quatf Rot;
+
+ mutable Matrix4f Mat;
+ mutable bool MatCurrent;
+
+public:
+ Node() : Pos(Vector3f(0)), MatCurrent(1) { }
+ virtual ~Node() { }
+
+ enum NodeType
+ {
+ Node_NonDisplay,
+ Node_Container,
+ Node_Model
+ };
+ virtual NodeType GetType() const { return Node_NonDisplay; }
+
+ const Vector3f& GetPosition() const { return Pos; }
+ const Quatf& GetOrientation() const { return Rot; }
+ void SetPosition(Vector3f p) { Pos = p; MatCurrent = 0; }
+ void SetOrientation(Quatf q) { Rot = q; MatCurrent = 0; }
+
+ void Move(Vector3f p) { Pos += p; MatCurrent = 0; }
+ void Rotate(Quatf q) { Rot = q * Rot; MatCurrent = 0; }
+
+
+ // For testing only; causes Position an Orientation
+ void SetMatrix(const Matrix4f& m)
+ {
+ MatCurrent = true;
+ Mat = m;
+ }
+
+ const Matrix4f& GetMatrix() const
+ {
+ if (!MatCurrent)
+ {
+ Mat = Rot;
+ Mat = Matrix4f::Translation(Pos) * Mat;
+ MatCurrent = 1;
+ }
+ return Mat;
+ }
+
+ virtual void Render(const Matrix4f& ltw, RenderDevice* ren) { OVR_UNUSED2(ltw, ren); }
+};
+
+
+// Vertex type; same format is used for all shapes for simplicity.
+// Shapes are built by adding vertices to Model.
+struct Vertex
+{
+ Vector3f Pos;
+ Color C;
+ float U, V;
+ Vector3f Norm;
+
+ Vertex (const Vector3f& p, const Color& c = Color(64,0,0,255),
+ float u = 0, float v = 0, Vector3f n = Vector3f(1,0,0))
+ : Pos(p), C(c), U(u), V(v), Norm(n)
+ {}
+ Vertex(float x, float y, float z, const Color& c = Color(64,0,0,255),
+ float u = 0, float v = 0) : Pos(x,y,z), C(c), U(u), V(v)
+ { }
+
+ bool operator==(const Vertex& b) const
+ {
+ return Pos == b.Pos && C == b.C && U == b.U && V == b.V;
+ }
+};
+
+// LightingParams are stored in a uniform buffer, don't change it without fixing all renderers
+// Scene contains a set of LightingParams that is uses for rendering.
+struct LightingParams
+{
+ Vector4f Ambient;
+ Vector4f LightPos[8];
+ Vector4f LightColor[8];
+ float LightCount;
+ int Version;
+
+ LightingParams() : LightCount(0), Version(0) {}
+
+
+ void Update(const Matrix4f& view, const Vector4f* SceneLightPos)
+ {
+ Version++;
+ for (int i = 0; i < LightCount; i++)
+ {
+ LightPos[i] = view.Transform(SceneLightPos[i]);
+ }
+ }
+
+ void Set(ShaderSet* s) const
+ {
+ s->SetUniform4fv("Ambient", 1, &Ambient);
+ s->SetUniform1f("LightCount", LightCount);
+ s->SetUniform4fv("LightPos", (int)LightCount, LightPos);
+ s->SetUniform4fv("LightColor", (int)LightCount, LightColor);
+ }
+
+};
+
+//-----------------------------------------------------------------------------------
+
+// Model is a triangular mesh with a fill that can be added to scene.
+//
+class Model : public Node
+{
+public:
+ Array<Vertex> Vertices;
+ Array<UInt16> Indices;
+ PrimitiveType Type;
+ Ptr<ShaderFill> Fill;
+ bool Visible;
+
+ // Some renderers will create these if they didn't exist before rendering.
+ // Currently they are not updated, so vertex data should not be changed after rendering.
+ Ptr<Buffer> VertexBuffer;
+ Ptr<Buffer> IndexBuffer;
+
+ Model(PrimitiveType t = Prim_Triangles) : Type(t), Fill(NULL), Visible(true) { }
+ ~Model() { }
+
+ PrimitiveType GetPrimType() const { return Type; }
+
+ void SetVisible(bool visible) { Visible = visible; }
+ bool IsVisible() const { return Visible; }
+
+
+ // Node implementation.
+ virtual NodeType GetType() const { return Node_Model; }
+ virtual void Render(const Matrix4f& ltw, RenderDevice* ren);
+
+
+ // Returns the index next added vertex will have.
+ UInt16 GetNextVertexIndex() const
+ {
+ return (UInt16)Vertices.GetSize();
+ }
+
+ UInt16 AddVertex(const Vertex& v)
+ {
+ assert(!VertexBuffer && !IndexBuffer);
+ UInt16 index = (UInt16)Vertices.GetSize();
+ Vertices.PushBack(v);
+ return index;
+ }
+
+ void AddTriangle(UInt16 a, UInt16 b, UInt16 c)
+ {
+ Indices.PushBack(a);
+ Indices.PushBack(b);
+ Indices.PushBack(c);
+ }
+
+ // Uses texture coordinates for uniform world scaling (must use a repeat sampler).
+ void AddSolidColorBox(float x1, float y1, float z1,
+ float x2, float y2, float z2,
+ Color c);
+};
+
+
+// Container stores a collection of rendering nodes (Models or other containers).
+class Container : public Node
+{
+public:
+ Array<Ptr<Node> > Nodes;
+
+ Container() { }
+ ~Container() { }
+
+ virtual NodeType GetType() const { return Node_Container; }
+
+ virtual void Render(const Matrix4f& ltw, RenderDevice* ren);
+
+ void Add(Node *n) { Nodes.PushBack(n); }
+ void Clear() { Nodes.Clear(); }
+};
+
+
+// Scene combines a collection of model
+class Scene
+{
+public:
+ Container World;
+ Vector4f LightPos[8];
+ LightingParams Lighting;
+
+public:
+ void Render(RenderDevice* ren, const Matrix4f& view);
+
+ void SetAmbient(Vector4f color)
+ {
+ Lighting.Ambient = color;
+ }
+
+ void AddLight(Vector3f pos, Vector4f color)
+ {
+ int n = (int)Lighting.LightCount;
+ OVR_ASSERT(n < 8);
+ LightPos[n] = pos;
+ Lighting.LightColor[n] = color;
+ Lighting.LightCount++;
+ }
+
+ void Clear()
+ {
+ World.Clear();
+ Lighting.Ambient = Vector4f(0.0f, 0.0f, 0.0f, 0.0f);
+ Lighting.LightCount = 0;
+ }
+ };
+
+
+//-----------------------------------------------------------------------------------
+
+// Post-processing type to apply to scene after rendering. PostProcess_Distortion
+// applied distortion as described by DistortionConfig.
+enum PostProcessType
+{
+ PostProcess_None,
+ PostProcess_Distortion
+};
+
+enum DisplayMode
+{
+ Display_Window = 0,
+ Display_Fullscreen = 1
+};
+
+
+// Rendering parameters used by RenderDevice::CreateDevice.
+struct RendererParams
+{
+ int Multisample;
+ int Fullscreen;
+
+ // Windows - Monitor name for fullscreen mode.
+ String MonitorName;
+ // MacOS
+ long DisplayId;
+
+ RendererParams(int ms = 1) : Multisample(ms), Fullscreen(0) {}
+
+ bool IsDisplaySet() const
+ {
+ return MonitorName.GetLength() || DisplayId;
+ }
+};
+
+
+
+//-----------------------------------------------------------------------------------
+// ***** RenderDevice
+
+// Rendering device abstraction.
+// Provides platform-independent part of implementation, with platform-specific
+// part being in a separate derived class/file, such as D3D10::RenderDevice.
+//
+class RenderDevice : public RefCountBase<RenderDevice>
+{
+protected:
+ int WindowWidth, WindowHeight;
+ RendererParams Params;
+ Viewport VP;
+
+ Matrix4f Proj;
+ Ptr<Buffer> pTextVertexBuffer;
+
+ // For rendering with lens warping
+ PostProcessType CurPostProcess;
+ Ptr<Texture> pSceneColorTex; // Distortion render target, both eyes.
+ int SceneColorTexW;
+ int SceneColorTexH;
+ Ptr<ShaderSet> pPostProcessShader;
+ Ptr<Buffer> pFullScreenVertexBuffer;
+ float SceneRenderScale;
+ DistortionConfig Distortion;
+
+ // For lighting on platforms with uniform buffers
+ Ptr<Buffer> LightingBuffer;
+
+ void FinishScene1();
+
+public:
+ enum CompareFunc
+ {
+ Compare_Always = 0,
+ Compare_Less = 1,
+ Compare_Greater = 2,
+ Compare_Count
+ };
+ RenderDevice();
+ virtual ~RenderDevice() { Shutdown(); }
+
+ // This static function is implemented in each derived class
+ // to support a specific renderer type.
+ //static RenderDevice* CreateDevice(const RendererParams& rp, void* oswnd);
+
+
+ virtual void Init() {}
+ virtual void Shutdown() {}
+ virtual bool SetParams(const RendererParams&) { return 0; }
+
+ const RendererParams& GetParams() const { return Params; }
+
+
+ // StereoParams apply Viewport, Projection and Distortion simultaneously,
+ // doing full configuration for one eye.
+ void ApplyStereoParams(const StereoEyeParams& params)
+ {
+ SetViewport(params.VP);
+ SetProjection(params.Projection);
+ if (params.pDistortion)
+ SetDistortionConfig(*params.pDistortion, params.Eye);
+ }
+
+ virtual void SetViewport(const Viewport& vp);
+ void SetViewport(int x, int y, int w, int h) { SetViewport(Viewport(x,y,w,h)); }
+
+ // PostProcess distortion
+ void SetSceneRenderScale(float ss);
+
+ void SetDistortionConfig(const DistortionConfig& config, StereoEye eye = StereoEye_Left)
+ {
+ Distortion = config;
+ if (eye == StereoEye_Right)
+ Distortion.XCenterOffset = -Distortion.XCenterOffset;
+ }
+
+ // Set viewport ignoring any adjustments used for the stereo mode.
+ virtual void SetRealViewport(const Viewport& vp) = 0;
+
+ virtual void Clear(float r = 0, float g = 0, float b = 0, float a = 1, float depth = 1) = 0;
+
+ virtual bool IsFullscreen() const { return Params.Fullscreen != Display_Window; }
+ virtual void Present() = 0;
+ // Waits for rendering to complete; important for reducing latency.
+ virtual void ForceFlushGPU() { }
+
+ // Resources
+ virtual Buffer* CreateBuffer() { return NULL; }
+ virtual Texture* CreateTexture(int format, int width, int height, const void* data, int mipcount=1)
+ { OVR_UNUSED5(format,width,height,data, mipcount); return NULL; }
+
+
+ virtual ShaderSet* CreateShaderSet() { return new ShaderSet; }
+ virtual Shader* LoadBuiltinShader(ShaderStage stage, int shader) = 0;
+
+ // Rendering
+
+ // Begin drawing directly to the currently selected render target, no post-processing.
+ virtual void BeginRendering() {}
+ // Begin drawing the primary scene. This will have post-processing applied (if enabled)
+ // during FinishScene.
+ virtual void BeginScene(PostProcessType pp = PostProcess_None);
+ // Postprocess the scene and return to the screen render target.
+ virtual void FinishScene();
+
+ // Texture must have been created with Texture_RenderTarget. Use NULL for the default render target.
+ // NULL depth buffer means use an internal, temporary one.
+ virtual void SetRenderTarget(Texture* color, Texture* depth = NULL, Texture* stencil = NULL)
+ { OVR_UNUSED3(color, depth, stencil); }
+ virtual void SetDepthMode(bool enable, bool write, CompareFunc func = Compare_Less) = 0;
+ virtual void SetProjection(const Matrix4f& proj);
+ virtual void SetWorldUniforms(const Matrix4f& proj) = 0;
+
+ // The data is not copied, it must remain valid until the end of the frame
+ virtual void SetLighting(const LightingParams* light);
+
+ // The index 0 is reserved for non-buffer uniforms, and so cannot be used with this function.
+ virtual void SetCommonUniformBuffer(int i, Buffer* buffer) { OVR_UNUSED2(i, buffer); }
+
+ virtual Matrix4f GetProjection() const { return Proj; }
+
+ // This is a View matrix only, it will be combined with the projection matrix from SetProjection
+ virtual void Render(const Matrix4f& matrix, Model* model) = 0;
+ // offset is in bytes; indices can be null.
+ virtual void Render(const ShaderFill* fill, Buffer* vertices, Buffer* indices,
+ const Matrix4f& matrix, int offset, int count, PrimitiveType prim = Prim_Triangles) = 0;
+
+ virtual ShaderFill *CreateSimpleFill() = 0;
+ ShaderFill * CreateTextureFill(Texture* tex);
+
+
+ // Don't call these directly, use App/Platform instead
+ virtual bool SetFullscreen(DisplayMode fullscreen) { OVR_UNUSED(fullscreen); return false; }
+
+
+ enum PostProcessShader
+ {
+ PostProcessShader_Distortion = 0,
+ PostProcessShader_DistortionAndChromAb = 1,
+ PostProcessShader_Count
+ };
+
+ PostProcessShader GetPostProcessShader()
+ {
+ return PostProcessShaderActive;
+ }
+
+ void SetPostProcessShader(PostProcessShader newShader)
+ {
+ PostProcessShaderRequested = newShader;
+ }
+
+protected:
+ // Stereo & post-processing
+ virtual bool initPostProcessSupport(PostProcessType pptype);
+
+private:
+ PostProcessShader PostProcessShaderRequested;
+ PostProcessShader PostProcessShaderActive;
+};
+
+int GetNumMipLevels(int w, int h);
+
+// Filter an rgba image with a 2x2 box filter, for mipmaps.
+// Image size must be a power of 2.
+void FilterRgba2x2(const UByte* src, int w, int h, UByte* dest);
+
+}} // OVR::RenderTiny
+
+#endif
diff --git a/Samples/OculusRoomTiny/RenderTiny_GL_Device.cpp b/Samples/OculusRoomTiny/RenderTiny_GL_Device.cpp
new file mode 100644
index 0000000..197c62a
--- /dev/null
+++ b/Samples/OculusRoomTiny/RenderTiny_GL_Device.cpp
@@ -0,0 +1,784 @@
+/************************************************************************************
+
+Filename : RenderTiny_GL_Device.cpp
+Content : RenderDevice implementation for OpenGL (tiny version)
+Created : September 10, 2012
+Authors : Andrew Reisse, Artem Bolgar
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#include "RenderTiny_GL_Device.h"
+#include "Kernel/OVR_Log.h"
+
+namespace OVR { namespace RenderTiny { namespace GL {
+
+
+
+static const char* StdVertexShaderSrc =
+ "uniform mat4 Proj;\n"
+ "uniform mat4 View;\n"
+ "attribute vec4 Position;\n"
+ "attribute vec4 Color;\n"
+ "attribute vec2 TexCoord;\n"
+ "attribute vec3 Normal;\n"
+ "varying vec4 oColor;\n"
+ "varying vec2 oTexCoord;\n"
+ "varying vec3 oNormal;\n"
+ "varying vec3 oVPos;\n"
+ "void main()\n"
+ "{\n"
+ " gl_Position = Proj * (View * Position);\n"
+ " oNormal = vec3(View * vec4(Normal,0));\n"
+ " oVPos = vec3(View * Position);\n"
+ " oTexCoord = TexCoord;\n"
+ " oColor = Color;\n"
+ "}\n";
+
+static const char* DirectVertexShaderSrc =
+ "uniform mat4 View;\n"
+ "attribute vec4 Position;\n"
+ "attribute vec4 Color;\n"
+ "attribute vec2 TexCoord;\n"
+ "attribute vec3 Normal;\n"
+ "varying vec4 oColor;\n"
+ "varying vec2 oTexCoord;\n"
+ "varying vec3 oNormal;\n"
+ "void main()\n"
+ "{\n"
+ " gl_Position = View * Position;\n"
+ " oTexCoord = TexCoord;\n"
+ " oColor = Color;\n"
+ " oNormal = vec3(View * vec4(Normal,0));\n"
+ "}\n";
+
+static const char* SolidFragShaderSrc =
+ "uniform vec4 Color;\n"
+ "void main()\n"
+ "{\n"
+ " gl_FragColor = Color;\n"
+ "}\n";
+
+static const char* GouraudFragShaderSrc =
+ "varying vec4 oColor;\n"
+ "void main()\n"
+ "{\n"
+ " gl_FragColor = oColor;\n"
+ "}\n";
+
+static const char* TextureFragShaderSrc =
+ "uniform sampler2D Texture0;\n"
+ "varying vec4 oColor;\n"
+ "varying vec2 oTexCoord;\n"
+ "void main()\n"
+ "{\n"
+ " gl_FragColor = oColor * texture2D(Texture0, oTexCoord);\n"
+ " if (gl_FragColor.a < 0.4)\n"
+ " discard;\n"
+ "}\n";
+
+#define LIGHTING_COMMON \
+ "uniform vec3 Ambient;\n" \
+ "uniform vec4 LightPos[8];\n" \
+ "uniform vec4 LightColor[8];\n" \
+ "uniform float LightCount;\n" \
+ "varying vec4 oColor;\n" \
+ "varying vec2 oTexCoord;\n" \
+ "varying vec3 oNormal;\n" \
+ "varying vec3 oVPos;\n" \
+ "vec4 DoLight()\n" \
+ "{\n" \
+ " vec3 norm = normalize(oNormal);\n" \
+ " vec3 light = Ambient;\n" \
+ " for (int i = 0; i < int(LightCount); i++)\n" \
+ " {\n" \
+ " vec3 ltp = (LightPos[i].xyz - oVPos);\n" \
+ " float ldist = length(ltp);\n" \
+ " ltp = normalize(ltp);\n" \
+ " light += clamp(LightColor[i].rgb * oColor.rgb * (dot(norm, ltp) / ldist), 0.0,1.0);\n" \
+ " }\n" \
+ " return vec4(light, oColor.a);\n" \
+ "}\n"
+
+static const char* LitSolidFragShaderSrc =
+ LIGHTING_COMMON
+ "void main()\n"
+ "{\n"
+ " gl_FragColor = DoLight() * oColor;\n"
+ "}\n";
+
+static const char* LitTextureFragShaderSrc =
+ "uniform sampler2D Texture0;\n"
+ LIGHTING_COMMON
+ "void main()\n"
+ "{\n"
+ " gl_FragColor = DoLight() * texture2D(Texture0, oTexCoord);\n"
+ "}\n";
+
+static const char* PostProcessVertexShaderSrc =
+ "uniform mat4 View;\n"
+ "uniform mat4 Texm;\n"
+ "attribute vec4 Position;\n"
+ "attribute vec2 TexCoord;\n"
+ "varying vec2 oTexCoord;\n"
+ "void main()\n"
+ "{\n"
+ " gl_Position = View * Position;\n"
+ " oTexCoord = vec2(Texm * vec4(TexCoord,0,1));\n"
+ " oTexCoord.y = 1.0-oTexCoord.y;\n"
+ "}\n";
+
+static const char* PostProcessFragShaderSrc =
+ "uniform vec2 LensCenter;\n"
+ "uniform vec2 ScreenCenter;\n"
+ "uniform vec2 Scale;\n"
+ "uniform vec2 ScaleIn;\n"
+ "uniform vec4 HmdWarpParam;\n"
+ "uniform sampler2D Texture0;\n"
+ "varying vec2 oTexCoord;\n"
+ "\n"
+ "vec2 HmdWarp(vec2 in01)\n"
+ "{\n"
+ " vec2 theta = (in01 - LensCenter) * ScaleIn;\n" // Scales to [-1, 1]
+ " float rSq = theta.x * theta.x + theta.y * theta.y;\n"
+ " vec2 theta1 = theta * (HmdWarpParam.x + HmdWarpParam.y * rSq + "
+ " HmdWarpParam.z * rSq * rSq + HmdWarpParam.w * rSq * rSq * rSq);\n"
+ " return LensCenter + Scale * theta1;\n"
+ "}\n"
+ "void main()\n"
+ "{\n"
+ " vec2 tc = HmdWarp(oTexCoord);\n"
+ " if (!all(equal(clamp(tc, ScreenCenter-vec2(0.25,0.5), ScreenCenter+vec2(0.25,0.5)), tc)))\n"
+ " gl_FragColor = vec4(0);\n"
+ " else\n"
+ " gl_FragColor = texture2D(Texture0, tc);\n"
+ "}\n";
+
+// Shader with lens distortion and chromatic aberration correction.
+static const char* PostProcessFullFragShaderSrc =
+ "uniform vec2 LensCenter;\n"
+ "uniform vec2 ScreenCenter;\n"
+ "uniform vec2 Scale;\n"
+ "uniform vec2 ScaleIn;\n"
+ "uniform vec4 HmdWarpParam;\n"
+ "uniform vec4 ChromAbParam;\n"
+ "uniform sampler2D Texture0;\n"
+ "varying vec2 oTexCoord;\n"
+ "\n"
+ // Scales input texture coordinates for distortion.
+ // ScaleIn maps texture coordinates to Scales to ([-1, 1]), although top/bottom will be
+ // larger due to aspect ratio.
+ "void main()\n"
+ "{\n"
+ " vec2 theta = (oTexCoord - LensCenter) * ScaleIn;\n" // Scales to [-1, 1]
+ " float rSq= theta.x * theta.x + theta.y * theta.y;\n"
+ " vec2 theta1 = theta * (HmdWarpParam.x + HmdWarpParam.y * rSq + "
+ " HmdWarpParam.z * rSq * rSq + HmdWarpParam.w * rSq * rSq * rSq);\n"
+ " \n"
+ " // Detect whether blue texture coordinates are out of range since these will scaled out the furthest.\n"
+ " vec2 thetaBlue = theta1 * (ChromAbParam.z + ChromAbParam.w * rSq);\n"
+ " vec2 tcBlue = LensCenter + Scale * thetaBlue;\n"
+ " if (!all(equal(clamp(tcBlue, ScreenCenter-vec2(0.25,0.5), ScreenCenter+vec2(0.25,0.5)), tcBlue)))\n"
+ " {\n"
+ " gl_FragColor = vec4(0);\n"
+ " return;\n"
+ " }\n"
+ " \n"
+ " // Now do blue texture lookup.\n"
+ " float blue = texture2D(Texture0, tcBlue).b;\n"
+ " \n"
+ " // Do green lookup (no scaling).\n"
+ " vec2 tcGreen = LensCenter + Scale * theta1;\n"
+ " vec4 center = texture2D(Texture0, tcGreen);\n"
+ " \n"
+ " // Do red scale and lookup.\n"
+ " vec2 thetaRed = theta1 * (ChromAbParam.x + ChromAbParam.y * rSq);\n"
+ " vec2 tcRed = LensCenter + Scale * thetaRed;\n"
+ " float red = texture2D(Texture0, tcRed).r;\n"
+ " \n"
+ " gl_FragColor = vec4(red, center.g, blue, 1);\n"
+ "}\n";
+
+static const char* VShaderSrcs[VShader_Count] =
+{
+ DirectVertexShaderSrc,
+ StdVertexShaderSrc,
+ PostProcessVertexShaderSrc
+};
+static const char* FShaderSrcs[FShader_Count] =
+{
+ SolidFragShaderSrc,
+ GouraudFragShaderSrc,
+ TextureFragShaderSrc,
+ PostProcessFragShaderSrc,
+ PostProcessFullFragShaderSrc,
+ LitSolidFragShaderSrc,
+ LitTextureFragShaderSrc
+};
+
+
+
+RenderDevice::RenderDevice(const RendererParams& p)
+{
+ for (int i = 0; i < VShader_Count; i++)
+ VertexShaders[i] = *new Shader(this, Shader_Vertex, VShaderSrcs[i]);
+
+ for (int i = 0; i < FShader_Count; i++)
+ FragShaders[i] = *new Shader(this, Shader_Fragment, FShaderSrcs[i]);
+
+ Ptr<ShaderSet> gouraudShaders = *new ShaderSet();
+ gouraudShaders->SetShader(VertexShaders[VShader_MVP]);
+ gouraudShaders->SetShader(FragShaders[FShader_Gouraud]);
+ DefaultFill = *new ShaderFill(gouraudShaders);
+
+ glGenFramebuffersEXT(1, &CurrentFbo);
+}
+
+Shader *RenderDevice::LoadBuiltinShader(ShaderStage stage, int shader)
+{
+ switch (stage)
+ {
+ case Shader_Vertex: return VertexShaders[shader];
+ case Shader_Fragment: return FragShaders[shader];
+ default:
+ return NULL;
+ }
+}
+
+
+void RenderDevice::BeginRendering()
+{
+ glEnable(GL_DEPTH_TEST);
+ glEnable(GL_CULL_FACE);
+ glFrontFace(GL_CW);
+
+ glLineWidth(3.0f);
+ glEnable(GL_LINE_SMOOTH);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+}
+
+void RenderDevice::SetDepthMode(bool enable, bool write, CompareFunc func)
+{
+ if (enable)
+ {
+ glEnable(GL_DEPTH_TEST);
+ glDepthMask(write);
+ switch (func)
+ {
+ case Compare_Always: glDepthFunc(GL_ALWAYS); break;
+ case Compare_Less: glDepthFunc(GL_LESS); break;
+ case Compare_Greater: glDepthFunc(GL_GREATER); break;
+ default: assert(0);
+ }
+ }
+ else
+ glDisable(GL_DEPTH_TEST);
+}
+
+void RenderDevice::SetRealViewport(const Viewport& vp)
+{
+ int wh;
+ if (CurRenderTarget)
+ wh = CurRenderTarget->Height;
+ else
+ wh = WindowHeight;
+ glViewport(vp.x, wh-vp.y-vp.h, vp.w, vp.h);
+
+ glEnable(GL_SCISSOR_TEST);
+ glScissor(vp.x, wh-vp.y-vp.h, vp.w, vp.h);
+}
+
+void RenderDevice::Clear(float r, float g, float b, float a, float depth)
+{
+ glClearColor(r,g,b,a);
+ glClearDepth(depth);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+}
+
+RBuffer* RenderDevice::GetDepthBuffer(int w, int h, int ms)
+{
+ for (unsigned i = 0; i < DepthBuffers.GetSize(); i++)
+ if (w == DepthBuffers[i]->Width && h == DepthBuffers[i]->Height)// && ms == DepthBuffers[i]->Samples)
+ return DepthBuffers[i];
+
+ //Ptr<Texture> newDepth = *CreateTexture(Texture_Depth|Texture_RenderTarget|ms, w, h, NULL);
+ Ptr<RBuffer> newDepth = *new RBuffer(GL_DEPTH24_STENCIL8, w, h); // combined depth stencil
+ DepthBuffers.PushBack(newDepth);
+ return newDepth.GetPtr();
+}
+
+void RenderDevice::SetRenderTarget(RenderTiny::Texture* color, RenderTiny::Texture*, RenderTiny::Texture* stencil)
+{
+ OVR_UNUSED(stencil);
+
+ CurRenderTarget = (Texture*)color;
+ if (color == NULL)
+ {
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+ return;
+ }
+ //if (depth == NULL)
+ RBuffer* depth = GetDepthBuffer(color->GetWidth(), color->GetHeight(), 0); //CurRenderTarget->Samples);
+
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, CurrentFbo);
+ glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, ((Texture*)color)->TexId, 0);
+ if (depth)
+ //glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, ((Texture*)depth)->TexId, 0);
+ glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, ((RBuffer*)depth)->BufId);
+ else
+ glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, 0);
+
+ GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
+ if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
+ OVR_DEBUG_LOG(("framebuffer not complete: %x", status));
+}
+
+
+void RenderDevice::SetWorldUniforms(const Matrix4f& proj)
+{
+ Proj = proj.Transposed();
+}
+
+void RenderDevice::SetTexture(RenderTiny::ShaderStage, int slot, const Texture* t)
+{
+ glActiveTexture(GL_TEXTURE0 + slot);
+ glBindTexture(GL_TEXTURE_2D, ((Texture*)t)->TexId);
+ glActiveTexture(GL_TEXTURE0);
+}
+
+Buffer* RenderDevice::CreateBuffer()
+{
+ return new Buffer(this);
+}
+
+void RenderDevice::Render(const Matrix4f& matrix, Model* model)
+{
+ // Store data in buffers if not already
+ if (!model->VertexBuffer)
+ {
+ Ptr<RenderTiny::Buffer> vb = *CreateBuffer();
+ vb->Data(Buffer_Vertex, &model->Vertices[0], model->Vertices.GetSize() * sizeof(Vertex));
+ model->VertexBuffer = vb;
+ }
+ if (!model->IndexBuffer)
+ {
+ Ptr<RenderTiny::Buffer> ib = *CreateBuffer();
+ ib->Data(Buffer_Index, &model->Indices[0], model->Indices.GetSize() * 2);
+ model->IndexBuffer = ib;
+ }
+
+ Render(model->Fill ? (const ShaderFill*)model->Fill : (const ShaderFill*)DefaultFill,
+ model->VertexBuffer, model->IndexBuffer,
+ matrix, 0, (int)model->Indices.GetSize(), model->GetPrimType());
+}
+
+void RenderDevice::Render(const ShaderFill* fill, RenderTiny::Buffer* vertices, RenderTiny::Buffer* indices,
+ const Matrix4f& matrix, int offset, int count, PrimitiveType rprim)
+{
+ ShaderSet* shaders = (ShaderSet*) ((ShaderFill*)fill)->GetShaders();
+
+ GLenum prim;
+ switch (rprim)
+ {
+ case Prim_Triangles:
+ prim = GL_TRIANGLES;
+ break;
+ case Prim_Lines:
+ prim = GL_LINES;
+ break;
+ case Prim_TriangleStrip:
+ prim = GL_TRIANGLE_STRIP;
+ break;
+ default:
+ assert(0);
+ return;
+ }
+
+ fill->Set();
+ if (shaders->ProjLoc >= 0)
+ glUniformMatrix4fv(shaders->ProjLoc, 1, 0, &Proj.M[0][0]);
+ if (shaders->ViewLoc >= 0)
+ glUniformMatrix4fv(shaders->ViewLoc, 1, 0, &matrix.Transposed().M[0][0]);
+
+ if (shaders->UsesLighting && Lighting->Version != shaders->LightingVer)
+ {
+ shaders->LightingVer = Lighting->Version;
+ Lighting->Set(shaders);
+ }
+
+ glBindBuffer(GL_ARRAY_BUFFER, ((Buffer*)vertices)->GLBuffer);
+ for (int i = 0; i < 4; i++)
+ glEnableVertexAttribArray(i);
+
+ glVertexAttribPointer(0, 3, GL_FLOAT, false, sizeof(Vertex), (char*)offset + offsetof(Vertex, Pos));
+ glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, true, sizeof(Vertex), (char*)offset + offsetof(Vertex, C));
+ glVertexAttribPointer(2, 2, GL_FLOAT, false, sizeof(Vertex), (char*)offset + offsetof(Vertex, U));
+ glVertexAttribPointer(3, 3, GL_FLOAT, false, sizeof(Vertex), (char*)offset + offsetof(Vertex, Norm));
+
+ if (indices)
+ {
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ((Buffer*)indices)->GLBuffer);
+ glDrawElements(prim, count, GL_UNSIGNED_SHORT, NULL);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ }
+ else
+ {
+ glDrawArrays(prim, 0, count);
+ }
+
+ for (int i = 0; i < 4; i++)
+ glDisableVertexAttribArray(i);
+}
+
+void RenderDevice::SetLighting(const LightingParams* lt)
+{
+ Lighting = lt;
+}
+
+Buffer::~Buffer()
+{
+ if (GLBuffer)
+ glDeleteBuffers(1, &GLBuffer);
+}
+
+bool Buffer::Data(int use, const void* buffer, size_t size)
+{
+ switch (use & Buffer_TypeMask)
+ {
+ case Buffer_Index: Use = GL_ELEMENT_ARRAY_BUFFER; break;
+ default: Use = GL_ARRAY_BUFFER; break;
+ }
+
+ if (!GLBuffer)
+ glGenBuffers(1, &GLBuffer);
+
+ int mode = GL_DYNAMIC_DRAW;
+ if (use & Buffer_ReadOnly)
+ mode = GL_STATIC_DRAW;
+
+ glBindBuffer(Use, GLBuffer);
+ glBufferData(Use, size, buffer, mode);
+ glBindBuffer(Use, 0);
+ return 1;
+}
+
+void* Buffer::Map(size_t start, size_t size, int flags)
+{
+ int mode = GL_WRITE_ONLY;
+ //if (flags & Map_Unsynchronized)
+ // mode |= GL_MAP_UNSYNCHRONIZED;
+
+ glBindBuffer(Use, GLBuffer);
+ void* v = glMapBuffer(Use, mode);
+ glBindBuffer(Use, 0);
+ return v;
+}
+
+bool Buffer::Unmap(void*)
+{
+ glBindBuffer(Use, GLBuffer);
+ int r = glUnmapBuffer(Use);
+ glBindBuffer(Use, 0);
+ return r;
+}
+
+bool Shader::Compile(const char* src)
+{
+ if (!GLShader)
+ GLShader = glCreateShader(GLStage());
+
+ glShaderSource(GLShader, 1, &src, 0);
+ glCompileShader(GLShader);
+ GLint r;
+ glGetShaderiv(GLShader, GL_COMPILE_STATUS, &r);
+ if (!r)
+ {
+ GLchar msg[1024];
+ glGetShaderInfoLog(GLShader, sizeof(msg), 0, msg);
+ if (msg[0])
+ OVR_DEBUG_LOG(("Compiling shader\n%s\nfailed: %s\n", src, msg));
+ if (!r)
+ return 0;
+ }
+ return 1;
+}
+
+ShaderSet::ShaderSet()
+{
+ Prog = glCreateProgram();
+}
+ShaderSet::~ShaderSet()
+{
+ glDeleteProgram(Prog);
+}
+
+bool ShaderSet::Link()
+{
+ glBindAttribLocation(Prog, 0, "Position");
+ glBindAttribLocation(Prog, 1, "Color");
+ glBindAttribLocation(Prog, 2, "TexCoord");
+ glBindAttribLocation(Prog, 3, "Normal");
+
+ glLinkProgram(Prog);
+ GLint r;
+ glGetProgramiv(Prog, GL_LINK_STATUS, &r);
+ if (!r)
+ {
+ GLchar msg[1024];
+ glGetProgramInfoLog(Prog, sizeof(msg), 0, msg);
+ OVR_DEBUG_LOG(("Linking shaders failed: %s\n", msg));
+ if (!r)
+ return 0;
+ }
+ glUseProgram(Prog);
+
+ UniformInfo.Clear();
+ LightingVer = 0;
+ UsesLighting = 0;
+ GLuint i = 0;
+ for(;; i++)
+ {
+ GLsizei namelen;
+ GLint size = 0;
+ GLenum type;
+ GLchar name[32];
+ glGetActiveUniform(Prog, i, sizeof(name), &namelen, &size, &type, name);
+ if (size)
+ {
+ int l = glGetUniformLocation(Prog, name);
+ char *np = name;
+ while (*np)
+ {
+ if (*np == '[')
+ *np = 0;
+ np++;
+ }
+ Uniform u;
+ u.Name = name;
+ u.Location = l;
+ u.Size = size;
+ switch (type)
+ {
+ case GL_FLOAT: u.Type = 1; break;
+ case GL_FLOAT_VEC2: u.Type = 2; break;
+ case GL_FLOAT_VEC3: u.Type = 3; break;
+ case GL_FLOAT_VEC4: u.Type = 4; break;
+ case GL_FLOAT_MAT4: u.Type = 16; break;
+ default:
+ continue;
+ }
+ UniformInfo.PushBack(u);
+ if (!strcmp(name, "LightCount"))
+ UsesLighting = 1;
+ }
+ else
+ break;
+ }
+
+ ProjLoc = glGetUniformLocation(Prog, "Proj");
+ ViewLoc = glGetUniformLocation(Prog, "View");
+ for (int i = 0; i < 8; i++)
+ {
+ char texv[32];
+ sprintf(texv, "Texture%d", i);
+ TexLoc[i] = glGetUniformLocation(Prog, texv);
+ if (TexLoc[i] < 0)
+ break;
+
+ glUniform1i(TexLoc[i], i);
+ }
+ if (UsesLighting)
+ OVR_ASSERT(ProjLoc >= 0 && ViewLoc >= 0);
+ return 1;
+}
+
+void ShaderSet::Set(PrimitiveType) const
+{
+ glUseProgram(Prog);
+}
+
+bool ShaderSet::SetUniform(const char* name, int n, const float* v)
+{
+ for (int i = 0; i < UniformInfo.GetSize(); i++)
+ if (!strcmp(UniformInfo[i].Name.ToCStr(), name))
+ {
+ OVR_ASSERT(UniformInfo[i].Location >= 0);
+ glUseProgram(Prog);
+ switch (UniformInfo[i].Type)
+ {
+ case 1: glUniform1fv(UniformInfo[i].Location, n, v); break;
+ case 2: glUniform2fv(UniformInfo[i].Location, n/2, v); break;
+ case 3: glUniform3fv(UniformInfo[i].Location, n/3, v); break;
+ case 4: glUniform4fv(UniformInfo[i].Location, n/4, v); break;
+ default: OVR_ASSERT(0);
+ }
+ return 1;
+ }
+
+ OVR_DEBUG_LOG(("Warning: uniform %s not present in selected shader", name));
+ return 0;
+}
+
+bool ShaderSet::SetUniform4x4f(const char* name, const Matrix4f& m)
+{
+ for (int i = 0; i < UniformInfo.GetSize(); i++)
+ if (!strcmp(UniformInfo[i].Name.ToCStr(), name))
+ {
+ glUseProgram(Prog);
+ glUniformMatrix4fv(UniformInfo[i].Location, 1, 1, &m.M[0][0]);
+ return 1;
+ }
+
+ OVR_DEBUG_LOG(("Warning: uniform %s not present in selected shader", name));
+ return 0;
+}
+
+Texture::Texture(RenderDevice* r, int w, int h) : Ren(r), Width(w), Height(h)
+{
+ glGenTextures(1, &TexId);
+}
+
+Texture::~Texture()
+{
+ if (TexId)
+ glDeleteTextures(1, &TexId);
+}
+
+void Texture::Set(int slot, RenderTiny::ShaderStage stage) const
+{
+ Ren->SetTexture(stage, slot, this);
+}
+
+void Texture::SetSampleMode(int sm)
+{
+ glBindTexture(GL_TEXTURE_2D, TexId);
+ switch (sm & Sample_FilterMask)
+ {
+ case Sample_Linear:
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 0);
+ break;
+
+ case Sample_Anisotropic:
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 8);
+ break;
+
+ case Sample_Nearest:
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 0);
+ break;
+ }
+
+ switch (sm & Sample_AddressMask)
+ {
+ case Sample_Repeat:
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ break;
+
+ case Sample_Clamp:
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ break;
+
+ case Sample_ClampBorder:
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
+ break;
+ }
+ glBindTexture(GL_TEXTURE_2D, 0);
+}
+
+Texture* RenderDevice::CreateTexture(int format, int width, int height, const void* data, int mipcount)
+{
+ GLenum glformat, gltype = GL_UNSIGNED_BYTE;
+ switch(format & Texture_TypeMask)
+ {
+ case Texture_RGBA: glformat = GL_RGBA; break;
+ case Texture_Depth: glformat = GL_DEPTH; gltype = GL_DEPTH_COMPONENT; break;
+ default:
+ return NULL;
+ }
+ Texture* NewTex = new Texture(this, width, height);
+ glBindTexture(GL_TEXTURE_2D, NewTex->TexId);
+ glGetError();
+
+ glTexImage2D(GL_TEXTURE_2D, 0, glformat, width, height, 0, glformat, gltype, data);
+ OVR_ASSERT(!glGetError());
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ if (format == (Texture_RGBA|Texture_GenMipmaps)) // not render target
+ {
+ int srcw = width, srch = height;
+ int level = 0;
+ UByte* mipmaps = NULL;
+ do
+ {
+ level++;
+ int mipw = srcw >> 1; if (mipw < 1) mipw = 1;
+ int miph = srch >> 1; if (miph < 1) miph = 1;
+ if (mipmaps == NULL)
+ mipmaps = (UByte*)OVR_ALLOC(mipw * miph * 4);
+ FilterRgba2x2(level == 1 ? (const UByte*)data : mipmaps, srcw, srch, mipmaps);
+ glTexImage2D(GL_TEXTURE_2D, level, glformat, mipw, miph, 0, glformat, gltype, mipmaps);
+ OVR_ASSERT(!glGetError());
+ srcw = mipw;
+ srch = miph;
+ } while (srcw > 1 || srch > 1);
+ if (mipmaps)
+ OVR_FREE(mipmaps);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, level);
+ OVR_ASSERT(!glGetError());
+ }
+ else
+ {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, mipcount-1);
+ OVR_ASSERT(!glGetError());
+ }
+
+ OVR_ASSERT(!glGetError());
+ glBindTexture(GL_TEXTURE_2D, 0);
+ return NewTex;
+}
+
+RBuffer::RBuffer(GLenum format, GLint w, GLint h)
+{
+ Width = w;
+ Height = h;
+ glGenRenderbuffersEXT(1, &BufId);
+ glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, BufId);
+ glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, format, w, h);
+ glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
+}
+
+RBuffer::~RBuffer()
+{
+ glDeleteRenderbuffersEXT(1, &BufId);
+}
+
+}}}
diff --git a/Samples/OculusRoomTiny/RenderTiny_GL_Device.h b/Samples/OculusRoomTiny/RenderTiny_GL_Device.h
new file mode 100644
index 0000000..feb504f
--- /dev/null
+++ b/Samples/OculusRoomTiny/RenderTiny_GL_Device.h
@@ -0,0 +1,228 @@
+/************************************************************************************
+
+Filename : RenderTiny_GL_Device.h
+Content : RenderDevice implementation header for OpenGL (tiny version)
+Created : September 10, 2012
+Authors : Andrew Reisse, Artem Bolgar
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#ifndef OVR_Render_GL_Device_h
+#define OVR_Render_GL_Device_h
+
+#include "RenderTiny_Device.h"
+
+#if defined(OVR_OS_WIN32)
+#include <Windows.h>
+#endif
+
+#if defined(OVR_OS_MAC)
+#include <OpenGL/gl.h>
+#include <OpenGL/glext.h>
+#else
+#define GL_GLEXT_PROTOTYPES
+#include <GL/gl.h>
+#include <GL/glext.h>
+#endif
+
+namespace OVR { namespace RenderTiny { namespace GL {
+
+class RenderDevice;
+
+class Buffer : public RenderTiny::Buffer
+{
+public:
+ RenderDevice* Ren;
+ size_t Size;
+ GLenum Use;
+ GLuint GLBuffer;
+
+public:
+ Buffer(RenderDevice* r) : Ren(r), Size(0), Use(0), GLBuffer(0) {}
+ ~Buffer();
+
+ GLuint GetBuffer() { return GLBuffer; }
+
+ virtual size_t GetSize() { return Size; }
+ virtual void* Map(size_t start, size_t size, int flags = 0);
+ virtual bool Unmap(void *m);
+ virtual bool Data(int use, const void* buffer, size_t size);
+};
+
+class Texture : public RenderTiny::Texture
+{
+public:
+ RenderDevice* Ren;
+ GLuint TexId;
+ int Width, Height;
+
+ Texture(RenderDevice* r, int w, int h);
+ ~Texture();
+
+ virtual int GetWidth() const { return Width; }
+ virtual int GetHeight() const { return Height; }
+
+ virtual void SetSampleMode(int);
+
+ virtual void Set(int slot, ShaderStage stage = Shader_Fragment) const;
+};
+
+class Shader : public RenderTiny::Shader
+{
+public:
+ GLuint GLShader;
+
+ Shader(RenderDevice*, ShaderStage st, GLuint s) : RenderTiny::Shader(st), GLShader(s) {}
+ Shader(RenderDevice*, ShaderStage st, const char* src) : RenderTiny::Shader(st), GLShader(0)
+ {
+ Compile(src);
+ }
+ ~Shader()
+ {
+ if (GLShader)
+ glDeleteShader(GLShader);
+ }
+ bool Compile(const char* src);
+
+ GLenum GLStage() const
+ {
+ switch (Stage)
+ {
+ default: OVR_ASSERT(0); return GL_NONE;
+ case Shader_Vertex: return GL_VERTEX_SHADER;
+ case Shader_Fragment: return GL_FRAGMENT_SHADER;
+ }
+ }
+
+ //void Set(PrimitiveType prim) const;
+ //void SetUniformBuffer(Render::Buffer* buffers, int i = 0);
+};
+
+class ShaderSet : public RenderTiny::ShaderSet
+{
+public:
+ GLuint Prog;
+
+ struct Uniform
+ {
+ String Name;
+ int Location, Size;
+ int Type; // currently number of floats in vector
+ };
+ Array<Uniform> UniformInfo;
+
+ int ProjLoc, ViewLoc;
+ int TexLoc[8];
+ bool UsesLighting;
+ int LightingVer;
+
+ ShaderSet();
+ ~ShaderSet();
+
+ virtual void SetShader(RenderTiny::Shader *s)
+ {
+ Shaders[s->GetStage()] = s;
+ Shader* gls = (Shader*)s;
+ glAttachShader(Prog, gls->GLShader);
+ if (Shaders[Shader_Vertex] && Shaders[Shader_Fragment])
+ Link();
+ }
+ virtual void UnsetShader(int stage)
+ {
+ Shader* gls = (Shader*)(RenderTiny::Shader*)Shaders[stage];
+ if (gls)
+ glDetachShader(Prog, gls->GLShader);
+ Shaders[stage] = NULL;
+ Link();
+ }
+
+ virtual void Set(PrimitiveType prim) const;
+
+ // Set a uniform (other than the standard matrices). It is undefined whether the
+ // uniforms from one shader occupy the same space as those in other shaders
+ // (unless a buffer is used, then each buffer is independent).
+ virtual bool SetUniform(const char* name, int n, const float* v);
+ virtual bool SetUniform4x4f(const char* name, const Matrix4f& m);
+
+ bool Link();
+};
+
+ class RBuffer : public RefCountBase<RBuffer>
+{
+ public:
+ int Width, Height;
+ GLuint BufId;
+
+ RBuffer(GLenum format, GLint w, GLint h);
+ ~RBuffer();
+};
+
+class RenderDevice : public RenderTiny::RenderDevice
+{
+ Ptr<Shader> VertexShaders[VShader_Count];
+ Ptr<Shader> FragShaders[FShader_Count];
+
+ Ptr<ShaderFill> DefaultFill;
+
+ Matrix4f Proj;
+
+ Ptr<Texture> CurRenderTarget;
+ Array<Ptr<RBuffer> > DepthBuffers;
+ GLuint CurrentFbo;
+
+ const LightingParams* Lighting;
+
+
+public:
+ RenderDevice(const RendererParams& p);
+
+ virtual void SetRealViewport(const Viewport& vp);
+
+ //virtual void SetScissor(int x, int y, int w, int h);
+
+ virtual void Clear(float r = 0, float g = 0, float b = 0, float a = 1, float depth = 1);
+ virtual void Rect(float left, float top, float right, float bottom) { OVR_UNUSED4(left,top,right,bottom); }
+
+ virtual void BeginRendering();
+ virtual void SetDepthMode(bool enable, bool write, CompareFunc func = Compare_Less);
+ virtual void SetWorldUniforms(const Matrix4f& proj);
+
+ RBuffer* GetDepthBuffer(int w, int h, int ms);
+
+ virtual void SetRenderTarget(RenderTiny::Texture* color,
+ RenderTiny::Texture* depth = NULL, RenderTiny::Texture* stencil = NULL);
+
+ virtual void SetLighting(const LightingParams* lt);
+
+ virtual void Render(const Matrix4f& matrix, Model* model);
+ virtual void Render(const ShaderFill* fill, RenderTiny::Buffer* vertices, RenderTiny::Buffer* indices,
+ const Matrix4f& matrix, int offset, int count, PrimitiveType prim = Prim_Triangles);
+
+ virtual Buffer* CreateBuffer();
+ virtual Texture* CreateTexture(int format, int width, int height, const void* data, int mipcount=1);
+ virtual ShaderSet* CreateShaderSet() { return new ShaderSet; }
+
+ virtual ShaderFill *CreateSimpleFill() { return DefaultFill; }
+
+ virtual Shader *LoadBuiltinShader(ShaderStage stage, int shader);
+
+ void SetTexture(RenderTiny::ShaderStage, int slot, const Texture* t);
+};
+
+}}}
+
+#endif
diff --git a/Samples/OculusRoomTiny/Win32_OculusRoomTiny.cpp b/Samples/OculusRoomTiny/Win32_OculusRoomTiny.cpp
new file mode 100644
index 0000000..2e6dd3e
--- /dev/null
+++ b/Samples/OculusRoomTiny/Win32_OculusRoomTiny.cpp
@@ -0,0 +1,712 @@
+/************************************************************************************
+
+Filename : Win32_OculusRoomTiny.cpp
+Content : First-person view test application for Oculus Rift
+Created : October 4, 2012
+Authors : Michael Antonov, Andrew Reisse
+
+Copyright : Copyright 2012 Oculus, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+*************************************************************************************/
+
+#include "Win32_OculusRoomTiny.h"
+#include "RenderTiny_D3D1X_Device.h"
+
+//-------------------------------------------------------------------------------------
+// ***** OculusRoomTiny Class
+
+// Static pApp simplifies routing the window function.
+OculusRoomTinyApp* OculusRoomTinyApp::pApp = 0;
+
+
+OculusRoomTinyApp::OculusRoomTinyApp(HINSTANCE hinst)
+ : pRender(0),
+ LastUpdate(0),
+
+ // Win32
+ hWnd(NULL),
+ hInstance(hinst), Quit(0), MouseCaptured(true),
+ hXInputModule(0), pXInputGetState(0),
+
+ // Initial location
+ EyePos(0.0f, 1.6f, -5.0f),
+ EyeYaw(YawInitial), EyePitch(0), EyeRoll(0),
+ LastSensorYaw(0),
+ SConfig(),
+ PostProcess(PostProcess_Distortion),
+ ShiftDown(false),
+ ControlDown(false)
+{
+ pApp = this;
+
+ Width = 1280;
+ Height = 800;
+
+ StartupTicks = OVR::Timer::GetTicks();
+ LastPadPacketNo = 0;
+
+ MoveForward = MoveBack = MoveLeft = MoveRight = 0;
+ GamepadMove = Vector3f(0);
+ GamepadRotate = Vector3f(0);
+}
+
+OculusRoomTinyApp::~OculusRoomTinyApp()
+{
+ RemoveHandlerFromDevices();
+ pSensor.Clear();
+ pHMD.Clear();
+ destroyWindow();
+ pApp = 0;
+}
+
+
+int OculusRoomTinyApp::OnStartup(const char* args)
+{
+ OVR_UNUSED(args);
+
+
+ // *** Oculus HMD & Sensor Initialization
+
+ // Create DeviceManager and first available HMDDevice from it.
+ // Sensor object is created from the HMD, to ensure that it is on the
+ // correct device.
+
+ pManager = *DeviceManager::Create();
+
+ // We'll handle it's messages in this case.
+ pManager->SetMessageHandler(this);
+
+
+ int detectionResult = IDCONTINUE;
+ const char* detectionMessage;
+
+ do
+ {
+ // Release Sensor/HMD in case this is a retry.
+ pSensor.Clear();
+ pHMD.Clear();
+ RenderParams.MonitorName.Clear();
+
+ pHMD = *pManager->EnumerateDevices<HMDDevice>().CreateDevice();
+ if (pHMD)
+ {
+ pSensor = *pHMD->GetSensor();
+
+ // This will initialize HMDInfo with information about configured IPD,
+ // screen size and other variables needed for correct projection.
+ // We pass HMD DisplayDeviceName into the renderer to select the
+ // correct monitor in full-screen mode.
+ if (pHMD->GetDeviceInfo(&HMDInfo))
+ {
+ RenderParams.MonitorName = HMDInfo.DisplayDeviceName;
+ RenderParams.DisplayId = HMDInfo.DisplayId;
+ SConfig.SetHMDInfo(HMDInfo);
+ }
+ }
+ else
+ {
+ // If we didn't detect an HMD, try to create the sensor directly.
+ // This is useful for debugging sensor interaction; it is not needed in
+ // a shipping app.
+ pSensor = *pManager->EnumerateDevices<SensorDevice>().CreateDevice();
+ }
+
+
+ // If there was a problem detecting the Rift, display appropriate message.
+ detectionResult = IDCONTINUE;
+
+ if (!pHMD && !pSensor)
+ detectionMessage = "Oculus Rift not detected.";
+ else if (!pHMD)
+ detectionMessage = "Oculus Sensor detected; HMD Display not detected.";
+ else if (!pSensor)
+ detectionMessage = "Oculus HMD Display detected; Sensor not detected.";
+ else if (HMDInfo.DisplayDeviceName[0] == '\0')
+ detectionMessage = "Oculus Sensor detected; HMD display EDID not detected.";
+ else
+ detectionMessage = 0;
+
+ if (detectionMessage)
+ {
+ String messageText(detectionMessage);
+ messageText += "\n\n"
+ "Press 'Try Again' to run retry detection.\n"
+ "Press 'Continue' to run full-screen anyway.";
+
+ detectionResult = ::MessageBoxA(0, messageText.ToCStr(), "Oculus Rift Detection",
+ MB_CANCELTRYCONTINUE|MB_ICONWARNING);
+
+ if (detectionResult == IDCANCEL)
+ return 1;
+ }
+
+ } while (detectionResult != IDCONTINUE);
+
+
+ if (HMDInfo.HResolution > 0)
+ {
+ Width = HMDInfo.HResolution;
+ Height = HMDInfo.VResolution;
+ }
+
+
+ if (!setupWindow())
+ return 1;
+
+ if (pSensor)
+ {
+ // We need to attach sensor to SensorFusion object for it to receive
+ // body frame messages and update orientation. SFusion.GetOrientation()
+ // is used in OnIdle() to orient the view.
+ SFusion.AttachToSensor(pSensor);
+ SFusion.SetDelegateMessageHandler(this);
+ SFusion.SetPredictionEnabled(true);
+ }
+
+
+ // *** Initialize Rendering
+
+ // Enable multi-sampling by default.
+ RenderParams.Multisample = 4;
+ RenderParams.Fullscreen = true;
+
+ // Setup Graphics.
+ pRender = *RenderTiny::D3D10::RenderDevice::CreateDevice(RenderParams, (void*)hWnd);
+ if (!pRender)
+ return 1;
+
+
+ // *** Configure Stereo settings.
+
+ SConfig.SetFullViewport(Viewport(0,0, Width, Height));
+ SConfig.SetStereoMode(Stereo_LeftRight_Multipass);
+
+ // Configure proper Distortion Fit.
+ // For 7" screen, fit to touch left side of the view, leaving a bit of invisible
+ // screen on the top (saves on rendering cost).
+ // For smaller screens (5.5"), fit to the top.
+ if (HMDInfo.HScreenSize > 0.0f)
+ {
+ if (HMDInfo.HScreenSize > 0.140f) // 7"
+ SConfig.SetDistortionFitPointVP(-1.0f, 0.0f);
+ else
+ SConfig.SetDistortionFitPointVP(0.0f, 1.0f);
+ }
+
+ pRender->SetSceneRenderScale(SConfig.GetDistortionScale());
+
+ SConfig.Set2DAreaFov(DegreeToRad(85.0f));
+
+
+ // *** Populate Room Scene
+
+ // This creates lights and models.
+ PopulateRoomScene(&Scene, pRender);
+
+
+ LastUpdate = GetAppTime();
+ return 0;
+}
+
+void OculusRoomTinyApp::OnMessage(const Message& msg)
+{
+ if (msg.Type == Message_DeviceAdded && msg.pDevice == pManager)
+ {
+ LogText("DeviceManager reported device added.\n");
+ }
+ else if (msg.Type == Message_DeviceRemoved && msg.pDevice == pManager)
+ {
+ LogText("DeviceManager reported device removed.\n");
+ }
+ else if (msg.Type == Message_DeviceAdded && msg.pDevice == pSensor)
+ {
+ LogText("Sensor reported device added.\n");
+ }
+ else if (msg.Type == Message_DeviceRemoved && msg.pDevice == pSensor)
+ {
+ LogText("Sensor reported device removed.\n");
+ }
+}
+
+
+void OculusRoomTinyApp::OnGamepad(float padLx, float padLy, float padRx, float padRy)
+{
+ GamepadMove = Vector3f(padLx * padLx * (padLx > 0 ? 1 : -1),
+ 0,
+ padLy * padLy * (padLy > 0 ? -1 : 1));
+ GamepadRotate = Vector3f(2 * padRx, -2 * padRy, 0);
+}
+
+void OculusRoomTinyApp::OnMouseMove(int x, int y, int modifiers)
+{
+ OVR_UNUSED(modifiers);
+
+ // Mouse motion here is always relative.
+ int dx = x, dy = y;
+ const float maxPitch = ((3.1415f/2)*0.98f);
+
+ // Apply to rotation. Subtract for right body frame rotation,
+ // since yaw rotation is positive CCW when looking down on XZ plane.
+ EyeYaw -= (Sensitivity * dx)/ 360.0f;
+
+ if (!pSensor)
+ {
+ EyePitch -= (Sensitivity * dy)/ 360.0f;
+
+ if (EyePitch > maxPitch)
+ EyePitch = maxPitch;
+ if (EyePitch < -maxPitch)
+ EyePitch = -maxPitch;
+ }
+}
+
+void OculusRoomTinyApp::OnKey(unsigned vk, bool down)
+{
+ switch (vk)
+ {
+ case 'Q':
+ if (down && ControlDown)
+ Quit = true;
+ break;
+ case VK_ESCAPE:
+ if (!down)
+ Quit = true;
+ break;
+
+ // Handle player movement keys.
+ // We just update movement state here, while the actual translation is done in OnIdle()
+ // based on time.
+ case 'W': MoveForward = down ? (MoveForward | 1) : (MoveForward & ~1); break;
+ case 'S': MoveBack = down ? (MoveBack | 1) : (MoveBack & ~1); break;
+ case 'A': MoveLeft = down ? (MoveLeft | 1) : (MoveLeft & ~1); break;
+ case 'D': MoveRight = down ? (MoveRight | 1) : (MoveRight & ~1); break;
+ case VK_UP: MoveForward = down ? (MoveForward | 2) : (MoveForward & ~2); break;
+ case VK_DOWN: MoveBack = down ? (MoveBack | 2) : (MoveBack & ~2); break;
+
+ case 'R':
+ SFusion.Reset();
+ break;
+
+ case 'P':
+ if (down)
+ {
+ // Toggle chromatic aberration correction on/off.
+ RenderDevice::PostProcessShader shader = pRender->GetPostProcessShader();
+
+ if (shader == RenderDevice::PostProcessShader_Distortion)
+ {
+ pRender->SetPostProcessShader(RenderDevice::PostProcessShader_DistortionAndChromAb);
+ }
+ else if (shader == RenderDevice::PostProcessShader_DistortionAndChromAb)
+ {
+ pRender->SetPostProcessShader(RenderDevice::PostProcessShader_Distortion);
+ }
+ else
+ OVR_ASSERT(false);
+ }
+ break;
+
+ // Switch rendering modes/distortion.
+ case VK_F1:
+ SConfig.SetStereoMode(Stereo_None);
+ PostProcess = PostProcess_None;
+ break;
+ case VK_F2:
+ SConfig.SetStereoMode(Stereo_LeftRight_Multipass);
+ PostProcess = PostProcess_None;
+ break;
+ case VK_F3:
+ SConfig.SetStereoMode(Stereo_LeftRight_Multipass);
+ PostProcess = PostProcess_Distortion;
+ break;
+
+ // Stereo IPD adjustments, in meter (default IPD is 64mm).
+ case VK_OEM_PLUS:
+ case VK_INSERT:
+ if (down)
+ SConfig.SetIPD(SConfig.GetIPD() + 0.0005f * (ShiftDown ? 5.0f : 1.0f));
+ break;
+ case VK_OEM_MINUS:
+ case VK_DELETE:
+ if (down)
+ SConfig.SetIPD(SConfig.GetIPD() - 0.0005f * (ShiftDown ? 5.0f : 1.0f));
+ break;
+
+ // Holding down Shift key accelerates adjustment velocity.
+ case VK_SHIFT:
+ ShiftDown = down;
+ break;
+ case VK_CONTROL:
+ ControlDown = down;
+ break;
+ }
+}
+
+
+void OculusRoomTinyApp::OnIdle()
+{
+ double curtime = GetAppTime();
+ float dt = float(curtime - LastUpdate);
+ LastUpdate = curtime;
+
+
+ // Handle Sensor motion.
+ // We extract Yaw, Pitch, Roll instead of directly using the orientation
+ // to allow "additional" yaw manipulation with mouse/controller.
+ if (pSensor)
+ {
+ Quatf hmdOrient = SFusion.GetOrientation();
+ float yaw = 0.0f;
+
+ hmdOrient.GetEulerAngles<Axis_Y, Axis_X, Axis_Z>(&yaw, &EyePitch, &EyeRoll);
+
+ EyeYaw += (yaw - LastSensorYaw);
+ LastSensorYaw = yaw;
+ }
+
+
+ // Gamepad rotation.
+ EyeYaw -= GamepadRotate.x * dt;
+
+ if (!pSensor)
+ {
+ // Allow gamepad to look up/down, but only if there is no Rift sensor.
+ EyePitch -= GamepadRotate.y * dt;
+
+ const float maxPitch = ((3.1415f/2)*0.98f);
+ if (EyePitch > maxPitch)
+ EyePitch = maxPitch;
+ if (EyePitch < -maxPitch)
+ EyePitch = -maxPitch;
+ }
+
+ // Handle keyboard movement.
+ // This translates EyePos based on Yaw vector direction and keys pressed.
+ // Note that Pitch and Roll do not affect movement (they only affect view).
+ if (MoveForward || MoveBack || MoveLeft || MoveRight)
+ {
+ Vector3f localMoveVector(0,0,0);
+ Matrix4f yawRotate = Matrix4f::RotationY(EyeYaw);
+
+ if (MoveForward)
+ localMoveVector = ForwardVector;
+ else if (MoveBack)
+ localMoveVector = -ForwardVector;
+
+ if (MoveRight)
+ localMoveVector += RightVector;
+ else if (MoveLeft)
+ localMoveVector -= RightVector;
+
+ // Normalize vector so we don't move faster diagonally.
+ localMoveVector.Normalize();
+ Vector3f orientationVector = yawRotate.Transform(localMoveVector);
+ orientationVector *= MoveSpeed * dt * (ShiftDown ? 3.0f : 1.0f);
+
+ EyePos += orientationVector;
+ }
+
+ else if (GamepadMove.LengthSq() > 0)
+ {
+ Matrix4f yawRotate = Matrix4f::RotationY(EyeYaw);
+ Vector3f orientationVector = yawRotate.Transform(GamepadMove);
+ orientationVector *= MoveSpeed * dt;
+ EyePos += orientationVector;
+ }
+
+
+ // Rotate and position View Camera, using YawPitchRoll in BodyFrame coordinates.
+ //
+ Matrix4f rollPitchYaw = Matrix4f::RotationY(EyeYaw) * Matrix4f::RotationX(EyePitch) *
+ Matrix4f::RotationZ(EyeRoll);
+ Vector3f up = rollPitchYaw.Transform(UpVector);
+ Vector3f forward = rollPitchYaw.Transform(ForwardVector);
+
+
+ // Minimal head modelling.
+ float headBaseToEyeHeight = 0.15f; // Vertical height of eye from base of head
+ float headBaseToEyeProtrusion = 0.09f; // Distance forward of eye from base of head
+
+ Vector3f eyeCenterInHeadFrame(0.0f, headBaseToEyeHeight, -headBaseToEyeProtrusion);
+ Vector3f shiftedEyePos = EyePos + rollPitchYaw.Transform(eyeCenterInHeadFrame);
+ shiftedEyePos.y -= eyeCenterInHeadFrame.y; // Bring the head back down to original height
+
+ View = Matrix4f::LookAtRH(shiftedEyePos, shiftedEyePos + forward, up);
+
+ // This is what transformation would be without head modeling.
+ // View = Matrix4f::LookAtRH(EyePos, EyePos + forward, up);
+
+ switch(SConfig.GetStereoMode())
+ {
+ case Stereo_None:
+ Render(SConfig.GetEyeRenderParams(StereoEye_Center));
+ break;
+
+ case Stereo_LeftRight_Multipass:
+ Render(SConfig.GetEyeRenderParams(StereoEye_Left));
+ Render(SConfig.GetEyeRenderParams(StereoEye_Right));
+ break;
+ }
+
+ pRender->Present();
+ // Force GPU to flush the scene, resulting in the lowest possible latency.
+ pRender->ForceFlushGPU();
+}
+
+
+// Render the scene for one eye.
+void OculusRoomTinyApp::Render(const StereoEyeParams& stereo)
+{
+ pRender->BeginScene(PostProcess);
+
+ // Apply Viewport/Projection for the eye.
+ pRender->ApplyStereoParams(stereo);
+ pRender->Clear();
+ pRender->SetDepthMode(true, true);
+
+ Scene.Render(pRender, stereo.ViewAdjust * View);
+
+ pRender->FinishScene();
+}
+
+
+//-------------------------------------------------------------------------------------
+// ***** Win32-Specific Logic
+
+bool OculusRoomTinyApp::setupWindow()
+{
+
+ WNDCLASS wc;
+ memset(&wc, 0, sizeof(wc));
+ wc.lpszClassName = L"OVRAppWindow";
+ wc.style = CS_OWNDC;
+ wc.lpfnWndProc = systemWindowProc;
+ wc.cbWndExtra = sizeof(OculusRoomTinyApp*);
+ RegisterClass(&wc);
+
+
+ RECT winSize = { 0, 0, Width, Height };
+ AdjustWindowRect(&winSize, WS_POPUP, false);
+ hWnd = CreateWindowA("OVRAppWindow", "OculusRoomTiny", WS_POPUP|WS_VISIBLE,
+ HMDInfo.DesktopX, HMDInfo.DesktopY,
+ winSize.right-winSize.left, winSize.bottom-winSize.top,
+ NULL, NULL, hInstance, (LPVOID)this);
+
+
+ // Initialize Window center in screen coordinates
+ POINT center = { Width / 2, Height / 2 };
+ ::ClientToScreen(hWnd, &center);
+ WindowCenter = center;
+
+
+ return (hWnd != NULL);
+}
+
+void OculusRoomTinyApp::destroyWindow()
+{
+ pRender.Clear();
+
+ if (hWnd)
+ {
+ // Release window resources.
+ ::DestroyWindow(hWnd);
+ UnregisterClass(L"OVRAppWindow", hInstance);
+ hWnd = 0;
+ Width = Height = 0;
+ }
+}
+
+
+LRESULT CALLBACK OculusRoomTinyApp::systemWindowProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
+{
+ if (msg == WM_NCCREATE)
+ pApp->hWnd = hwnd;
+ return pApp->windowProc(msg, wp, lp);
+}
+
+void OculusRoomTinyApp::giveUsFocus(bool setFocus)
+{
+ if (setFocus)
+ {
+ ::SetCursorPos(WindowCenter.x, WindowCenter.y);
+
+ MouseCaptured = true;
+ ::SetCapture(hWnd);
+ ::ShowCursor(FALSE);
+
+ }
+ else
+ {
+ MouseCaptured = false;
+ ::ReleaseCapture();
+ ::ShowCursor(TRUE);
+ }
+}
+
+LRESULT OculusRoomTinyApp::windowProc(UINT msg, WPARAM wp, LPARAM lp)
+{
+ switch (msg)
+ {
+ case WM_MOUSEMOVE:
+ {
+ if (MouseCaptured)
+ {
+ // Convert mouse motion to be relative (report the offset and re-center).
+ POINT newPos = { LOWORD(lp), HIWORD(lp) };
+ ::ClientToScreen(hWnd, &newPos);
+ if ((newPos.x == WindowCenter.x) && (newPos.y == WindowCenter.y))
+ break;
+ ::SetCursorPos(WindowCenter.x, WindowCenter.y);
+
+ LONG dx = newPos.x - WindowCenter.x;
+ LONG dy = newPos.y - WindowCenter.y;
+ pApp->OnMouseMove(dx, dy, 0);
+ }
+ }
+ break;
+
+ case WM_MOVE:
+ {
+ RECT r;
+ GetClientRect(hWnd, &r);
+ WindowCenter.x = r.right/2;
+ WindowCenter.y = r.bottom/2;
+ ::ClientToScreen(hWnd, &WindowCenter);
+ }
+ break;
+
+ case WM_KEYDOWN:
+ OnKey((unsigned)wp, true);
+ break;
+ case WM_KEYUP:
+ OnKey((unsigned)wp, false);
+ break;
+
+ case WM_SETFOCUS:
+ giveUsFocus(true);
+ break;
+
+ case WM_KILLFOCUS:
+ giveUsFocus(false);
+ break;
+
+ case WM_CREATE:
+ // Hack to position mouse in fullscreen window shortly after startup.
+ SetTimer(hWnd, 0, 100, NULL);
+ break;
+
+ case WM_TIMER:
+ KillTimer(hWnd, 0);
+ giveUsFocus(true);
+ break;
+
+ case WM_QUIT:
+ case WM_CLOSE:
+ Quit = true;
+ return 0;
+ }
+
+ return DefWindowProc(hWnd, msg, wp, lp);
+}
+
+static inline float GamepadStick(short in)
+{
+ float v;
+ if (abs(in) < 9000)
+ return 0;
+ else if (in > 9000)
+ v = (float) in - 9000;
+ else
+ v = (float) in + 9000;
+ return v / (32767 - 9000);
+}
+
+static inline float GamepadTrigger(BYTE in)
+{
+ return (in < 30) ? 0.0f : (float(in-30) / 225);
+}
+
+
+int OculusRoomTinyApp::Run()
+{
+ // Loop processing messages until Quit flag is set,
+ // rendering game scene inside of OnIdle().
+
+ while (!Quit)
+ {
+ MSG msg;
+ if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ else
+ {
+ // Read game-pad.
+ XINPUT_STATE xis;
+
+ if (pXInputGetState && !pXInputGetState(0, &xis) &&
+ (xis.dwPacketNumber != LastPadPacketNo))
+ {
+ OnGamepad(GamepadStick(xis.Gamepad.sThumbLX),
+ GamepadStick(xis.Gamepad.sThumbLY),
+ GamepadStick(xis.Gamepad.sThumbRX),
+ GamepadStick(xis.Gamepad.sThumbRY));
+ //pad.LT = GamepadTrigger(xis.Gamepad.bLeftTrigger);
+ LastPadPacketNo = xis.dwPacketNumber;
+ }
+
+ pApp->OnIdle();
+
+ // Keep sleeping when we're minimized.
+ if (IsIconic(hWnd))
+ Sleep(10);
+ }
+ }
+
+ return 0;
+}
+
+
+//-------------------------------------------------------------------------------------
+// ***** Program Startup
+
+int WINAPI WinMain(HINSTANCE hinst, HINSTANCE, LPSTR inArgs, int)
+{
+ int exitCode = 0;
+
+ // Initializes LibOVR. This LogMask_All enables maximum logging.
+ // Custom allocator can also be specified here.
+ OVR::System::Init(OVR::Log::ConfigureDefaultLog(OVR::LogMask_All));
+
+ // Scope to force application destructor before System::Destroy.
+ {
+ OculusRoomTinyApp app(hinst);
+ //app.hInstance = hinst;
+
+ exitCode = app.OnStartup(inArgs);
+ if (!exitCode)
+ {
+ // Processes messages and calls OnIdle() to do rendering.
+ exitCode = app.Run();
+ }
+ }
+
+ // No OVR functions involving memory are allowed after this.
+ OVR::System::Destroy();
+
+ OVR_DEBUG_STATEMENT(_CrtDumpMemoryLeaks());
+ return exitCode;
+}
diff --git a/Samples/OculusRoomTiny/Win32_OculusRoomTiny.h b/Samples/OculusRoomTiny/Win32_OculusRoomTiny.h
new file mode 100644
index 0000000..5a0eedf
--- /dev/null
+++ b/Samples/OculusRoomTiny/Win32_OculusRoomTiny.h
@@ -0,0 +1,189 @@
+/************************************************************************************
+
+Filename : OculusRoomTiny.h
+Content : Simplest possible first-person view test application for Oculus Rift
+Created : March 10, 2012
+Authors : Michael Antonov, Andrew Reisse
+
+Copyright : Copyright 2012 Oculus, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+*************************************************************************************/
+#ifndef INC_OculusRoomTiny_h
+#define INC_OculusRoomTiny_h
+
+#include <Windows.h>
+#include <xinput.h>
+
+#include "OVR.h"
+#include "Util/Util_Render_Stereo.h"
+#include "../../LibOVR/Src/Kernel/OVR_Timer.h"
+#include "RenderTiny_D3D1X_Device.h"
+
+using namespace OVR;
+using namespace OVR::RenderTiny;
+
+
+//-------------------------------------------------------------------------------------
+// ***** OculusRoomTiny Description
+
+// This app renders a simple flat-shaded room allowing the user to move along the
+// floor and look around with an HMD, mouse, keyboard and gamepad.
+// By default, the application will start full-screen on Oculus Rift.
+//
+// The following keys work:
+//
+// 'W', 'S', 'A', 'D' - Move forward, back; strafe left/right.
+// F1 - No stereo, no distortion.
+// F2 - Stereo, no distortion.
+// F3 - Stereo and distortion.
+//
+
+// The world RHS coordinate system is defines as follows (as seen in perspective view):
+// Y - Up
+// Z - Back
+// X - Right
+const Vector3f UpVector(0.0f, 1.0f, 0.0f);
+const Vector3f ForwardVector(0.0f, 0.0f, -1.0f);
+const Vector3f RightVector(1.0f, 0.0f, 0.0f);
+
+// We start out looking in the positive Z (180 degree rotation).
+const float YawInitial = 3.141592f;
+const float Sensitivity = 1.0f;
+const float MoveSpeed = 3.0f; // m/s
+
+
+//-------------------------------------------------------------------------------------
+// ***** OculusRoomTiny Application class
+
+// An instance of this class is created on application startup (main/WinMain).
+//
+// It then works as follows:
+//
+// OnStartup - Window, graphics and HMD setup is done here.
+// This function will initialize OVR::DeviceManager and HMD,
+// creating SensorDevice and attaching it to SensorFusion.
+// This needs to be done before obtaining sensor data.
+//
+// OnIdle - Does per-frame processing, processing SensorFusion and
+// movement input and rendering the frame.
+
+class OculusRoomTinyApp : public MessageHandler
+{
+public:
+ OculusRoomTinyApp(HINSTANCE hinst);
+ ~OculusRoomTinyApp();
+
+ // Initializes graphics, Rift input and creates world model.
+ virtual int OnStartup(const char* args);
+ // Called per frame to sample SensorFucion and render the world.
+ virtual void OnIdle();
+
+ // Installed for Oculus device messages. Optional.
+ virtual void OnMessage(const Message& msg);
+
+ // Handle input events for movement.
+ virtual void OnGamepad(float padLx, float padLY, float padRx, float padRy);
+ virtual void OnMouseMove(int x, int y, int modifiers);
+ virtual void OnKey(unsigned vk, bool down);
+
+ // Render the view for one eye.
+ void Render(const StereoEyeParams& stereo);
+
+ // Main application loop.
+ int Run();
+
+ // Return amount of time passed since application started in seconds.
+ double GetAppTime() const
+ {
+ return (OVR::Timer::GetTicks() - StartupTicks) * (1.0 / (double)OVR::Timer::MksPerSecond);
+ }
+
+
+protected:
+
+ // Win32 window setup interface.
+ LRESULT windowProc(UINT msg, WPARAM wp, LPARAM lp);
+ bool setupWindow();
+ void destroyWindow();
+ // Win32 static function that delegates to WindowProc member function.
+ static LRESULT CALLBACK systemWindowProc(HWND window, UINT msg, WPARAM wp, LPARAM lp);
+
+ void giveUsFocus(bool setFocus);
+
+ static OculusRoomTinyApp* pApp;
+
+ // *** Rendering Variables
+ Ptr<RenderDevice> pRender;
+ RendererParams RenderParams;
+ int Width, Height;
+
+
+ // *** Win32 System Variables
+ HWND hWnd;
+ HINSTANCE hInstance;
+ POINT WindowCenter; // In desktop coordinates
+ bool Quit;
+ bool MouseCaptured;
+
+ // Dynamically ink to XInput to simplify projects.
+ typedef DWORD (WINAPI *PFn_XInputGetState)(DWORD dwUserIndex, XINPUT_STATE* pState);
+ PFn_XInputGetState pXInputGetState;
+ HMODULE hXInputModule;
+ UInt32 LastPadPacketNo;
+
+
+ // *** Oculus HMD Variables
+
+ Ptr<DeviceManager> pManager;
+ Ptr<SensorDevice> pSensor;
+ Ptr<HMDDevice> pHMD;
+ SensorFusion SFusion;
+ OVR::HMDInfo HMDInfo;
+
+ // Last update seconds, used for move speed timing.
+ double LastUpdate;
+ UInt64 StartupTicks;
+
+ // Position and look. The following apply:
+ Vector3f EyePos;
+ float EyeYaw; // Rotation around Y, CCW positive when looking at RHS (X,Z) plane.
+ float EyePitch; // Pitch. If sensor is plugged in, only read from sensor.
+ float EyeRoll; // Roll, only accessible from Sensor.
+ float LastSensorYaw; // Stores previous Yaw value from to support computing delta.
+
+ // Movement state; different bits may be set based on the state of keys.
+ UByte MoveForward;
+ UByte MoveBack;
+ UByte MoveLeft;
+ UByte MoveRight;
+ Vector3f GamepadMove, GamepadRotate;
+
+ Matrix4f View;
+ RenderTiny::Scene Scene;
+
+ // Stereo view parameters.
+ StereoConfig SConfig;
+ PostProcessType PostProcess;
+
+ // Shift accelerates movement/adjustment velocity.
+ bool ShiftDown;
+ bool ControlDown;
+};
+
+// Adds sample models and lights to the argument scene.
+void PopulateRoomScene(Scene* scene, RenderDevice* render);
+
+
+#endif
diff --git a/Samples/OculusWorldDemo/Makefile b/Samples/OculusWorldDemo/Makefile
new file mode 100644
index 0000000..5ffc0cc
--- /dev/null
+++ b/Samples/OculusWorldDemo/Makefile
@@ -0,0 +1,5 @@
+
+WORLDDEMO_SRCS := Samples/OculusWorldDemo/OculusWorldDemo.cpp \
+ $(PLATFORM_SRCS) $(RENDER_SRCS)
+
+$(eval $(call BUILD_RENDER_APP, Samples/OculusRoomTiny/$(C)/OculusWorldDemo, $(WORLDDEMO_SRCS)))
diff --git a/Samples/OculusWorldDemo/OculusWorldDemo.cpp b/Samples/OculusWorldDemo/OculusWorldDemo.cpp
new file mode 100644
index 0000000..18e614c
--- /dev/null
+++ b/Samples/OculusWorldDemo/OculusWorldDemo.cpp
@@ -0,0 +1,1836 @@
+/************************************************************************************
+
+Filename : OculusWorldDemo.cpp
+Content : First-person view test application for Oculus Rift
+Created : October 4, 2012
+Authors : Michael Antonov, Andrew Reisse, Steve LaValle
+ Peter Hoff, Dan Goodman, Bryan Croteau
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+*************************************************************************************/
+
+#include "OVR.h"
+
+#include "Player.h"
+
+#include "../CommonSrc/Platform/Platform_Default.h"
+#include "../CommonSrc/Render/Render_Device.h"
+#include "../CommonSrc/Render/Render_XMLSceneLoader.h"
+#include "../CommonSrc/Render/Render_FontEmbed_DejaVu48.h"
+#include "../CommonSrc/Platform/Gamepad.h"
+
+#include <Kernel/OVR_SysFile.h>
+#include <Kernel/OVR_Log.h>
+#include <Kernel/OVR_Timer.h>
+
+// Filename to be loaded by default, searching specified paths.
+#define WORLDDEMO_ASSET_FILE "Tuscany.xml"
+#define WORLDDEMO_ASSET_PATH1 "Assets/Tuscany/"
+#define WORLDDEMO_ASSET_PATH2 "../Assets/Tuscany/"
+// This path allows the shortcut to work.
+#define WORLDDEMO_ASSET_PATH3 "Samples/OculusWorldDemo/Assets/Tuscany/"
+
+
+using namespace OVR;
+using namespace OVR::Platform;
+using namespace OVR::Render;
+
+//-------------------------------------------------------------------------------------
+// ***** OculusWorldDemo Description
+
+// This app renders a simple flat-shaded room allowing the user to move along the
+// floor and look around with an HMD, mouse and keyboard. The following keys work:
+//
+// 'W', 'S', 'A', 'D' and Arrow Keys - Move forward, back; strafe left/right.
+// F1 - No stereo, no distortion.
+// F2 - Stereo, no distortion.
+// F3 - Stereo and distortion.
+// F4 - Toggle MSAA.
+// F9 - Cycle through fullscreen and windowed modes. Necessary for previewing content with Rift.
+//
+// Important Oculus-specific logic can be found at following locations:
+//
+// OculusWorldDemoApp::OnStartup - This function will initialize OVR::DeviceManager and HMD,
+// creating SensorDevice and attaching it to SensorFusion.
+// This needs to be done before obtaining sensor data.
+//
+// OculusWorldDemoApp::OnIdle - Here we poll SensorFusion for orientation, apply it
+// to the scene and handle movement.
+// Stereo rendering is also done here, by delegating to
+// to Render function for each eye.
+//
+
+//-------------------------------------------------------------------------------------
+// ***** OculusWorldDemo Application class
+
+// An instance of this class is created on application startup (main/WinMain).
+// It then works as follows:
+// - Graphics and HMD setup is done OculusWorldDemoApp::OnStartup(). This function
+// also creates the room model from Slab declarations.
+// - Per-frame processing is done in OnIdle(). This function processes
+// sensor and movement input and then renders the frame.
+// - Additional input processing is done in OnMouse, OnKey.
+
+class OculusWorldDemoApp : public Application, public MessageHandler
+{
+public:
+ OculusWorldDemoApp();
+ ~OculusWorldDemoApp();
+
+ virtual int OnStartup(int argc, const char** argv);
+ virtual void OnIdle();
+
+ virtual void OnMouseMove(int x, int y, int modifiers);
+ virtual void OnKey(KeyCode key, int chr, bool down, int modifiers);
+ virtual void OnResize(int width, int height);
+
+ virtual void OnMessage(const Message& msg);
+
+ void Render(const StereoEyeParams& stereo);
+
+ // Sets temporarily displayed message for adjustments
+ void SetAdjustMessage(const char* format, ...);
+ // Overrides current timeout, in seconds (not the future default value);
+ // intended to be called right after SetAdjustMessage.
+ void SetAdjustMessageTimeout(float timeout);
+
+ // Stereo setting adjustment functions.
+ // Called with deltaTime when relevant key is held.
+ void AdjustFov(float dt);
+ void AdjustAspect(float dt);
+ void AdjustIPD(float dt);
+ void AdjustEyeHeight(float dt);
+
+ void AdjustMotionPrediction(float dt);
+
+ void AdjustDistortion(float dt, int kIndex, const char* label);
+ void AdjustDistortionK0(float dt) { AdjustDistortion(dt, 0, "K0"); }
+ void AdjustDistortionK1(float dt) { AdjustDistortion(dt, 1, "K1"); }
+ void AdjustDistortionK2(float dt) { AdjustDistortion(dt, 2, "K2"); }
+ void AdjustDistortionK3(float dt) { AdjustDistortion(dt, 3, "K3"); }
+
+ // Adds room model to scene.
+ void PopulateScene(const char* fileName);
+ void PopulatePreloadScene();
+ void ClearScene();
+
+ // Magnetometer calibration procedure
+ void UpdateManualMagCalibration();
+
+protected:
+ RenderDevice* pRender;
+ RendererParams RenderParams;
+ int Width, Height;
+ int Screen;
+ int FirstScreenInCycle;
+
+ // Magnetometer calibration and yaw correction
+ Util::MagCalibration MagCal;
+ bool MagAwaitingForwardLook;
+
+ // *** Oculus HMD Variables
+ Ptr<DeviceManager> pManager;
+ Ptr<SensorDevice> pSensor;
+ Ptr<HMDDevice> pHMD;
+ SensorFusion SFusion;
+ HMDInfo HMDInfo;
+
+ Ptr<LatencyTestDevice> pLatencyTester;
+ Util::LatencyTest LatencyUtil;
+
+ double LastUpdate;
+ int FPS;
+ int FrameCounter;
+ double NextFPSUpdate;
+
+ Array<Ptr<CollisionModel> > CollisionModels;
+ Array<Ptr<CollisionModel> > GroundCollisionModels;
+
+ // Loading process displays screenshot in first frame
+ // and then proceeds to load until finished.
+ enum LoadingStateType
+ {
+ LoadingState_Frame0,
+ LoadingState_DoLoad,
+ LoadingState_Finished
+ };
+
+ // Player
+ Player Player;
+ Matrix4f View;
+ Scene MainScene;
+ Scene LoadingScene;
+ Scene GridScene;
+ Scene YawMarkGreenScene;
+ Scene YawMarkRedScene;
+ Scene YawLinesScene;
+
+ LoadingStateType LoadingState;
+
+ Ptr<ShaderFill> LitSolid, LitTextures[4];
+
+ // Stereo view parameters.
+ StereoConfig SConfig;
+ PostProcessType PostProcess;
+
+ // LOD
+ String MainFilePath;
+ Array<String> LODFilePaths;
+ int ConsecutiveLowFPSFrames;
+ int CurrentLODFileIndex;
+
+ float DistortionK0;
+ float DistortionK1;
+ float DistortionK2;
+ float DistortionK3;
+
+ String AdjustMessage;
+ double AdjustMessageTimeout;
+
+ // Saved distortion state.
+ float SavedK0, SavedK1, SavedK2, SavedK3;
+ float SavedESD, SavedAspect, SavedEyeDistance;
+
+ // Allows toggling color around distortion.
+ Color DistortionClearColor;
+
+ // Stereo settings adjustment state.
+ typedef void (OculusWorldDemoApp::*AdjustFuncType)(float);
+ bool ShiftDown;
+ AdjustFuncType pAdjustFunc;
+ float AdjustDirection;
+
+ enum SceneRenderMode
+ {
+ Scene_World,
+ Scene_Grid,
+ Scene_Both,
+ Scene_YawView
+ };
+ SceneRenderMode SceneMode;
+
+
+ enum TextScreen
+ {
+ Text_None,
+ Text_Orientation,
+ Text_Config,
+ Text_Help,
+ Text_Count
+ };
+ TextScreen TextScreen;
+
+ struct DeviceStatusNotificationDesc
+ {
+ DeviceHandle Handle;
+ MessageType Action;
+
+ DeviceStatusNotificationDesc():Action(Message_None) {}
+ DeviceStatusNotificationDesc(MessageType mt, const DeviceHandle& dev)
+ : Handle(dev), Action(mt) {}
+ };
+ Array<DeviceStatusNotificationDesc> DeviceStatusNotificationsQueue;
+
+
+ Model* CreateModel(Vector3f pos, struct SlabModel* sm);
+ Model* CreateBoundingModel(CollisionModel &cm);
+ void PopulateLODFileNames();
+ void DropLOD();
+ void RaiseLOD();
+ void CycleDisplay();
+ void GamepadStateChanged(const GamepadState& pad);
+
+ // Variable used by UpdateManualCalibration
+ float FirstMagYaw;
+};
+
+//-------------------------------------------------------------------------------------
+
+OculusWorldDemoApp::OculusWorldDemoApp()
+ : pRender(0),
+ LastUpdate(0),
+ LoadingState(LoadingState_Frame0),
+ // Initial location
+ SConfig(),
+ PostProcess(PostProcess_Distortion),
+ DistortionClearColor(0, 0, 0),
+
+ ShiftDown(false),
+ pAdjustFunc(0),
+ AdjustDirection(1.0f),
+ SceneMode(Scene_World),
+ TextScreen(Text_None)
+{
+ Width = 1280;
+ Height = 800;
+ Screen = 0;
+ FirstScreenInCycle = 0;
+
+ FPS = 0;
+ FrameCounter = 0;
+ NextFPSUpdate = 0;
+
+ ConsecutiveLowFPSFrames = 0;
+ CurrentLODFileIndex = 0;
+
+ AdjustMessageTimeout = 0;
+}
+
+OculusWorldDemoApp::~OculusWorldDemoApp()
+{
+ RemoveHandlerFromDevices();
+
+ if(DejaVu.fill)
+ {
+ DejaVu.fill->Release();
+ }
+ pLatencyTester.Clear();
+ pSensor.Clear();
+ pHMD.Clear();
+
+ CollisionModels.ClearAndRelease();
+ GroundCollisionModels.ClearAndRelease();
+}
+
+int OculusWorldDemoApp::OnStartup(int argc, const char** argv)
+{
+
+ // *** Oculus HMD & Sensor Initialization
+
+ // Create DeviceManager and first available HMDDevice from it.
+ // Sensor object is created from the HMD, to ensure that it is on the
+ // correct device.
+
+ pManager = *DeviceManager::Create();
+
+ // We'll handle it's messages in this case.
+ pManager->SetMessageHandler(this);
+
+ pHMD = *pManager->EnumerateDevices<HMDDevice>().CreateDevice();
+ if(pHMD)
+ {
+ pSensor = *pHMD->GetSensor();
+
+ // This will initialize HMDInfo with information about configured IPD,
+ // screen size and other variables needed for correct projection.
+ // We pass HMD DisplayDeviceName into the renderer to select the
+ // correct monitor in full-screen mode.
+ if(pHMD->GetDeviceInfo(&HMDInfo))
+ {
+ //RenderParams.MonitorName = hmd.DisplayDeviceName;
+ SConfig.SetHMDInfo(HMDInfo);
+ }
+ }
+ else
+ {
+ // If we didn't detect an HMD, try to create the sensor directly.
+ // This is useful for debugging sensor interaction; it is not needed in
+ // a shipping app.
+ pSensor = *pManager->EnumerateDevices<SensorDevice>().CreateDevice();
+ }
+
+ // Create the Latency Tester device and assign it to the LatencyTesterUtil object.
+ pLatencyTester = *pManager->EnumerateDevices<LatencyTestDevice>().CreateDevice();
+ if (pLatencyTester)
+ {
+ LatencyUtil.SetDevice(pLatencyTester);
+ }
+ // Make the user aware which devices are present.
+ if(pHMD == NULL && pSensor == NULL)
+ {
+ SetAdjustMessage("---------------------------------\nNO HMD DETECTED\nNO SENSOR DETECTED\n---------------------------------");
+ }
+ else if(pHMD == NULL)
+ {
+ SetAdjustMessage("----------------------------\nNO HMD DETECTED\n----------------------------");
+ }
+ else if(pSensor == NULL)
+ {
+ SetAdjustMessage("---------------------------------\nNO SENSOR DETECTED\n---------------------------------");
+ }
+ else
+ {
+ SetAdjustMessage("--------------------------------------------\n"
+ "Press F9 for Full-Screen on Rift\n"
+ "--------------------------------------------");
+ }
+
+ // First message should be extra-long.
+ SetAdjustMessageTimeout(10.0f);
+
+
+ if(HMDInfo.HResolution > 0)
+ {
+ Width = HMDInfo.HResolution;
+ Height = HMDInfo.VResolution;
+ }
+
+ if(!pPlatform->SetupWindow(Width, Height))
+ {
+ return 1;
+ }
+
+ String Title = "Oculus World Demo";
+ if(HMDInfo.ProductName[0])
+ {
+ Title += " : ";
+ Title += HMDInfo.ProductName;
+ }
+ pPlatform->SetWindowTitle(Title);
+
+ // Report relative mouse motion in OnMouseMove
+ pPlatform->SetMouseMode(Mouse_Relative);
+
+ if(pSensor)
+ {
+ // We need to attach sensor to SensorFusion object for it to receive
+ // body frame messages and update orientation. SFusion.GetOrientation()
+ // is used in OnIdle() to orient the view.
+ SFusion.AttachToSensor(pSensor);
+
+ SFusion.SetDelegateMessageHandler(this);
+
+ SFusion.SetPredictionEnabled(true);
+ }
+
+
+ // *** Initialize Rendering
+
+ const char* graphics = "d3d11";
+
+ // Select renderer based on command line arguments.
+ for(int i = 1; i < argc; i++)
+ {
+ if(!strcmp(argv[i], "-r") && i < argc - 1)
+ {
+ graphics = argv[i + 1];
+ }
+ else if(!strcmp(argv[i], "-fs"))
+ {
+ RenderParams.Fullscreen = true;
+ }
+ }
+
+ // Enable multi-sampling by default.
+ RenderParams.Multisample = 4;
+ pRender = pPlatform->SetupGraphics(OVR_DEFAULT_RENDER_DEVICE_SET,
+ graphics, RenderParams);
+
+
+
+ // *** Configure Stereo settings.
+
+ SConfig.SetFullViewport(Viewport(0, 0, Width, Height));
+ SConfig.SetStereoMode(Stereo_LeftRight_Multipass);
+
+ // Configure proper Distortion Fit.
+ // For 7" screen, fit to touch left side of the view, leaving a bit of
+ // invisible screen on the top (saves on rendering cost).
+ // For smaller screens (5.5"), fit to the top.
+ if (HMDInfo.HScreenSize > 0.0f)
+ {
+ if (HMDInfo.HScreenSize > 0.140f) // 7"
+ SConfig.SetDistortionFitPointVP(-1.0f, 0.0f);
+ else
+ SConfig.SetDistortionFitPointVP(0.0f, 1.0f);
+ }
+
+ pRender->SetSceneRenderScale(SConfig.GetDistortionScale());
+ //pRender->SetSceneRenderScale(0.8f);
+
+ SConfig.Set2DAreaFov(DegreeToRad(85.0f));
+
+
+ // *** Identify Scene File & Prepare for Loading
+
+ // This creates lights and models.
+ if (argc == 2)
+ {
+ MainFilePath = argv[1];
+ PopulateLODFileNames();
+ }
+ else
+ {
+ fprintf(stderr, "Usage: OculusWorldDemo [input XML]\n");
+ MainFilePath = WORLDDEMO_ASSET_FILE;
+ }
+
+ // Try to modify path for correctness in case specified file is not found.
+ if (!SysFile(MainFilePath).IsValid())
+ {
+ String prefixPath1(pPlatform->GetContentDirectory() + "/" + WORLDDEMO_ASSET_PATH1),
+ prefixPath2(WORLDDEMO_ASSET_PATH2),
+ prefixPath3(WORLDDEMO_ASSET_PATH3);
+ if (SysFile(prefixPath1 + MainFilePath).IsValid())
+ MainFilePath = prefixPath1 + MainFilePath;
+ else if (SysFile(prefixPath2 + MainFilePath).IsValid())
+ MainFilePath = prefixPath2 + MainFilePath;
+ else if (SysFile(prefixPath3 + MainFilePath).IsValid())
+ MainFilePath = prefixPath3 + MainFilePath;
+ }
+
+ PopulatePreloadScene();
+
+ LastUpdate = pPlatform->GetAppTime();
+ //pPlatform->PlayMusicFile(L"Loop.wav");
+
+ return 0;
+}
+
+void OculusWorldDemoApp::OnMessage(const Message& msg)
+{
+ if (msg.Type == Message_DeviceAdded || msg.Type == Message_DeviceRemoved)
+ {
+ if (msg.pDevice == pManager)
+ {
+ const MessageDeviceStatus& statusMsg =
+ static_cast<const MessageDeviceStatus&>(msg);
+
+ { // limit the scope of the lock
+ Lock::Locker lock(pManager->GetHandlerLock());
+ DeviceStatusNotificationsQueue.PushBack(
+ DeviceStatusNotificationDesc(statusMsg.Type, statusMsg.Handle));
+ }
+
+ switch (statusMsg.Type)
+ {
+ case OVR::Message_DeviceAdded:
+ LogText("DeviceManager reported device added.\n");
+ break;
+
+ case OVR::Message_DeviceRemoved:
+ LogText("DeviceManager reported device removed.\n");
+ break;
+
+ default: OVR_ASSERT(0); // unexpected type
+ }
+ }
+ }
+}
+
+void OculusWorldDemoApp::OnResize(int width, int height)
+{
+ Width = width;
+ Height = height;
+ SConfig.SetFullViewport(Viewport(0, 0, Width, Height));
+}
+
+void OculusWorldDemoApp::OnMouseMove(int x, int y, int modifiers)
+{
+ if(modifiers & Mod_MouseRelative)
+ {
+ // Get Delta
+ int dx = x, dy = y;
+
+ const float maxPitch = ((3.1415f / 2) * 0.98f);
+
+ // Apply to rotation. Subtract for right body frame rotation,
+ // since yaw rotation is positive CCW when looking down on XZ plane.
+ Player.EyeYaw -= (Sensitivity * dx) / 360.0f;
+
+ if(!pSensor)
+ {
+ Player.EyePitch -= (Sensitivity * dy) / 360.0f;
+
+ if(Player.EyePitch > maxPitch)
+ {
+ Player.EyePitch = maxPitch;
+ }
+ if(Player.EyePitch < -maxPitch)
+ {
+ Player.EyePitch = -maxPitch;
+ }
+ }
+ }
+}
+
+
+void OculusWorldDemoApp::OnKey(KeyCode key, int chr, bool down, int modifiers)
+{
+ OVR_UNUSED(chr);
+
+ switch(key)
+ {
+ case Key_Q:
+ if (down && (modifiers & Mod_Control))
+ {
+ pPlatform->Exit(0);
+ }
+ break;
+
+ // Handle player movement keys.
+ // We just update movement state here, while the actual translation is done in OnIdle()
+ // based on time.
+ case Key_W:
+ Player.MoveForward = down ? (Player.MoveForward | 1) : (Player.MoveForward & ~1);
+ break;
+ case Key_S:
+ Player.MoveBack = down ? (Player.MoveBack | 1) : (Player.MoveBack & ~1);
+ break;
+ case Key_A:
+ Player.MoveLeft = down ? (Player.MoveLeft | 1) : (Player.MoveLeft & ~1);
+ break;
+ case Key_D:
+ Player.MoveRight = down ? (Player.MoveRight | 1) : (Player.MoveRight & ~1);
+ break;
+ case Key_Up:
+ Player.MoveForward = down ? (Player.MoveForward | 2) : (Player.MoveForward & ~2);
+ break;
+ case Key_Down:
+ Player.MoveBack = down ? (Player.MoveBack | 2) : (Player.MoveBack & ~2);
+ break;
+ case Key_Left:
+ Player.MoveLeft = down ? (Player.MoveLeft | 2) : (Player.MoveLeft & ~2);
+ break;
+ case Key_Right:
+ Player.MoveRight = down ? (Player.MoveRight | 2) : (Player.MoveRight & ~2);
+ break;
+
+ case Key_Minus:
+ pAdjustFunc = down ? &OculusWorldDemoApp::AdjustEyeHeight : 0;
+ AdjustDirection = -1;
+ break;
+ case Key_Equal:
+ pAdjustFunc = down ? &OculusWorldDemoApp::AdjustEyeHeight : 0;
+ AdjustDirection = 1;
+ break;
+
+ case Key_B:
+ if (down)
+ {
+ if(SConfig.GetDistortionScale() == 1.0f)
+ {
+ if(SConfig.GetHMDInfo().HScreenSize > 0.140f) // 7"
+ {
+ SConfig.SetDistortionFitPointVP(-1.0f, 0.0f);
+ }
+ else
+ {
+ SConfig.SetDistortionFitPointVP(0.0f, 1.0f);
+ }
+ }
+ else
+ {
+ // No fitting; scale == 1.0.
+ SConfig.SetDistortionFitPointVP(0, 0);
+ }
+ }
+ break;
+
+ // Support toggling background color for distortion so that we can see
+ // the effect on the periphery.
+ case Key_V:
+ if (down)
+ {
+ if(DistortionClearColor.B == 0)
+ {
+ DistortionClearColor = Color(0, 128, 255);
+ }
+ else
+ {
+ DistortionClearColor = Color(0, 0, 0);
+ }
+
+ pRender->SetDistortionClearColor(DistortionClearColor);
+ }
+ break;
+
+
+ case Key_F1:
+ SConfig.SetStereoMode(Stereo_None);
+ PostProcess = PostProcess_None;
+ SetAdjustMessage("StereoMode: None");
+ break;
+ case Key_F2:
+ SConfig.SetStereoMode(Stereo_LeftRight_Multipass);
+ PostProcess = PostProcess_None;
+ SetAdjustMessage("StereoMode: Stereo + No Distortion");
+ break;
+ case Key_F3:
+ SConfig.SetStereoMode(Stereo_LeftRight_Multipass);
+ PostProcess = PostProcess_Distortion;
+ SetAdjustMessage("StereoMode: Stereo + Distortion");
+ break;
+
+ case Key_R:
+ SFusion.Reset();
+ SetAdjustMessage("Sensor Fusion Reset");
+ break;
+
+ case Key_Space:
+ if (!down)
+ {
+ TextScreen = (enum TextScreen)((TextScreen + 1) % Text_Count);
+ }
+ break;
+
+ case Key_F4:
+ if (!down)
+ {
+ RenderParams = pRender->GetParams();
+ RenderParams.Multisample = RenderParams.Multisample > 1 ? 1 : 4;
+ pRender->SetParams(RenderParams);
+ if(RenderParams.Multisample > 1)
+ {
+ SetAdjustMessage("Multisampling On");
+ }
+ else
+ {
+ SetAdjustMessage("Multisampling Off");
+ }
+ }
+ break;
+ case Key_F9:
+ if (!down)
+ {
+ CycleDisplay();
+ }
+ break;
+
+#ifdef OVR_OS_MAC
+ case Key_F10: // F11 is reserved on Mac
+#else
+ case Key_F11:
+#endif
+ if (!down)
+ {
+ RenderParams = pRender->GetParams();
+ RenderParams.Display = DisplayId(SConfig.GetHMDInfo().DisplayDeviceName,SConfig.GetHMDInfo().DisplayId);
+ pRender->SetParams(RenderParams);
+
+ pPlatform->SetMouseMode(Mouse_Normal);
+ pPlatform->SetFullscreen(RenderParams, pRender->IsFullscreen() ? Display_Window : Display_FakeFullscreen);
+ pPlatform->SetMouseMode(Mouse_Relative); // Avoid mode world rotation jump.
+ // If using an HMD, enable post-process (for distortion) and stereo.
+ if(RenderParams.IsDisplaySet() && pRender->IsFullscreen())
+ {
+ SConfig.SetStereoMode(Stereo_LeftRight_Multipass);
+ PostProcess = PostProcess_Distortion;
+ }
+ }
+ break;
+
+ case Key_Escape:
+ if(!down)
+ {
+ // switch to primary screen windowed mode
+ pPlatform->SetFullscreen(RenderParams, Display_Window);
+ RenderParams.Display = pPlatform->GetDisplay(0);
+ pRender->SetParams(RenderParams);
+ Screen = 0;
+ }
+ break;
+
+ // Stereo adjustments.
+ case Key_BracketLeft:
+ pAdjustFunc = down ? &OculusWorldDemoApp::AdjustFov : 0;
+ AdjustDirection = 1;
+ break;
+ case Key_BracketRight:
+ pAdjustFunc = down ? &OculusWorldDemoApp::AdjustFov : 0;
+ AdjustDirection = -1;
+ break;
+
+ case Key_Insert:
+ case Key_Num0:
+ pAdjustFunc = down ? &OculusWorldDemoApp::AdjustIPD : 0;
+ AdjustDirection = 1;
+ break;
+ case Key_Delete:
+ case Key_Num9:
+ pAdjustFunc = down ? &OculusWorldDemoApp::AdjustIPD : 0;
+ AdjustDirection = -1;
+ break;
+
+ case Key_PageUp:
+ pAdjustFunc = down ? &OculusWorldDemoApp::AdjustAspect : 0;
+ AdjustDirection = 1;
+ break;
+ case Key_PageDown:
+ pAdjustFunc = down ? &OculusWorldDemoApp::AdjustAspect : 0;
+ AdjustDirection = -1;
+ break;
+
+ // Distortion correction adjustments
+ case Key_H:
+ pAdjustFunc = down ? &OculusWorldDemoApp::AdjustDistortionK0 : NULL;
+ AdjustDirection = -1;
+ break;
+ case Key_Y:
+ pAdjustFunc = down ? &OculusWorldDemoApp::AdjustDistortionK0 : NULL;
+ AdjustDirection = 1;
+ break;
+ case Key_J:
+ pAdjustFunc = down ? &OculusWorldDemoApp::AdjustDistortionK1 : NULL;
+ AdjustDirection = -1;
+ break;
+ case Key_U:
+ pAdjustFunc = down ? &OculusWorldDemoApp::AdjustDistortionK1 : NULL;
+ AdjustDirection = 1;
+ break;
+ case Key_K:
+ pAdjustFunc = down ? &OculusWorldDemoApp::AdjustDistortionK2 : NULL;
+ AdjustDirection = -1;
+ break;
+ case Key_I:
+ pAdjustFunc = down ? &OculusWorldDemoApp::AdjustDistortionK2 : NULL;
+ AdjustDirection = 1;
+ break;
+ case Key_L:
+ pAdjustFunc = down ? &OculusWorldDemoApp::AdjustDistortionK3 : NULL;
+ AdjustDirection = -1;
+ break;
+ case Key_O:
+ pAdjustFunc = down ? &OculusWorldDemoApp::AdjustDistortionK3 : NULL;
+ AdjustDirection = 1;
+ break;
+
+
+ case Key_Tab:
+ if (down)
+ {
+ float t0 = SConfig.GetDistortionK(0),
+ t1 = SConfig.GetDistortionK(1),
+ t2 = SConfig.GetDistortionK(2),
+ t3 = SConfig.GetDistortionK(3);
+ float tESD = SConfig.GetEyeToScreenDistance(),
+ taspect = SConfig.GetAspectMultiplier(),
+ tipd = SConfig.GetIPD();
+
+ if(SavedK0 > 0.0f)
+ {
+ SConfig.SetDistortionK(0, SavedK0);
+ SConfig.SetDistortionK(1, SavedK1);
+ SConfig.SetDistortionK(2, SavedK2);
+ SConfig.SetDistortionK(3, SavedK3);
+ SConfig.SetEyeToScreenDistance(SavedESD);
+ SConfig.SetAspectMultiplier(SavedAspect);
+ SConfig.SetIPD(SavedEyeDistance);
+
+ SetAdjustMessage("Restored:\n"
+ "ESD:\t120 %.3f\t350 Eye:\t490 %.3f\n"
+ "K0: \t120 %.4f\t350 K2: \t490 %.4f\n"
+ "K1: \t120 %.4f\t350 K3: \t490 %.4f",
+ SavedESD, SavedEyeDistance,
+ SavedK0, SavedK2,
+ SavedK1, SavedK3);
+ }
+ else
+ {
+ SetAdjustMessage("Setting Saved");
+ }
+
+ SavedK0 = t0;
+ SavedK1 = t1;
+ SavedK2 = t2;
+ SavedK3 = t3;
+ SavedESD = tESD;
+ SavedAspect = taspect;
+ SavedEyeDistance = tipd;
+ }
+ break;
+
+ case Key_G:
+ if (down)
+ {
+ if(SceneMode == Scene_World)
+ {
+ SceneMode = Scene_Grid;
+ SetAdjustMessage("Grid Only");
+ }
+ else if(SceneMode == Scene_Grid)
+ {
+ SceneMode = Scene_Both;
+ SetAdjustMessage("Grid Overlay");
+ }
+ else if(SceneMode == Scene_Both)
+ {
+ SceneMode = Scene_World;
+ SetAdjustMessage("Grid Off");
+ }
+ }
+ break;
+
+ // Holding down Shift key accelerates adjustment velocity.
+ case Key_Shift:
+ ShiftDown = down;
+ break;
+
+ // Reset the camera position in case we get stuck
+ case Key_T:
+ Player.EyePos = Vector3f(10.0f, 1.6f, 10.0f);
+ break;
+
+ case Key_F5:
+ if (!down)
+ {
+ UPInt numNodes = MainScene.Models.GetSize();
+ for(UPInt i = 0; i < numNodes; i++)
+ {
+ Ptr<OVR::Render::Model> nodePtr = MainScene.Models[i];
+ Render::Model* pNode = nodePtr.GetPtr();
+ if(pNode->IsCollisionModel)
+ {
+ pNode->Visible = !pNode->Visible;
+ }
+ }
+ }
+ break;
+
+ case Key_N:
+ pAdjustFunc = down ? &OculusWorldDemoApp::AdjustMotionPrediction : NULL;
+ AdjustDirection = -1;
+ break;
+
+ case Key_M:
+ pAdjustFunc = down ? &OculusWorldDemoApp::AdjustMotionPrediction : NULL;
+ AdjustDirection = 1;
+ break;
+
+/*
+ case Key_N:
+ RaiseLOD();
+ break;
+ case Key_M:
+ DropLOD();
+ break;
+*/
+ // Start calibrating magnetometer
+ case Key_Z:
+ if (down)
+ {
+ if (MagCal.IsManuallyCalibrating())
+ MagAwaitingForwardLook = false;
+ else
+ {
+ MagCal.BeginManualCalibration(SFusion);
+ MagAwaitingForwardLook = true;
+ }
+ }
+ break;
+
+ case Key_X:
+ if (down)
+ {
+ MagCal.BeginAutoCalibration(SFusion);
+ SetAdjustMessage("Starting Auto Mag Calibration");
+ }
+ break;
+
+ // Set the magnetometer reference point
+ case Key_Semicolon:
+ if (down)
+ {
+ SFusion.SetMagReference();
+ SetAdjustMessage("Magnetometer Reference Set");
+ if (SFusion.IsMagReady())
+ SFusion.SetYawCorrectionEnabled(true);
+ }
+ break;
+
+ // Show view of yaw angles (for mag calibration/analysis)
+ case Key_F6:
+ if (down)
+ {
+ if (SceneMode != Scene_YawView)
+ {
+ SceneMode = Scene_YawView;
+ SetAdjustMessage("Magnetometer Yaw Angle Marks");
+ }
+ else
+ {
+ SceneMode = Scene_World;
+ SetAdjustMessage("Magnetometer Marks Off");
+ }
+ }
+ break;
+
+ case Key_C:
+ if (down)
+ {
+ // Toggle chromatic aberration correction on/off.
+ RenderDevice::PostProcessShader shader = pRender->GetPostProcessShader();
+
+ if (shader == RenderDevice::PostProcessShader_Distortion)
+ {
+ pRender->SetPostProcessShader(RenderDevice::PostProcessShader_DistortionAndChromAb);
+ SetAdjustMessage("Chromatic Aberration Correction On");
+ }
+ else if (shader == RenderDevice::PostProcessShader_DistortionAndChromAb)
+ {
+ pRender->SetPostProcessShader(RenderDevice::PostProcessShader_Distortion);
+ SetAdjustMessage("Chromatic Aberration Correction Off");
+ }
+ else
+ OVR_ASSERT(false);
+ }
+ break;
+
+ case Key_P:
+ if (down)
+ {
+ // Toggle motion prediction.
+ if (SFusion.IsPredictionEnabled())
+ {
+ SFusion.SetPredictionEnabled(false);
+ SetAdjustMessage("Motion Prediction Off");
+ }
+ else
+ {
+ SFusion.SetPredictionEnabled(true);
+ SetAdjustMessage("Motion Prediction On");
+ }
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void OculusWorldDemoApp::OnIdle()
+{
+
+ double curtime = pPlatform->GetAppTime();
+ float dt = float(curtime - LastUpdate);
+ LastUpdate = curtime;
+
+ // Update gamepad.
+ GamepadState gamepadState;
+ if (GetPlatformCore()->GetGamepadManager()->GetGamepadState(0, &gamepadState))
+ {
+ GamepadStateChanged(gamepadState);
+ }
+
+
+ if (LoadingState == LoadingState_DoLoad)
+ {
+ PopulateScene(MainFilePath.ToCStr());
+ LoadingState = LoadingState_Finished;
+ return;
+ }
+
+ // Check if any new devices were connected.
+ {
+ bool queueIsEmpty = false;
+ while (!queueIsEmpty)
+ {
+ DeviceStatusNotificationDesc desc;
+
+ {
+ Lock::Locker lock(pManager->GetHandlerLock());
+ if (DeviceStatusNotificationsQueue.GetSize() == 0)
+ break;
+ desc = DeviceStatusNotificationsQueue.Front();
+
+ // We can't call Clear under the lock since this may introduce a dead lock:
+ // this thread is locked by HandlerLock and the Clear might cause
+ // call of Device->Release, which will use Manager->DeviceLock. The bkg
+ // thread is most likely locked by opposite way:
+ // Manager->DeviceLock ==> HandlerLock, therefore - a dead lock.
+ // So, just grab the first element, save a copy of it and remove
+ // the element (Device->Release won't be called since we made a copy).
+
+ DeviceStatusNotificationsQueue.RemoveAt(0);
+ queueIsEmpty = (DeviceStatusNotificationsQueue.GetSize() == 0);
+ }
+
+ bool wasAlreadyCreated = desc.Handle.IsCreated();
+
+ if (desc.Action == Message_DeviceAdded)
+ {
+ switch(desc.Handle.GetType())
+ {
+ case Device_Sensor:
+ if (desc.Handle.IsAvailable() && !desc.Handle.IsCreated())
+ {
+ if (!pSensor)
+ {
+ pSensor = *desc.Handle.CreateDeviceTyped<SensorDevice>();
+ SFusion.AttachToSensor(pSensor);
+ SetAdjustMessage("---------------------------\n"
+ "SENSOR connected\n"
+ "---------------------------");
+ }
+ else if (!wasAlreadyCreated)
+ {
+ LogText("A new SENSOR has been detected, but it is not currently used.");
+ }
+ }
+ break;
+ case Device_LatencyTester:
+ if (desc.Handle.IsAvailable() && !desc.Handle.IsCreated())
+ {
+ if (!pLatencyTester)
+ {
+ pLatencyTester = *desc.Handle.CreateDeviceTyped<LatencyTestDevice>();
+ LatencyUtil.SetDevice(pLatencyTester);
+ if (!wasAlreadyCreated)
+ SetAdjustMessage("----------------------------------------\n"
+ "LATENCY TESTER connected\n"
+ "----------------------------------------");
+ }
+ }
+ break;
+ case Device_HMD:
+ {
+ OVR::HMDInfo info;
+ desc.Handle.GetDeviceInfo(&info);
+ // if strlen(info.DisplayDeviceName) == 0 then
+ // this HMD is 'fake' (created using sensor).
+ if (strlen(info.DisplayDeviceName) > 0 && (!pHMD || !info.IsSameDisplay(HMDInfo)))
+ {
+ SetAdjustMessage("------------------------\n"
+ "HMD connected\n"
+ "------------------------");
+ if (!pHMD || !desc.Handle.IsDevice(pHMD))
+ pHMD = *desc.Handle.CreateDeviceTyped<HMDDevice>();
+ // update stereo config with new HMDInfo
+ if (pHMD && pHMD->GetDeviceInfo(&HMDInfo))
+ {
+ //RenderParams.MonitorName = hmd.DisplayDeviceName;
+ SConfig.SetHMDInfo(HMDInfo);
+ }
+ LogText("HMD device added.\n");
+ }
+ break;
+ }
+ default:;
+ }
+ }
+ else if (desc.Action == Message_DeviceRemoved)
+ {
+ if (desc.Handle.IsDevice(pSensor))
+ {
+ LogText("Sensor reported device removed.\n");
+ SFusion.AttachToSensor(NULL);
+ pSensor.Clear();
+ SetAdjustMessage("-------------------------------\n"
+ "SENSOR disconnected.\n"
+ "-------------------------------");
+ }
+ else if (desc.Handle.IsDevice(pLatencyTester))
+ {
+ LogText("Latency Tester reported device removed.\n");
+ LatencyUtil.SetDevice(NULL);
+ pLatencyTester.Clear();
+ SetAdjustMessage("---------------------------------------------\n"
+ "LATENCY SENSOR disconnected.\n"
+ "---------------------------------------------");
+ }
+ else if (desc.Handle.IsDevice(pHMD))
+ {
+ if (pHMD && !pHMD->IsDisconnected())
+ {
+ SetAdjustMessage("---------------------------\n"
+ "HMD disconnected\n"
+ "---------------------------");
+ // Disconnect HMD. pSensor is used to restore 'fake' HMD device
+ // (can be NULL).
+ pHMD = pHMD->Disconnect(pSensor);
+
+ // This will initialize HMDInfo with information about configured IPD,
+ // screen size and other variables needed for correct projection.
+ // We pass HMD DisplayDeviceName into the renderer to select the
+ // correct monitor in full-screen mode.
+ if (pHMD && pHMD->GetDeviceInfo(&HMDInfo))
+ {
+ //RenderParams.MonitorName = hmd.DisplayDeviceName;
+ SConfig.SetHMDInfo(HMDInfo);
+ }
+ LogText("HMD device removed.\n");
+ }
+ }
+ }
+ else
+ OVR_ASSERT(0); // unexpected action
+ }
+ }
+
+ // If one of Stereo setting adjustment keys is pressed, adjust related state.
+ if (pAdjustFunc)
+ {
+ (this->*pAdjustFunc)(dt * AdjustDirection * (ShiftDown ? 5.0f : 1.0f));
+ }
+
+ // Process latency tester results.
+ const char* results = LatencyUtil.GetResultsString();
+ if (results != NULL)
+ {
+ LogText("LATENCY TESTER: %s\n", results);
+ }
+
+ // Have to place this as close as possible to where the HMD orientation is read.
+ LatencyUtil.ProcessInputs();
+
+ // Magnetometer calibration procedure
+ if (MagCal.IsManuallyCalibrating())
+ UpdateManualMagCalibration();
+
+ if (MagCal.IsAutoCalibrating())
+ {
+ MagCal.UpdateAutoCalibration(SFusion);
+ if (MagCal.IsCalibrated())
+ {
+ if (SFusion.IsMagReady())
+ SFusion.SetYawCorrectionEnabled(true);
+ Vector3f mc = MagCal.GetMagCenter();
+ SetAdjustMessage(" Magnetometer Calibration Complete \nCenter: %f %f %f",mc.x,mc.y,mc.z);
+ }
+ }
+
+ // Handle Sensor motion.
+ // We extract Yaw, Pitch, Roll instead of directly using the orientation
+ // to allow "additional" yaw manipulation with mouse/controller.
+ if(pSensor)
+ {
+ Quatf hmdOrient = SFusion.GetPredictedOrientation();
+
+ float yaw = 0.0f;
+ hmdOrient.GetEulerAngles<Axis_Y, Axis_X, Axis_Z>(&yaw, &Player.EyePitch, &Player.EyeRoll);
+
+ Player.EyeYaw += (yaw - Player.LastSensorYaw);
+ Player.LastSensorYaw = yaw;
+
+ // NOTE: We can get a matrix from orientation as follows:
+ // Matrix4f hmdMat(hmdOrient);
+
+ // Test logic - assign quaternion result directly to view:
+ // Quatf hmdOrient = SFusion.GetOrientation();
+ // View = Matrix4f(hmdOrient.Inverted()) * Matrix4f::Translation(-EyePos);
+ }
+
+
+ if(curtime >= NextFPSUpdate)
+ {
+ NextFPSUpdate = curtime + 1.0;
+ FPS = FrameCounter;
+ FrameCounter = 0;
+ }
+ FrameCounter++;
+
+ if(FPS < 40)
+ {
+ ConsecutiveLowFPSFrames++;
+ }
+ else
+ {
+ ConsecutiveLowFPSFrames = 0;
+ }
+
+ if(ConsecutiveLowFPSFrames > 200)
+ {
+ DropLOD();
+ ConsecutiveLowFPSFrames = 0;
+ }
+
+ Player.EyeYaw -= Player.GamepadRotate.x * dt;
+ Player.HandleCollision(dt, &CollisionModels, &GroundCollisionModels, ShiftDown);
+
+ if(!pSensor)
+ {
+ Player.EyePitch -= Player.GamepadRotate.y * dt;
+
+ const float maxPitch = ((3.1415f / 2) * 0.98f);
+ if(Player.EyePitch > maxPitch)
+ {
+ Player.EyePitch = maxPitch;
+ }
+ if(Player.EyePitch < -maxPitch)
+ {
+ Player.EyePitch = -maxPitch;
+ }
+ }
+
+ // Rotate and position View Camera, using YawPitchRoll in BodyFrame coordinates.
+ //
+ Matrix4f rollPitchYaw = Matrix4f::RotationY(Player.EyeYaw) * Matrix4f::RotationX(Player.EyePitch) *
+ Matrix4f::RotationZ(Player.EyeRoll);
+ Vector3f up = rollPitchYaw.Transform(UpVector);
+ Vector3f forward = rollPitchYaw.Transform(ForwardVector);
+
+
+ // Minimal head modeling; should be moved as an option to SensorFusion.
+ float headBaseToEyeHeight = 0.15f; // Vertical height of eye from base of head
+ float headBaseToEyeProtrusion = 0.09f; // Distance forward of eye from base of head
+
+ Vector3f eyeCenterInHeadFrame(0.0f, headBaseToEyeHeight, -headBaseToEyeProtrusion);
+ Vector3f shiftedEyePos = Player.EyePos + rollPitchYaw.Transform(eyeCenterInHeadFrame);
+ shiftedEyePos.y -= eyeCenterInHeadFrame.y; // Bring the head back down to original height
+ View = Matrix4f::LookAtRH(shiftedEyePos, shiftedEyePos + forward, up);
+
+ // Transformation without head modeling.
+ // View = Matrix4f::LookAtRH(EyePos, EyePos + forward, up);
+
+ // This is an alternative to LookAtRH:
+ // Here we transpose the rotation matrix to get its inverse.
+ // View = (Matrix4f::RotationY(EyeYaw) * Matrix4f::RotationX(EyePitch) *
+ // Matrix4f::RotationZ(EyeRoll)).Transposed() *
+ // Matrix4f::Translation(-EyePos);
+
+
+ switch(SConfig.GetStereoMode())
+ {
+ case Stereo_None:
+ Render(SConfig.GetEyeRenderParams(StereoEye_Center));
+ break;
+
+ case Stereo_LeftRight_Multipass:
+ //case Stereo_LeftDouble_Multipass:
+ Render(SConfig.GetEyeRenderParams(StereoEye_Left));
+ Render(SConfig.GetEyeRenderParams(StereoEye_Right));
+ break;
+
+ }
+
+ pRender->Present();
+ // Force GPU to flush the scene, resulting in the lowest possible latency.
+ pRender->ForceFlushGPU();
+}
+
+
+
+void OculusWorldDemoApp::UpdateManualMagCalibration()
+{
+ float tyaw,yaw, pitch, roll;
+ Quatf hmdOrient = SFusion.GetOrientation();
+ // Note that yaw and pitch are used from two different Euler angle combinations. This
+ // is done so that pitch (looking "up" or "down") is not dependent on yaw angle
+ hmdOrient.GetEulerAngles<Axis_X, Axis_Z, Axis_Y>(&pitch, &roll, &yaw);
+ hmdOrient.GetEulerAngles<Axis_Y, Axis_Z, Axis_X>(&tyaw, &roll, &pitch);
+ Vector3f mag = SFusion.GetMagnetometer();
+ float dtr = Math<float>::DegreeToRadFactor;
+
+ switch(MagCal.NumberOfSamples())
+ {
+ case 0:
+ if (MagAwaitingForwardLook)
+ SetAdjustMessage("Magnetometer Calibration\n** Step 1: Please Look Forward **\n** and Press Z When Ready **");
+ else
+ if (fabs(pitch) < 10.0f*dtr)
+ {
+ MagCal.InsertIfAcceptable(hmdOrient, mag);
+ FirstMagYaw = yaw;
+ MagAwaitingForwardLook = false;
+ SFusion.SetMagReference();
+ }
+ break;
+ case 1:
+ SetAdjustMessage("Magnetometer Calibration\n** Step 2: Please Look Up **");
+ yaw -= FirstMagYaw;
+ if (yaw < -Math<float>::Pi)
+ yaw += Math<float>::TwoPi;
+ if (yaw > Math<float>::Pi)
+ yaw -= Math<float>::TwoPi;
+ if ((pitch > 50.0f*dtr) && (fabs(yaw) < 20.0f*dtr))
+ MagCal.InsertIfAcceptable(hmdOrient, mag);
+ break;
+ case 2:
+ SetAdjustMessage("Magnetometer Calibration\n** Step 3: Please Look Left **");
+ yaw -= FirstMagYaw;
+ if (yaw < -Math<float>::Pi)
+ yaw += Math<float>::TwoPi;
+ if (yaw > Math<float>::Pi)
+ yaw -= Math<float>::TwoPi;
+ if (yaw > 60.0f*dtr)
+ MagCal.InsertIfAcceptable(hmdOrient, mag);
+ break;
+ case 3:
+ SetAdjustMessage("Magnetometer Calibration\n** Step 4: Please Look Right **");
+ yaw -= FirstMagYaw;
+ if (yaw < -Math<float>::Pi)
+ yaw += Math<float>::TwoPi;
+ if (yaw > Math<float>::Pi)
+ yaw -= Math<float>::TwoPi;
+ if (yaw < -60.0f*dtr)
+ MagCal.InsertIfAcceptable(hmdOrient, mag);
+ break;
+ case 4:
+ if (!MagCal.IsCalibrated())
+ {
+ MagCal.SetCalibration(SFusion);
+ if (SFusion.IsMagReady())
+ SFusion.SetYawCorrectionEnabled(true);
+ Vector3f mc = MagCal.GetMagCenter();
+ SetAdjustMessage(" Magnetometer Calibration and Activation \nCenter: %f %f %f\nReference Yaw: %f",
+ mc.x,mc.y,mc.z,SFusion.GetMagRefYaw());
+ }
+ }
+}
+
+static const char* HelpText =
+ "F1 \t100 NoStereo \t420 Z \t520 Manual Mag Calib\n"
+ "F2 \t100 Stereo \t420 X \t520 Auto Mag Calib\n"
+ "F3 \t100 StereoHMD \t420 ; \t520 Mag Set Ref Point\n"
+ "F4 \t100 MSAA \t420 F6 \t520 Mag Info\n"
+ "F9 \t100 FullScreen \t420 R \t520 Reset SensorFusion\n"
+ "F11 \t100 Fast FullScreen \t500 - + \t660 Adj EyeHeight\n"
+ "C \t100 Chromatic Ab \t500 [ ] \t660 Adj FOV\n"
+ "P \t100 Motion Pred \t500 Shift \t660 Adj Faster\n"
+ "N/M \t180 Adj Motion Pred\n"
+ "( / ) \t180 Adj EyeDistance"
+ ;
+
+
+enum DrawTextCenterType
+{
+ DrawText_NoCenter= 0,
+ DrawText_VCenter = 0x1,
+ DrawText_HCenter = 0x2,
+ DrawText_Center = DrawText_VCenter | DrawText_HCenter
+};
+
+static void DrawTextBox(RenderDevice* prender, float x, float y,
+ float textSize, const char* text,
+ DrawTextCenterType centerType = DrawText_NoCenter)
+{
+ float ssize[2] = {0.0f, 0.0f};
+
+ prender->MeasureText(&DejaVu, text, textSize, ssize);
+
+ // Treat 0 a VCenter.
+ if (centerType & DrawText_HCenter)
+ {
+ x = -ssize[0]/2;
+ }
+ if (centerType & DrawText_VCenter)
+ {
+ y = -ssize[1]/2;
+ }
+
+ prender->FillRect(x-0.02f, y-0.02f, x+ssize[0]+0.02f, y+ssize[1]+0.02f, Color(40,40,100,210));
+ prender->RenderText(&DejaVu, text, x, y, textSize, Color(255,255,0,210));
+}
+
+void OculusWorldDemoApp::Render(const StereoEyeParams& stereo)
+{
+ pRender->BeginScene(PostProcess);
+
+ // *** 3D - Configures Viewport/Projection and Render
+ pRender->ApplyStereoParams(stereo);
+ pRender->Clear();
+
+ pRender->SetDepthMode(true, true);
+ if (SceneMode != Scene_Grid)
+ {
+ MainScene.Render(pRender, stereo.ViewAdjust * View);
+ }
+
+ if (SceneMode == Scene_YawView)
+ {
+ Matrix4f calView = Matrix4f();
+ float viewYaw = -Player.LastSensorYaw + SFusion.GetMagRefYaw();
+ calView.M[0][0] = calView.M[2][2] = cos(viewYaw);
+ calView.M[0][2] = sin(viewYaw);
+ calView.M[2][0] = -sin(viewYaw);
+ //LogText("yaw: %f\n",SFusion.GetMagRefYaw());
+
+ if (SFusion.IsYawCorrectionInProgress())
+ YawMarkGreenScene.Render(pRender, stereo.ViewAdjust);
+ else
+ YawMarkRedScene.Render(pRender, stereo.ViewAdjust);
+
+ if (fabs(Player.EyePitch) < Math<float>::Pi * 0.33)
+ YawLinesScene.Render(pRender, stereo.ViewAdjust * calView);
+ }
+
+ // *** 2D Text & Grid - Configure Orthographic rendering.
+
+ // Render UI in 2D orthographic coordinate system that maps [-1,1] range
+ // to a readable FOV area centered at your eye and properly adjusted.
+ pRender->ApplyStereoParams2D(stereo);
+ pRender->SetDepthMode(false, false);
+
+ float unitPixel = SConfig.Get2DUnitPixel();
+ float textHeight= unitPixel * 22;
+
+ if ((SceneMode == Scene_Grid)||(SceneMode == Scene_Both))
+ { // Draw grid two pixels thick.
+ GridScene.Render(pRender, Matrix4f());
+ GridScene.Render(pRender, Matrix4f::Translation(unitPixel,unitPixel,0));
+ }
+
+ // Display Loading screen-shot in frame 0.
+ if (LoadingState != LoadingState_Finished)
+ {
+ LoadingScene.Render(pRender, Matrix4f());
+ String loadMessage = String("Loading ") + MainFilePath;
+ DrawTextBox(pRender, 0.0f, 0.25f, textHeight, loadMessage.ToCStr(), DrawText_HCenter);
+ LoadingState = LoadingState_DoLoad;
+ }
+
+ if(AdjustMessageTimeout > pPlatform->GetAppTime())
+ {
+ DrawTextBox(pRender,0.0f,0.4f, textHeight, AdjustMessage.ToCStr(), DrawText_HCenter);
+ }
+
+ switch(TextScreen)
+ {
+ case Text_Orientation:
+ {
+ char buf[256], gpustat[256];
+ OVR_sprintf(buf, sizeof(buf),
+ " Yaw:%4.0f Pitch:%4.0f Roll:%4.0f \n"
+ " FPS: %d Frame: %d \n Pos: %3.2f, %3.2f, %3.2f \n"
+ " EyeHeight: %3.2f",
+ RadToDegree(Player.EyeYaw), RadToDegree(Player.EyePitch), RadToDegree(Player.EyeRoll),
+ FPS, FrameCounter, Player.EyePos.x, Player.EyePos.y, Player.EyePos.z, Player.EyePos.y);
+ size_t texMemInMB = pRender->GetTotalTextureMemoryUsage() / 1058576;
+ if (texMemInMB)
+ {
+ OVR_sprintf(gpustat, sizeof(gpustat), "\n GPU Tex: %u MB", texMemInMB);
+ OVR_strcat(buf, sizeof(buf), gpustat);
+ }
+
+ DrawTextBox(pRender, 0.0f, -0.15f, textHeight, buf, DrawText_HCenter);
+ }
+ break;
+
+ case Text_Config:
+ {
+ char textBuff[2048];
+
+ OVR_sprintf(textBuff, sizeof(textBuff),
+ "Fov\t300 %9.4f\n"
+ "EyeDistance\t300 %9.4f\n"
+ "DistortionK0\t300 %9.4f\n"
+ "DistortionK1\t300 %9.4f\n"
+ "DistortionK2\t300 %9.4f\n"
+ "DistortionK3\t300 %9.4f\n"
+ "TexScale\t300 %9.4f",
+ SConfig.GetYFOVDegrees(),
+ SConfig.GetIPD(),
+ SConfig.GetDistortionK(0),
+ SConfig.GetDistortionK(1),
+ SConfig.GetDistortionK(2),
+ SConfig.GetDistortionK(3),
+ SConfig.GetDistortionScale());
+
+ DrawTextBox(pRender, 0.0f, 0.0f, textHeight, textBuff, DrawText_Center);
+ }
+ break;
+
+ case Text_Help:
+ DrawTextBox(pRender, 0.0f, -0.1f, textHeight, HelpText, DrawText_Center);
+
+ default:
+ break;
+ }
+
+
+ // Display colored quad if we're doing a latency test.
+ Color colorToDisplay;
+ if (LatencyUtil.DisplayScreenColor(colorToDisplay))
+ {
+ pRender->FillRect(-0.4f, -0.4f, 0.4f, 0.4f, colorToDisplay);
+ }
+
+ pRender->FinishScene();
+}
+
+
+// Sets temporarily displayed message for adjustments
+void OculusWorldDemoApp::SetAdjustMessage(const char* format, ...)
+{
+ Lock::Locker lock(pManager->GetHandlerLock());
+ char textBuff[2048];
+ va_list argList;
+ va_start(argList, format);
+ OVR_vsprintf(textBuff, sizeof(textBuff), format, argList);
+ va_end(argList);
+
+ // Message will time out in 4 seconds.
+ AdjustMessage = textBuff;
+ AdjustMessageTimeout = pPlatform->GetAppTime() + 4.0f;
+}
+
+void OculusWorldDemoApp::SetAdjustMessageTimeout(float timeout)
+{
+ AdjustMessageTimeout = pPlatform->GetAppTime() + timeout;
+}
+
+// ***** View Control Adjustments
+
+void OculusWorldDemoApp::AdjustFov(float dt)
+{
+ float esd = SConfig.GetEyeToScreenDistance() + 0.01f * dt;
+ SConfig.SetEyeToScreenDistance(esd);
+ SetAdjustMessage("ESD:%6.3f FOV: %6.3f", esd, SConfig.GetYFOVDegrees());
+}
+
+void OculusWorldDemoApp::AdjustAspect(float dt)
+{
+ float rawAspect = SConfig.GetAspect() / SConfig.GetAspectMultiplier();
+ float newAspect = SConfig.GetAspect() + 0.01f * dt;
+ SConfig.SetAspectMultiplier(newAspect / rawAspect);
+ SetAdjustMessage("Aspect: %6.3f", newAspect);
+}
+
+void OculusWorldDemoApp::AdjustDistortion(float dt, int kIndex, const char* label)
+{
+ SConfig.SetDistortionK(kIndex, SConfig.GetDistortionK(kIndex) + 0.03f * dt);
+ SetAdjustMessage("%s: %6.4f", label, SConfig.GetDistortionK(kIndex));
+}
+
+void OculusWorldDemoApp::AdjustIPD(float dt)
+{
+ SConfig.SetIPD(SConfig.GetIPD() + 0.025f * dt);
+ SetAdjustMessage("EyeDistance: %6.4f", SConfig.GetIPD());
+}
+
+void OculusWorldDemoApp::AdjustEyeHeight(float dt)
+{
+ float dist = 0.5f * dt;
+
+ Player.EyeHeight += dist;
+ Player.EyePos.y += dist;
+
+ SetAdjustMessage("EyeHeight: %4.2f", Player.EyeHeight);
+}
+
+void OculusWorldDemoApp::AdjustMotionPrediction(float dt)
+{
+ float motionPred = SFusion.GetPredictionDelta() + 0.01f * dt;
+
+ if (motionPred < 0.0f)
+ {
+ motionPred = 0.0f;
+ }
+
+ SFusion.SetPrediction(motionPred);
+
+ SetAdjustMessage("MotionPrediction: %6.3fs", motionPred);
+}
+
+
+// Loads the scene data
+void OculusWorldDemoApp::PopulateScene(const char *fileName)
+{
+ XmlHandler xmlHandler;
+ if(!xmlHandler.ReadFile(fileName, pRender, &MainScene, &CollisionModels, &GroundCollisionModels))
+ {
+ SetAdjustMessage("---------------------------------\nFILE LOAD FAILED\n---------------------------------");
+ SetAdjustMessageTimeout(10.0f);
+ }
+
+ MainScene.SetAmbient(Vector4f(1.0f, 1.0f, 1.0f, 1.0f));
+
+ // Distortion debug grid (brought up by 'G' key).
+ Ptr<Model> gridModel = *Model::CreateGrid(Vector3f(0,0,0), Vector3f(1.0f/10, 0,0), Vector3f(0,1.0f/10,0),
+ 10, 10, 5,
+ Color(0, 255, 0, 255), Color(255, 50, 50, 255) );
+ GridScene.World.Add(gridModel);
+
+ // Yaw angle marker and lines (brought up by ';' key).
+ float shifty = -0.5f;
+ Ptr<Model> yawMarkGreenModel = *Model::CreateBox(Color(0, 255, 0, 255), Vector3f(0.0f, shifty, -2.0f), Vector3f(0.05f, 0.05f, 0.05f));
+ YawMarkGreenScene.World.Add(yawMarkGreenModel);
+ Ptr<Model> yawMarkRedModel = *Model::CreateBox(Color(255, 0, 0, 255), Vector3f(0.0f, shifty, -2.0f), Vector3f(0.05f, 0.05f, 0.05f));
+ YawMarkRedScene.World.Add(yawMarkRedModel);
+
+ Ptr<Model> yawLinesModel = *new Model(Prim_Lines);
+ float r = 2.0f;
+ float theta0 = Math<float>::PiOver2;
+ float theta1 = 0.0f;
+ Color c = Color(255, 200, 200, 255);
+ for (int i = 0; i < 35; i++)
+ {
+ theta1 = theta0 + Math<float>::Pi / 18.0f;
+ yawLinesModel->AddLine(yawLinesModel->AddVertex(Vector3f(r*cos(theta0),shifty,-r*sin(theta0)),c),
+ yawLinesModel->AddVertex(Vector3f(r*cos(theta1),shifty,-r*sin(theta1)),c));
+ theta0 = theta1;
+ yawLinesModel->AddLine(yawLinesModel->AddVertex(Vector3f(r*cos(theta0),shifty,-r*sin(theta0)),c),
+ yawLinesModel->AddVertex(Vector3f(r*cos(theta0),shifty+0.1f,-r*sin(theta0)),c));
+ theta0 = theta1;
+ }
+ theta1 = theta0 + Math<float>::Pi / 18.0f;
+ yawLinesModel->AddLine(yawLinesModel->AddVertex(Vector3f(r*cos(theta0),shifty,-r*sin(theta0)),c),
+ yawLinesModel->AddVertex(Vector3f(r*cos(theta1),shifty,-r*sin(theta1)),c));
+ yawLinesModel->AddLine(yawLinesModel->AddVertex(Vector3f(0.0f,shifty+0.1f,-r),c),
+ yawLinesModel->AddVertex(Vector3f(r*sin(0.02f),shifty,-r*cos(0.02f)),c));
+ yawLinesModel->AddLine(yawLinesModel->AddVertex(Vector3f(0.0f,shifty+0.1f,-r),c),
+ yawLinesModel->AddVertex(Vector3f(r*sin(-0.02f),shifty,-r*cos(-0.02f)),c));
+ yawLinesModel->SetPosition(Vector3f(0.0f,0.0f,0.0f));
+
+ YawLinesScene.World.Add(yawLinesModel);
+}
+
+
+void OculusWorldDemoApp::PopulatePreloadScene()
+{
+ // Load-screen screen shot image
+ String fileName = MainFilePath;
+ fileName.StripExtension();
+
+ Ptr<File> imageFile = *new SysFile(fileName + "_LoadScreen.tga");
+ Ptr<Texture> imageTex;
+ if (imageFile->IsValid())
+ imageTex = *LoadTextureTga(pRender, imageFile);
+
+ // Image is rendered as a single quad.
+ if (imageTex)
+ {
+ imageTex->SetSampleMode(Sample_Anisotropic|Sample_Repeat);
+ Ptr<Model> m = *new Model(Prim_Triangles);
+ m->AddVertex(-0.5f, 0.5f, 0.0f, Color(255,255,255,255), 0.0f, 0.0f);
+ m->AddVertex( 0.5f, 0.5f, 0.0f, Color(255,255,255,255), 1.0f, 0.0f);
+ m->AddVertex( 0.5f, -0.5f, 0.0f, Color(255,255,255,255), 1.0f, 1.0f);
+ m->AddVertex(-0.5f, -0.5f, 0.0f, Color(255,255,255,255), 0.0f, 1.0f);
+ m->AddTriangle(2,1,0);
+ m->AddTriangle(0,3,2);
+
+ Ptr<ShaderFill> fill = *new ShaderFill(*pRender->CreateShaderSet());
+ fill->GetShaders()->SetShader(pRender->LoadBuiltinShader(Shader_Vertex, VShader_MVP));
+ fill->GetShaders()->SetShader(pRender->LoadBuiltinShader(Shader_Fragment, FShader_Texture));
+ fill->SetTexture(0, imageTex);
+ m->Fill = fill;
+
+ LoadingScene.World.Add(m);
+ }
+}
+
+void OculusWorldDemoApp::ClearScene()
+{
+ MainScene.Clear();
+ GridScene.Clear();
+ YawMarkGreenScene.Clear();
+ YawMarkRedScene.Clear();
+ YawLinesScene.Clear();
+}
+
+void OculusWorldDemoApp::PopulateLODFileNames()
+{
+ //OVR::String mainFilePath = MainFilePath;
+ LODFilePaths.PushBack(MainFilePath);
+ int LODIndex = 1;
+ SPInt pos = strcspn(MainFilePath.ToCStr(), ".");
+ SPInt len = strlen(MainFilePath.ToCStr());
+ SPInt diff = len - pos;
+
+ if (diff == 0)
+ return;
+
+ while(true)
+ {
+ char pathWithoutExt[250];
+ char buffer[250];
+ for(SPInt i = 0; i < pos; ++i)
+ {
+ pathWithoutExt[i] = MainFilePath[(int)i];
+ }
+ pathWithoutExt[pos] = '\0';
+ OVR_sprintf(buffer, sizeof(buffer), "%s%i.xml", pathWithoutExt, LODIndex);
+ FILE* fp = 0;
+#if defined(_MSC_VER) && (_MSC_VER >= 1400 )
+ errno_t err = fopen_s(&fp, buffer, "rb");
+ if(!fp || err)
+ {
+#else
+ fp = fopen(buffer, "rb");
+ if(!fp)
+ {
+#endif
+ break;
+ }
+ fclose(fp);
+ OVR::String result = buffer;
+ LODFilePaths.PushBack(result);
+ LODIndex++;
+ }
+}
+
+void OculusWorldDemoApp::DropLOD()
+{
+ if(CurrentLODFileIndex < (int)(LODFilePaths.GetSize() - 1))
+ {
+ ClearScene();
+ CurrentLODFileIndex++;
+ PopulateScene(LODFilePaths[CurrentLODFileIndex].ToCStr());
+ }
+}
+
+void OculusWorldDemoApp::RaiseLOD()
+{
+ if(CurrentLODFileIndex > 0)
+ {
+ ClearScene();
+ CurrentLODFileIndex--;
+ PopulateScene(LODFilePaths[CurrentLODFileIndex].ToCStr());
+ }
+}
+
+//-----------------------------------------------------------------------------
+void OculusWorldDemoApp::CycleDisplay()
+{
+ int screenCount = pPlatform->GetDisplayCount();
+
+ // If Windowed, switch to the HMD screen first in Full-Screen Mode.
+ // If already Full-Screen, cycle to next screen until we reach FirstScreenInCycle.
+
+ if (pRender->IsFullscreen())
+ {
+ // Right now, we always need to restore window before going to next screen.
+ pPlatform->SetFullscreen(RenderParams, Display_Window);
+
+ Screen++;
+ if (Screen == screenCount)
+ Screen = 0;
+
+ RenderParams.Display = pPlatform->GetDisplay(Screen);
+
+ if (Screen != FirstScreenInCycle)
+ {
+ pRender->SetParams(RenderParams);
+ pPlatform->SetFullscreen(RenderParams, Display_Fullscreen);
+ }
+ }
+ else
+ {
+ // Try to find HMD Screen, making it the first screen in full-screen Cycle.
+ FirstScreenInCycle = 0;
+
+ if (pHMD)
+ {
+ DisplayId HMD (SConfig.GetHMDInfo().DisplayDeviceName, SConfig.GetHMDInfo().DisplayId);
+ for (int i = 0; i< screenCount; i++)
+ {
+ if (pPlatform->GetDisplay(i) == HMD)
+ {
+ FirstScreenInCycle = i;
+ break;
+ }
+ }
+ }
+
+ // Switch full-screen on the HMD.
+ Screen = FirstScreenInCycle;
+ RenderParams.Display = pPlatform->GetDisplay(Screen);
+ pRender->SetParams(RenderParams);
+ pPlatform->SetFullscreen(RenderParams, Display_Fullscreen);
+ }
+}
+
+void OculusWorldDemoApp::GamepadStateChanged(const GamepadState& pad)
+{
+ Player.GamepadMove = Vector3f(pad.LX * pad.LX * (pad.LX > 0 ? 1 : -1),
+ 0,
+ pad.LY * pad.LY * (pad.LY > 0 ? -1 : 1));
+ Player.GamepadRotate = Vector3f(2 * pad.RX, -2 * pad.RY, 0);
+}
+
+
+//-------------------------------------------------------------------------------------
+
+OVR_PLATFORM_APP(OculusWorldDemoApp);
diff --git a/Samples/OculusWorldDemo/OculusWorldDemo.rc b/Samples/OculusWorldDemo/OculusWorldDemo.rc
new file mode 100644
index 0000000..49a6a5a
--- /dev/null
+++ b/Samples/OculusWorldDemo/OculusWorldDemo.rc
Binary files differ
diff --git a/Samples/OculusWorldDemo/OculusWorldDemo_Msvc2010.vcxproj b/Samples/OculusWorldDemo/OculusWorldDemo_Msvc2010.vcxproj
new file mode 100644
index 0000000..456e3b6
--- /dev/null
+++ b/Samples/OculusWorldDemo/OculusWorldDemo_Msvc2010.vcxproj
@@ -0,0 +1,211 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{8051B877-2992-4F64-8C3B-FAF88B6D83AA}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>OculusWorldDemo</RootNamespace>
+ <ProjectName>OculusWorldDemo</ProjectName>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <IntDir>$(Configuration)\Obj\</IntDir>
+ <OutDir>$(ProjectDir)$(Configuration)\</OutDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <IntDir>$(Configuration)\Obj\</IntDir>
+ <OutDir>$(ProjectDir)$(Configuration)\</OutDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <IntDir>$(Configuration)\Obj\</IntDir>
+ <OutDir>$(ProjectDir)$(Configuration)\</OutDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <IntDir>$(Configuration)\Obj\</IntDir>
+ <OutDir>$(ProjectDir)$(Configuration)\</OutDir>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level4</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>OVR_BUILD_DEBUG;WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>../../LibOVR/Include;../../3rdParty/TinyXml;../../LibOVR/Src;../../3rdParty/glext;$(DXSDK_DIR)/Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <DebugInformationFormat>OldStyle</DebugInformationFormat>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <MinimalRebuild>false</MinimalRebuild>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>libovrd.lib;dxgi.lib;d3d10.lib;d3d11.lib;d3dcompiler.lib;opengl32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalLibraryDirectories>../../LibOVR/Lib/Win32;$(DXSDK_DIR)/Lib/x86;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level4</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>OVR_BUILD_DEBUG;WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>../../LibOVR/Include;../../LibOVR/Src;../../3rdParty/glext;$(DXSDK_DIR)/Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <DebugInformationFormat>OldStyle</DebugInformationFormat>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <MinimalRebuild>false</MinimalRebuild>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>libovr64d.lib;dxgi.lib;d3d10.lib;d3d11.lib;d3dcompiler.lib;opengl32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalLibraryDirectories>../../LibOVR/Lib/x64;$(DXSDK_DIR)/Lib/x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level4</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>../../LibOVR/Include;../../3rdParty/TinyXml;../../LibOVR/Src;../../3rdParty/glext;$(DXSDK_DIR)/Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <DebugInformationFormat>OldStyle</DebugInformationFormat>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <AdditionalLibraryDirectories>../../LibOVR/Lib/Win32;$(DXSDK_DIR)/Lib/x86;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>libovr.lib;dxgi.lib;d3d10.lib;d3d11.lib;d3dcompiler.lib;opengl32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level4</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>../../LibOVR/Include;../../LibOVR/Src;../../3rdParty/glext;$(DXSDK_DIR)/Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <DebugInformationFormat>OldStyle</DebugInformationFormat>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <AdditionalLibraryDirectories>../../LibOVR/Lib/x64;$(DXSDK_DIR)/Lib/x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>libovr64.lib;dxgi.lib;d3d10.lib;d3d11.lib;d3dcompiler.lib;opengl32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="..\CommonSrc\Platform\Platform.cpp" />
+ <ClCompile Include="..\CommonSrc\Platform\Win32_Gamepad.cpp" />
+ <ClCompile Include="..\CommonSrc\Platform\Win32_Platform.cpp" />
+ <ClCompile Include="..\CommonSrc\Render\Render_LoadTextureDDS.cpp" />
+ <ClCompile Include="..\CommonSrc\Render\Render_LoadTextureTGA.cpp" />
+ <ClCompile Include="..\CommonSrc\Render\Render_Device.cpp" />
+ <ClCompile Include="..\CommonSrc\Render\Render_D3D10_Device.cpp" />
+ <ClCompile Include="..\CommonSrc\Render\Render_D3D11_Device.cpp" />
+ <ClCompile Include="..\CommonSrc\Render\Render_D3D1X_Device.cpp">
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+ </ClCompile>
+ <ClCompile Include="..\..\3rdParty\TinyXml\tinyxml2.cpp" />
+ <ClCompile Include="..\CommonSrc\Render\Render_XmlSceneLoader.cpp" />
+ <ClCompile Include="OculusWorldDemo.cpp" />
+ <ClCompile Include="Player.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\CommonSrc\Platform\Gamepad.h" />
+ <ClInclude Include="..\CommonSrc\Platform\Platform.h" />
+ <ClInclude Include="..\CommonSrc\Platform\Platform_Default.h" />
+ <ClInclude Include="..\CommonSrc\Platform\Win32_Gamepad.h" />
+ <ClInclude Include="..\CommonSrc\Platform\Win32_Platform.h" />
+ <ClInclude Include="..\CommonSrc\Render\Render_Font.h" />
+ <ClInclude Include="..\CommonSrc\Render\Render_Device.h" />
+ <ClInclude Include="..\CommonSrc\Render\Render_D3D10_Device.h" />
+ <ClInclude Include="..\CommonSrc\Render\Render_D3D11_Device.h" />
+ <ClInclude Include="..\CommonSrc\Render\Render_D3D1X_Device.h" />
+ <ClInclude Include="..\..\3rdParty\TinyXml\tinyxml2.h" />
+ <ClInclude Include="..\CommonSrc\Render\Render_XmlSceneLoader.h" />
+ <ClInclude Include="Player.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="OculusWorldDemo.rc" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/Samples/OculusWorldDemo/OculusWorldDemo_Msvc2010.vcxproj.filters b/Samples/OculusWorldDemo/OculusWorldDemo_Msvc2010.vcxproj.filters
new file mode 100644
index 0000000..f024f9a
--- /dev/null
+++ b/Samples/OculusWorldDemo/OculusWorldDemo_Msvc2010.vcxproj.filters
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="CommonSrc">
+ <UniqueIdentifier>{ba6e9e50-0655-4f12-a136-6d2a06015925}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="CommonSrc\Platform">
+ <UniqueIdentifier>{16e20d8b-eff7-454c-be85-02037c399e97}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="CommonSrc\Render">
+ <UniqueIdentifier>{1b6d51ae-a405-4f3d-be93-41a50db4f328}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\CommonSrc\Platform\Platform.cpp">
+ <Filter>CommonSrc\Platform</Filter>
+ </ClCompile>
+ <ClCompile Include="..\CommonSrc\Platform\Win32_Platform.cpp">
+ <Filter>CommonSrc\Platform</Filter>
+ </ClCompile>
+ <ClCompile Include="..\CommonSrc\Render\Render_Device.cpp">
+ <Filter>CommonSrc\Render</Filter>
+ </ClCompile>
+ <ClCompile Include="..\CommonSrc\Render\Render_D3D1X_Device.cpp">
+ <Filter>CommonSrc\Render</Filter>
+ </ClCompile>
+ <ClCompile Include="..\CommonSrc\Render\Render_D3D10_Device.cpp">
+ <Filter>CommonSrc\Render</Filter>
+ </ClCompile>
+ <ClCompile Include="OculusWorldDemo.cpp" />
+ <ClCompile Include="..\CommonSrc\Render\Render_D3D11_Device.cpp">
+ <Filter>CommonSrc\Render</Filter>
+ </ClCompile>
+ <ClCompile Include="..\CommonSrc\Render\Render_LoadTextureTGA.cpp">
+ <Filter>CommonSrc\Render</Filter>
+ </ClCompile>
+ <ClCompile Include="Player.cpp" />
+ <ClCompile Include="..\..\3rdParty\TinyXml\tinyxml2.cpp" />
+ <ClCompile Include="..\CommonSrc\Render\Render_LoadTextureDDS.cpp">
+ <Filter>CommonSrc\Render</Filter>
+ </ClCompile>
+ <ClCompile Include="..\CommonSrc\Render\Render_XmlSceneLoader.cpp">
+ <Filter>CommonSrc\Render</Filter>
+ </ClCompile>
+ <ClCompile Include="..\CommonSrc\Platform\Win32_Gamepad.cpp">
+ <Filter>CommonSrc\Platform</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\CommonSrc\Platform\Win32_Platform.h">
+ <Filter>CommonSrc\Platform</Filter>
+ </ClInclude>
+ <ClInclude Include="..\CommonSrc\Platform\Platform.h">
+ <Filter>CommonSrc\Platform</Filter>
+ </ClInclude>
+ <ClInclude Include="..\CommonSrc\Platform\Platform_Default.h">
+ <Filter>CommonSrc\Platform</Filter>
+ </ClInclude>
+ <ClInclude Include="..\CommonSrc\Render\Render_Font.h">
+ <Filter>CommonSrc\Render</Filter>
+ </ClInclude>
+ <ClInclude Include="..\CommonSrc\Render\Render_Device.h">
+ <Filter>CommonSrc\Render</Filter>
+ </ClInclude>
+ <ClInclude Include="..\CommonSrc\Render\Render_D3D1X_Device.h">
+ <Filter>CommonSrc\Render</Filter>
+ </ClInclude>
+ <ClInclude Include="..\CommonSrc\Render\Render_D3D10_Device.h">
+ <Filter>CommonSrc\Render</Filter>
+ </ClInclude>
+ <ClInclude Include="..\CommonSrc\Render\Render_D3D11_Device.h">
+ <Filter>CommonSrc\Render</Filter>
+ </ClInclude>
+ <ClInclude Include="Player.h" />
+ <ClInclude Include="..\..\3rdParty\TinyXml\tinyxml2.h" />
+ <ClInclude Include="..\CommonSrc\Render\Render_XmlSceneLoader.h">
+ <Filter>CommonSrc\Render</Filter>
+ </ClInclude>
+ <ClInclude Include="..\CommonSrc\Platform\Gamepad.h">
+ <Filter>CommonSrc\Platform</Filter>
+ </ClInclude>
+ <ClInclude Include="..\CommonSrc\Platform\Win32_Gamepad.h">
+ <Filter>CommonSrc\Platform</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="OculusWorldDemo.rc" />
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/Samples/OculusWorldDemo/Player.cpp b/Samples/OculusWorldDemo/Player.cpp
new file mode 100644
index 0000000..92b6f13
--- /dev/null
+++ b/Samples/OculusWorldDemo/Player.cpp
@@ -0,0 +1,166 @@
+/************************************************************************************
+
+Filename : Player.cpp
+Content : Player location and hit-testing logic source
+Created : October 4, 2012
+
+Copyright : Copyright 2012 Oculus, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+*************************************************************************************/
+
+#include "Player.h"
+#include <Kernel/OVR_Alg.h>
+
+Player::Player(void)
+ : EyeHeight(1.8f),
+ EyePos(7.7f, 1.8f, -1.0f),
+ EyeYaw(YawInitial), EyePitch(0), EyeRoll(0),
+ LastSensorYaw(0)
+{
+ MoveForward = MoveBack = MoveLeft = MoveRight = 0;
+ GamepadMove = Vector3f(0);
+ GamepadRotate = Vector3f(0);
+}
+
+
+Player::~Player(void)
+{
+}
+
+void Player::HandleCollision(double dt, Array<Ptr<CollisionModel> >* collisionModels,
+ Array<Ptr<CollisionModel> >* groundCollisionModels, bool shiftDown)
+{
+ if(MoveForward || MoveBack || MoveLeft || MoveRight || GamepadMove.LengthSq() > 0)
+ {
+ Vector3f orientationVector;
+ // Handle keyboard movement.
+ // This translates EyePos based on Yaw vector direction and keys pressed.
+ // Note that Pitch and Roll do not affect movement (they only affect view).
+ if(MoveForward || MoveBack || MoveLeft || MoveRight)
+ {
+ Vector3f localMoveVector(0, 0, 0);
+ Matrix4f yawRotate = Matrix4f::RotationY(EyeYaw);
+
+ if (MoveForward)
+ {
+ localMoveVector = ForwardVector;
+ }
+ else if (MoveBack)
+ {
+ localMoveVector = -ForwardVector;
+ }
+
+ if (MoveRight)
+ {
+ localMoveVector += RightVector;
+ }
+ else if (MoveLeft)
+ {
+ localMoveVector -= RightVector;
+ }
+
+ // Normalize vector so we don't move faster diagonally.
+ localMoveVector.Normalize();
+ orientationVector = yawRotate.Transform(localMoveVector);
+ }
+ else if (GamepadMove.LengthSq() > 0)
+ {
+ Matrix4f yawRotate = Matrix4f::RotationY(EyeYaw);
+ GamepadMove.Normalize();
+ orientationVector = yawRotate.Transform(GamepadMove);
+ }
+
+ float moveLength = OVR::Alg::Min<float>(MoveSpeed * (float)dt * (shiftDown ? 3.0f : 1.0f), 1.0f);
+
+ float checkLengthForward = moveLength;
+ Planef collisionPlaneForward;
+ float checkLengthLeft = moveLength;
+ Planef collisionPlaneLeft;
+ float checkLengthRight = moveLength;
+ Planef collisionPlaneRight;
+ bool gotCollision = false;
+ bool gotCollisionLeft = false;
+ bool gotCollisionRight = false;
+
+ for(unsigned int i = 0; i < collisionModels->GetSize(); ++i)
+ {
+ // Checks for collisions at eye level, which should prevent us from
+ // slipping under walls
+ if (collisionModels->At(i)->TestRay(EyePos, orientationVector, checkLengthForward,
+ &collisionPlaneForward))
+ {
+ gotCollision = true;
+ }
+
+ Matrix4f leftRotation = Matrix4f::RotationY(45 * (Math<float>::Pi / 180.0f));
+ Vector3f leftVector = leftRotation.Transform(orientationVector);
+ if (collisionModels->At(i)->TestRay(EyePos, leftVector, checkLengthLeft,
+ &collisionPlaneLeft))
+ {
+ gotCollisionLeft = true;
+ }
+ Matrix4f rightRotation = Matrix4f::RotationY(-45 * (Math<float>::Pi / 180.0f));
+ Vector3f rightVector = rightRotation.Transform(orientationVector);
+ if (collisionModels->At(i)->TestRay(EyePos, rightVector, checkLengthRight,
+ &collisionPlaneRight))
+ {
+ gotCollisionRight = true;
+ }
+ }
+
+ if (gotCollision)
+ {
+ // Project orientationVector onto the plane
+ Vector3f slideVector = orientationVector - collisionPlaneForward.N
+ * (orientationVector * collisionPlaneForward.N);
+
+ // Make sure we aren't in a corner
+ for(unsigned int j = 0; j < collisionModels->GetSize(); ++j)
+ {
+ if (collisionModels->At(j)->TestPoint(EyePos - Vector3f(0.0f, RailHeight, 0.0f) +
+ (slideVector * (moveLength))) )
+ {
+ moveLength = 0;
+ }
+ }
+ if (moveLength != 0)
+ {
+ orientationVector = slideVector;
+ }
+ }
+ // Checks for collisions at foot level, which allows us to follow terrain
+ orientationVector *= moveLength;
+ EyePos += orientationVector;
+
+ Planef collisionPlaneDown;
+ float finalDistanceDown = 10;
+
+ for(unsigned int i = 0; i < groundCollisionModels->GetSize(); ++i)
+ {
+ float checkLengthDown = 10;
+ if (groundCollisionModels->At(i)->TestRay(EyePos, Vector3f(0.0f, -1.0f, 0.0f),
+ checkLengthDown, &collisionPlaneDown))
+ {
+ finalDistanceDown = Alg::Min(finalDistanceDown, checkLengthDown);
+ }
+ }
+
+ // Maintain the minimum camera height
+ if (EyeHeight - finalDistanceDown < 1.0f)
+ {
+ EyePos.y += EyeHeight - finalDistanceDown;
+ }
+ }
+}
diff --git a/Samples/OculusWorldDemo/Player.h b/Samples/OculusWorldDemo/Player.h
new file mode 100644
index 0000000..fc762c4
--- /dev/null
+++ b/Samples/OculusWorldDemo/Player.h
@@ -0,0 +1,79 @@
+/************************************************************************************
+
+Filename : Player.h
+Content : Player location and hit-testing logic
+Created : October 4, 2012
+
+Copyright : Copyright 2012 Oculus, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+
+*************************************************************************************/
+
+#ifndef OVR_WorldDemo_Player_h
+#define OVR_WorldDemo_Player_h
+
+#include "OVR.h"
+#include "../CommonSrc/Render/Render_Device.h"
+
+using namespace OVR;
+using namespace OVR::Render;
+
+//-------------------------------------------------------------------------------------
+// The RHS coordinate system is defines as follows (as seen in perspective view):
+// Y - Up
+// Z - Back
+// X - Right
+const Vector3f UpVector(0.0f, 1.0f, 0.0f);
+const Vector3f ForwardVector(0.0f, 0.0f, -1.0f);
+const Vector3f RightVector(1.0f, 0.0f, 0.0f);
+
+// We start out looking in the positive Z (180 degree rotation).
+const float YawInitial = 3.141592f;
+const float Sensitivity = 1.0f;
+const float MoveSpeed = 3.0f; // m/s
+
+// These are used for collision detection
+const float RailHeight = 0.8f;
+
+
+//-------------------------------------------------------------------------------------
+// ***** Player
+
+// Player class describes position and movement state of the player in the 3D world.
+class Player
+{
+public:
+ // Position and look. The following apply:
+ Vector3f EyePos;
+ float EyeHeight;
+ float EyeYaw; // Rotation around Y, CCW positive when looking at RHS (X,Z) plane.
+ float EyePitch; // Pitch. If sensor is plugged in, only read from sensor.
+ float EyeRoll; // Roll, only accessible from Sensor.
+ float LastSensorYaw; // Stores previous Yaw value from to support computing delta.
+
+ // Movement state; different bits may be set based on the state of keys.
+ UByte MoveForward;
+ UByte MoveBack;
+ UByte MoveLeft;
+ UByte MoveRight;
+ Vector3f GamepadMove, GamepadRotate;
+
+ Player();
+ ~Player();
+ void HandleCollision(double dt, Array<Ptr<CollisionModel> >* collisionModels,
+ Array<Ptr<CollisionModel> >* groundCollisionModels, bool shiftDown);
+};
+
+#endif
diff --git a/Samples/SensorBox/SensorBoxTest.cpp b/Samples/SensorBox/SensorBoxTest.cpp
new file mode 100644
index 0000000..637f235
--- /dev/null
+++ b/Samples/SensorBox/SensorBoxTest.cpp
@@ -0,0 +1,506 @@
+/************************************************************************************
+
+Filename : SensorBoxTest.h
+Content : Visual orientaion sensor test app; renders a rotating box over axes.
+Created : October 1, 2012
+Authors : Michael Antonov
+
+Copyright : Copyright 2012 Oculus, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+*************************************************************************************/
+
+#include "OVR.h"
+#include "Kernel/OVR_String.h"
+
+#include "../CommonSrc/Platform/Platform_Default.h"
+#include "../CommonSrc/Render/Render_Device.h"
+#include "../CommonSrc/Platform/Gamepad.h"
+
+using namespace OVR;
+using namespace OVR::Platform;
+using namespace OVR::Render;
+
+//-------------------------------------------------------------------------------------
+// ***** SensorBoxTest Description
+
+// This application renders an axes-colored box that rotates with sensor input. App allows
+// user to toggle views for debugging purposes by pressing F1, F2, F3 keys.
+// Application further allows running multiple sensors at once to compare sensor quality.
+//
+// The Right-handed coordinate system is defines as follows (as seen in perspective view):
+// Y - Up (colored red)
+// Z - Back (Out from screen, colored blue)
+// X - Right (green)
+// All cameras are looking at the origin.
+
+// Camera view types.
+enum ViewType
+{
+ View_Perspective,
+ View_XZ_UpY,
+ View_XY_DownZ,
+ View_Count
+};
+
+
+//-------------------------------------------------------------------------------------
+
+class InputTestApp : public Application
+{
+ RenderDevice* pRender;
+
+ Ptr<DeviceManager> pManager;
+ Ptr<HMDDevice> pHMD;
+ Ptr<SensorDevice> pSensor;
+ Ptr<SensorDevice> pSensor2;
+
+ SensorFusion SFusion;
+ SensorFusion SFusion2;
+
+ double LastUpdate;
+ ViewType CurrentView;
+
+ double LastTitleUpdate;
+
+ Matrix4f Proj;
+ Matrix4f View;
+ Scene Sc;
+ Ptr<Model> pAxes; // Model of the coordinate system
+ Ptr<Container> pBox; // Rendered box
+ Ptr<Container> pBox2; // Second model (right now just lines)
+
+ // Applies specified projection/lookAt direction to the scene.
+ void SetView(ViewType view);
+
+public:
+
+ InputTestApp();
+ ~InputTestApp();
+
+ virtual int OnStartup(int argc, const char** argv);
+ virtual void OnIdle();
+
+ virtual void OnMouseMove(int x, int y, int modifiers);
+ virtual void OnKey(KeyCode key, int chr, bool down, int modifiers);
+};
+
+InputTestApp::InputTestApp()
+ : pRender(0), CurrentView(View_Perspective),
+ LastUpdate(0), LastTitleUpdate(0), pAxes(0), pBox(0)
+{
+
+}
+
+
+/*
+void UseCase()
+{
+ using namespace OVR;
+
+ OVR::System::Init();
+
+ Ptr<DeviceManager> pManager = 0;
+ Ptr<HMDDevice> pHMD = 0;
+ Ptr<SensorDevice> pSensor = 0;
+ SensorFusion FusionResult;
+
+
+ // *** Initialization - Create the first available HMD Device
+ pManager = *DeviceManager::Create();
+ pHMD = *pManager->EnumerateDevices<HMDDevice>().CreateDevice();
+ if (!pHMD)
+ return;
+ pSensor = *pHMD->GetSensor();
+
+ // Get DisplayDeviceName, ScreenWidth/Height, etc..
+ HMDInfo hmdInfo;
+ pHMD->GetDeviceInfo(&hmdInfo);
+
+ if (pSensor)
+ FusionResult.AttachToSensor(pSensor);
+
+ // *** Per Frame
+ // Get orientation quaternion to control view
+ Quatf q = FusionResult.GetOrientation();
+
+ // Create a matrix from quaternion,
+ // where elements [0][0] through [3][3] contain rotation.
+ Matrix4f bodyFrameMatrix(q);
+
+ // Get Euler angles from quaternion, in specified axis rotation order.
+ float yaw, pitch, roll;
+ q.GetEulerAngles<Axis_Y, Axis_X, Axis_Z>(&yaw, &pitch, &roll);
+
+ // *** Shutdown
+ pSensor.Clear();
+ pHMD.Clear();
+ pManager.Clear();
+
+ OVR::System::Destroy();
+}
+*/
+
+InputTestApp::~InputTestApp()
+{
+ pSensor.Clear();
+ pManager.Clear();
+
+}
+
+
+int InputTestApp::OnStartup(int argc, const char** argv)
+{
+ if (!pPlatform->SetupWindow(1200,800))
+ return 1;
+
+
+ pManager = *DeviceManager::Create();
+
+ // This initialization logic supports running two sensors at the same time.
+
+ DeviceEnumerator<SensorDevice> isensor = pManager->EnumerateDevices<SensorDevice>();
+ DeviceEnumerator<SensorDevice> oculusSensor;
+ DeviceEnumerator<SensorDevice> oculusSensor2;
+
+ while(isensor)
+ {
+ DeviceInfo di;
+ if (isensor.GetDeviceInfo(&di))
+ {
+ if (strstr(di.ProductName, "Tracker"))
+ {
+ if (!oculusSensor)
+ oculusSensor = isensor;
+ else if (!oculusSensor2)
+ oculusSensor2 = isensor;
+ }
+ }
+
+ isensor.Next();
+ }
+
+ if (oculusSensor)
+ {
+ pSensor = *oculusSensor.CreateDevice();
+
+ if (pSensor)
+ pSensor->SetRange(SensorRange(4 * 9.81f, 8 * Math<float>::Pi, 1.0f), true);
+
+ if (oculusSensor2)
+ {
+ // Second Oculus sensor, useful for comparing firmware behavior & settings.
+ pSensor2 = *oculusSensor2.CreateDevice();
+
+ if (pSensor2)
+ pSensor2->SetRange(SensorRange(4 * 9.81f, 8 * Math<float>::Pi, 1.0f), true);
+ }
+ }
+
+ oculusSensor.Clear();
+ oculusSensor2.Clear();
+
+
+ /*
+ DeviceHandle hHMD = pManager->EnumerateDevices<HMDDevice>();
+ HMDInfo hmdInfo;
+ if (hHMD)
+ {
+ hHMD.GetDeviceInfo(&hmdInfo);
+ }
+ */
+
+ if (pSensor)
+ SFusion.AttachToSensor(pSensor);
+ if (pSensor2)
+ SFusion2.AttachToSensor(pSensor2);
+
+ /*
+ // Test rotation: This give rotations clockwise (CW) while looking from
+ // origin in the direction of the axis.
+
+ Vector3f xV(1,0,0);
+ Vector3f zV(0,0,1);
+
+ Vector3f rxV = Matrix4f::RotationZ(DegreeToRad(10.0f)).Transform(xV);
+ Vector3f ryV = Matrix4f::RotationY(DegreeToRad(10.0f)).Transform(xV);
+ Vector3f rzV = Matrix4f::RotationX(DegreeToRad(10.0f)).Transform(zV);
+ */
+
+ // Report relative mouse motion (not absolute position)
+ // pPlatform->SetMouseMode(Mouse_Relative);
+
+ const char* graphics = "d3d10";
+ for (int i = 1; i < argc; i++)
+ if (!strcmp(argv[i], "-r") && i < argc-1)
+ graphics = argv[i+1];
+
+ pRender = pPlatform->SetupGraphics(OVR_DEFAULT_RENDER_DEVICE_SET, graphics,
+ RendererParams());
+
+ //WireframeFill = pRender->CreateSimpleFill(Fill::F_Wireframe);
+
+
+
+ // *** Rotating Box
+
+ pBox = *new Container;
+ pBox->Add(Ptr<Model>(
+ *Model::CreateAxisFaceColorBox(-2.0f, 2.0f, Color(0, 0xAA, 0), // x = green
+ -1.0f, 1.0f, Color(0xAA,0, 0), // y = red
+ -1.0f, 1.0f, Color(0, 0, 0xAA)) )); // z = blue
+ // Drop-down line from box, to make it easier to see differences in angle.
+ Ptr<Model> downLine = *new Model(Prim_Lines);
+ downLine->AddLine(Vertex(0.0f,-4.5f, 0.0f, 0xFFE0B0B0),
+ Vertex(0.0f, 0.0f, 0.0f, 0xFFE0B0B0));
+ pBox->Add(downLine);
+ Sc.World.Add(pBox);
+
+
+ // Secondary rotating coordinate object, if we have two values.
+ if (pSensor2)
+ {
+ pBox2 = *new Container;
+
+ // Drop-down line from box, to make it easier to see differences in angle.
+ Ptr<Model> lines = *new Model(Prim_Lines);
+ lines->AddLine(Vertex( 0.0f,-4.0f, 0.0f, 0xFFA07070), // -Y
+ Vertex( 0.0f, 0.0f, 0.0f, 0xFFA07070));
+ lines->AddLine(Vertex(-4.0f, 0.0f, 0.0f, 0xFF70A070), // -X
+ Vertex( 0.0f, 0.0f, 0.0f, 0xFF70A070));
+ lines->AddLine(Vertex( 0.0f, 0.0f,-4.0f, 0xFF7070A0), // -Z
+ Vertex( 0.0f, 0.0f, 0.0f, 0xFF7070A0));
+ pBox2->Add(lines);
+ Sc.World.Add(pBox2);
+ }
+
+
+ // *** World axis X,Y,Z rendering.
+
+ pAxes = *new Model(Prim_Lines);
+ pAxes->AddLine(Vertex(-8.0f, 0.0f, 0.0f, 0xFF40FF40),
+ Vertex( 8.0f, 0.0f, 0.0f, 0xFF40FF40)); // X
+ pAxes->AddLine(Vertex( 7.6f, 0.4f, 0.4f, 0xFF40FF40),
+ Vertex( 8.0f, 0.0f, 0.0f, 0xFF40FF40)); // X - arrow
+ pAxes->AddLine(Vertex( 7.6f,-0.4f,-0.4f, 0xFF40FF40),
+ Vertex( 8.0f, 0.0f, 0.0f, 0xFF40FF40)); // X - arrow
+
+ pAxes->AddLine(Vertex( 0.0f,-8.0f, 0.0f, 0xFFFF4040),
+ Vertex( 0.0f, 8.0f, 0.0f, 0xFFFF4040)); // Y
+ pAxes->AddLine(Vertex( 0.4f, 7.6f, 0.0f, 0xFFFF4040),
+ Vertex( 0.0f, 8.0f, 0.0f, 0xFFFF4040)); // Y - arrow
+ pAxes->AddLine(Vertex(-0.4f, 7.6f, 0.0f, 0xFFFF4040),
+ Vertex( 0.0f, 8.0f, 0.0f, 0xFFFF4040)); // Y
+
+ pAxes->AddLine(Vertex( 0.0f, 0.0f,-8.0f, 0xFF4040FF),
+ Vertex( 0.0f, 0.0f, 8.0f, 0xFF4040FF)); // Z
+ pAxes->AddLine(Vertex( 0.4f, 0.0f, 7.6f, 0xFF4040FF),
+ Vertex( 0.0f, 0.0f, 8.0f, 0xFF4040FF)); // Z - arrow
+ pAxes->AddLine(Vertex(-0.4f, 0.0f, 7.6f, 0xFF4040FF),
+ Vertex( 0.0f, 0.0f, 8.0f, 0xFF4040FF)); // Z - arrow
+ Sc.World.Add(pAxes);
+
+
+ SetView(CurrentView);
+
+
+ LastUpdate = pPlatform->GetAppTime();
+ return 0;
+}
+
+void InputTestApp::SetView(ViewType type)
+{
+ switch(type)
+ {
+ case View_Perspective:
+ View = Matrix4f::LookAtRH(Vector3f(5.0f, 4.0f, 10.0f), // eye
+ Vector3f(0.0f, 1.5f, 0.0f), // at
+ Vector3f(0.0f, 1.0f, 0.0f));
+ break;
+
+ case View_XY_DownZ: // F2
+ View = Matrix4f::LookAtRH(Vector3f(0.0f, 0.0f, 10.0f), // eye
+ Vector3f(0.0f, 0.0f, 0.0f), // at
+ Vector3f(0.0f, 1.0f, 0.0f));
+ break;
+
+ case View_XZ_UpY:
+ View = Matrix4f::LookAtRH(Vector3f(0.0f,-10.0f, 0.0f), // eye
+ Vector3f(0.0f, 0.0f, 0.0f), // at
+ Vector3f(0.0f, 0.0f, 1.0f));
+
+ break;
+ default:
+ break;
+ }
+
+ Proj = Matrix4f::PerspectiveRH(DegreeToRad(70.0f), 1280 / (float)800,
+ 0.3f, 1000.0f); // LH
+}
+
+
+void InputTestApp::OnMouseMove(int x, int y, int modifiers)
+{
+ OVR_UNUSED3(x, y, modifiers);
+}
+
+
+static float CalcDownAngleDegrees(Quatf q)
+{
+ Vector3f downVector(0.0f, -1.0f, 0.0f);
+ Vector3f val= q.Rotate(downVector);
+ return RadToDegree(downVector.Angle(val));
+}
+
+void InputTestApp::OnKey(KeyCode key, int chr, bool down, int modifiers)
+{
+ OVR_UNUSED2(chr, modifiers);
+
+ switch (key)
+ {
+ case Key_Q:
+ if (!down)
+ pPlatform->Exit(0);
+ break;
+
+ case Key_F1:
+ CurrentView = View_Perspective;
+ SetView(CurrentView);
+ //UpdateWindowTitle();
+ break;
+ case Key_F2:
+ CurrentView = View_XY_DownZ;
+ SetView(CurrentView);
+ break;
+ case Key_F3:
+ CurrentView = View_XZ_UpY;
+ SetView(CurrentView);
+ break;
+
+ case Key_R:
+ if (down)
+ {
+ SFusion.Reset();
+ SFusion2.Reset();
+ }
+ break;
+
+ case Key_H:
+ if (down && pSensor)
+ {
+ SensorDevice::CoordinateFrame coord = pSensor->GetCoordinateFrame();
+ pSensor->SetCoordinateFrame(
+ (coord == SensorDevice::Coord_Sensor) ?
+ SensorDevice::Coord_HMD : SensorDevice::Coord_Sensor);
+ SFusion.Reset();
+ SFusion2.Reset();
+ }
+ break;
+
+ case Key_G:
+ if (down)
+ {
+ SFusion.SetGravityEnabled(!SFusion.IsGravityEnabled());
+ SFusion2.SetGravityEnabled(SFusion.IsGravityEnabled());
+ }
+ break;
+
+ case Key_A:
+
+ if (down)
+ {
+ if (!pSensor2)
+ {
+ LogText("Angle: %2.3f\n", CalcDownAngleDegrees(SFusion.GetOrientation()));
+ }
+ else
+ {
+ LogText("Angle: %2.3f Secondary Sensor Angle: %2.3f\n",
+ CalcDownAngleDegrees(SFusion.GetOrientation()),
+ CalcDownAngleDegrees(SFusion2.GetOrientation()));
+ }
+ }
+ break;
+
+ /*
+ case Key_End:
+ if (!down)
+ {
+ OriAdjust = OriSensor.Conj();
+ Sc.ViewPoint.SetOrientation(Quatf());
+ }
+ break; */
+ default:
+ break;
+ }
+}
+
+void InputTestApp::OnIdle()
+{
+ double curtime = pPlatform->GetAppTime();
+ // float dt = float(LastUpdate - curtime);
+ LastUpdate = curtime;
+
+ if (pBox)
+ {
+ Quatf q = SFusion.GetOrientation();
+ pBox->SetOrientation(q);
+
+ // Test Euler conversion, alternative to the above:
+ // Vector3f euler;
+ // SFusion.GetOrientation().GetEulerABC<Axis_Y, Axis_X, Axis_Z, Rotate_CCW, Handed_R>(&euler.y, &euler.x, &euler.z);
+ // Matrix4f mat = Matrix4f::RotationY(euler.y) * Matrix4f::RotationX(euler.x) * Matrix4f::RotationZ(euler.z);
+ // pBox->SetMatrix(mat);
+
+ // Update titlebar every 20th of a second.
+ if ((curtime - LastTitleUpdate) > 0.05f)
+ {
+ char titleBuffer[512];
+ SensorDevice::CoordinateFrame coord = SensorDevice::Coord_Sensor;
+ if (pSensor)
+ coord = pSensor->GetCoordinateFrame();
+
+ OVR_sprintf(titleBuffer, 512, "OVR SensorBox %s %s Ang: %0.3f",
+ (SFusion.IsGravityEnabled() ? "" : "[Grav Off]"),
+ (coord == SensorDevice::Coord_HMD) ? "[HMD Coord]" : "",
+ CalcDownAngleDegrees(q));
+ pPlatform->SetWindowTitle(titleBuffer);
+ LastTitleUpdate = curtime;
+ }
+ }
+
+ if (pBox2)
+ {
+ pBox2->SetOrientation(SFusion2.GetOrientation());
+ }
+
+ // Render
+ int w, h;
+ pPlatform->GetWindowSize(&w, &h);
+
+ pRender->SetViewport(0, 0, w, h);
+
+ pRender->Clear();
+ pRender->BeginScene();
+
+ pRender->SetProjection(Proj);
+ pRender->SetDepthMode(1,1);
+
+ Sc.Render(pRender, View);
+
+ pRender->Present();
+
+}
+
+OVR_PLATFORM_APP(InputTestApp);
diff --git a/Samples/SensorBox/SensorBoxTest.rc b/Samples/SensorBox/SensorBoxTest.rc
new file mode 100644
index 0000000..49a6a5a
--- /dev/null
+++ b/Samples/SensorBox/SensorBoxTest.rc
Binary files differ
diff --git a/Samples/SensorBox/SensorBoxTest_Msvc2010.vcxproj b/Samples/SensorBox/SensorBoxTest_Msvc2010.vcxproj
new file mode 100644
index 0000000..dc49cd9
--- /dev/null
+++ b/Samples/SensorBox/SensorBoxTest_Msvc2010.vcxproj
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{8051B837-2982-4F64-8C3B-FAF88B6D83AB}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>SensorBoxTest</RootNamespace>
+ <ProjectName>SensorBoxTest</ProjectName>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <IntDir>$(Configuration)\Obj\</IntDir>
+ <OutDir>$(ProjectDir)$(Configuration)\</OutDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <IntDir>$(Configuration)\Obj\</IntDir>
+ <OutDir>$(ProjectDir)$(Configuration)\</OutDir>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level4</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>OVR_BUILD_DEBUG;WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>../../LibOVR/Include;../../LibOVR/Src;../../3rdParty/glext;$(DXSDK_DIR)/Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <DebugInformationFormat>OldStyle</DebugInformationFormat>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <StringPooling>
+ </StringPooling>
+ <MinimalRebuild>false</MinimalRebuild>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>libovrd.lib;dxgi.lib;d3d10.lib;d3d11.lib;d3dcompiler.lib;opengl32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalLibraryDirectories>../../LibOVR/Lib/Win32;$(DXSDK_DIR)/Lib/x86;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level4</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>../../LibOVR/Include;../../LibOVR/Src;../../3rdParty/glext;$(DXSDK_DIR)/Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <DebugInformationFormat>OldStyle</DebugInformationFormat>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <AdditionalLibraryDirectories>../../LibOVR/Lib/Win32;$(DXSDK_DIR)/Lib/x86;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>libovr.lib;dxgi.lib;d3d10.lib;d3d11.lib;d3dcompiler.lib;opengl32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="..\CommonSrc\Platform\Platform.cpp" />
+ <ClCompile Include="..\CommonSrc\Platform\Win32_Gamepad.cpp" />
+ <ClCompile Include="..\CommonSrc\Platform\Win32_Platform.cpp" />
+ <ClCompile Include="..\CommonSrc\Render\Render_Device.cpp" />
+ <ClCompile Include="..\CommonSrc\Render\Render_D3D10_Device.cpp" />
+ <ClCompile Include="..\CommonSrc\Render\Render_D3D11_Device.cpp" />
+ <ClCompile Include="..\CommonSrc\Render\Render_D3D1X_Device.cpp">
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+ </ClCompile>
+ <ClCompile Include="SensorBoxTest.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\CommonSrc\Platform\Gamepad.h" />
+ <ClInclude Include="..\CommonSrc\Platform\Platform.h" />
+ <ClInclude Include="..\CommonSrc\Platform\Platform_Default.h" />
+ <ClInclude Include="..\CommonSrc\Platform\Win32_Gamepad.h" />
+ <ClInclude Include="..\CommonSrc\Platform\Win32_Platform.h" />
+ <ClInclude Include="..\CommonSrc\Render\Render_Font.h" />
+ <ClInclude Include="..\CommonSrc\Render\Render.h" />
+ <ClInclude Include="..\CommonSrc\Render\Render_D3D10_Device.h" />
+ <ClInclude Include="..\CommonSrc\Render\Render_D3D11_Device.h" />
+ <ClInclude Include="..\CommonSrc\Render\Render_D3D1X_Device.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="SensorBoxTest.rc" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/Samples/SensorBox/SensorBoxTest_Msvc2010.vcxproj.filters b/Samples/SensorBox/SensorBoxTest_Msvc2010.vcxproj.filters
new file mode 100644
index 0000000..075a68f
--- /dev/null
+++ b/Samples/SensorBox/SensorBoxTest_Msvc2010.vcxproj.filters
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="CommonSrc">
+ <UniqueIdentifier>{ba673e50-0655-4f12-a166-6d3ab6015925}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="CommonSrc\Platform">
+ <UniqueIdentifier>{16e20aab-eff7-45ff-be85-0203ad399e97}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="CommonSrc\Render">
+ <UniqueIdentifier>{1b6db3ae-a405-4f65-be93-41a62db4fa21}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\CommonSrc\Platform\Platform.cpp">
+ <Filter>CommonSrc\Platform</Filter>
+ </ClCompile>
+ <ClCompile Include="..\CommonSrc\Platform\Win32_Platform.cpp">
+ <Filter>CommonSrc\Platform</Filter>
+ </ClCompile>
+ <ClCompile Include="..\CommonSrc\Render\Render_Device.cpp">
+ <Filter>CommonSrc\Render</Filter>
+ </ClCompile>
+ <ClCompile Include="..\CommonSrc\Render\Render_D3D1X_Device.cpp">
+ <Filter>CommonSrc\Render</Filter>
+ </ClCompile>
+ <ClCompile Include="..\CommonSrc\Render\Render_D3D10_Device.cpp">
+ <Filter>CommonSrc\Render</Filter>
+ </ClCompile>
+ <ClCompile Include="..\CommonSrc\Render\Render_D3D11_Device.cpp">
+ <Filter>CommonSrc\Render</Filter>
+ </ClCompile>
+ <ClCompile Include="SensorBoxTest.cpp" />
+ <ClCompile Include="..\CommonSrc\Platform\Win32_Gamepad.cpp">
+ <Filter>CommonSrc\Platform</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\CommonSrc\Platform\Win32_Platform.h">
+ <Filter>CommonSrc\Platform</Filter>
+ </ClInclude>
+ <ClInclude Include="..\CommonSrc\Platform\Platform.h">
+ <Filter>CommonSrc\Platform</Filter>
+ </ClInclude>
+ <ClInclude Include="..\CommonSrc\Platform\Platform_Default.h">
+ <Filter>CommonSrc\Platform</Filter>
+ </ClInclude>
+ <ClInclude Include="..\CommonSrc\Render\Render_Font.h">
+ <Filter>CommonSrc\Render</Filter>
+ </ClInclude>
+ <ClInclude Include="..\CommonSrc\Render\Render.h">
+ <Filter>CommonSrc\Render</Filter>
+ </ClInclude>
+ <ClInclude Include="..\CommonSrc\Render\Render_D3D1X_Device.h">
+ <Filter>CommonSrc\Render</Filter>
+ </ClInclude>
+ <ClInclude Include="..\CommonSrc\Render\Render_D3D10_Device.h">
+ <Filter>CommonSrc\Render</Filter>
+ </ClInclude>
+ <ClInclude Include="..\CommonSrc\Render\Render_D3D11_Device.h">
+ <Filter>CommonSrc\Render</Filter>
+ </ClInclude>
+ <ClInclude Include="..\CommonSrc\Platform\Gamepad.h">
+ <Filter>CommonSrc\Platform</Filter>
+ </ClInclude>
+ <ClInclude Include="..\CommonSrc\Platform\Win32_Gamepad.h">
+ <Filter>CommonSrc\Platform</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="SensorBoxTest.rc" />
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/Win_OculusWorldDemo.lnk b/Win_OculusWorldDemo.lnk
new file mode 100644
index 0000000..d846ead
--- /dev/null
+++ b/Win_OculusWorldDemo.lnk
Binary files differ
diff --git a/readme.txt b/readme.txt
new file mode 100644
index 0000000..35fa876
--- /dev/null
+++ b/readme.txt
@@ -0,0 +1,6 @@
+Oculus SDK
+(C) Oculus VR, Inc. 2013. All Rights Reserved.
+
+The latest version of the Oculus SDK is available at http://developer.oculusvr.com.
+
+Please see the Oculus SDK Overview in the OculusSDK/Doc/ directory for more information. \ No newline at end of file