diff --git a/3rdparty/SDL-1.3.0-5387/src/video/x11/SDL_x11opengl.c b/3rdparty/SDL-1.3.0-5387/src/video/x11/SDL_x11opengl.c index f424615642..2c6e2e7b3b 100644 --- a/3rdparty/SDL-1.3.0-5387/src/video/x11/SDL_x11opengl.c +++ b/3rdparty/SDL-1.3.0-5387/src/video/x11/SDL_x11opengl.c @@ -410,6 +410,9 @@ X11_GL_CreateContext(_THIS, SDL_Window * window) _this->gl_config.major_version, GLX_CONTEXT_MINOR_VERSION_ARB, _this->gl_config.minor_version, + //FIXME GREGORY + // Request a debug context to ease opengl development + GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_DEBUG_BIT_ARB, 0 }; diff --git a/build.sh b/build.sh index 5deb1e5c8d..0417898d66 100755 --- a/build.sh +++ b/build.sh @@ -18,23 +18,38 @@ flags="" args="$@" clean_build=false -for f in $args; do - if [ "$f" = "gsdx" ] ; then - flags="$flags -DFORCE_INTERNAL_SDL=TRUE" - fi - if [ "$f" = "dev" ] ; then - flags="$flags -DCMAKE_BUILD_TYPE=Devel" - fi - if [ "$f" = "debug" ] ; then - flags="$flags -DCMAKE_BUILD_TYPE=Debug" - fi - if [ "$f" = "release" ] ; then - flags="$flags -DCMAKE_BUILD_TYPE=Release" - fi - if [ "$f" = "clean" ] ; then - clean_build=true - fi -done +for f in $* +do + case $f in + --sdl13) + flags="$flags -DFORCE_INTERNAL_SDL=TRUE" + ;; + --dev) + flags="$flags -DCMAKE_BUILD_TYPE=Devel" + ;; + --devel) + flags="$flags -DCMAKE_BUILD_TYPE=Devel" + ;; + --debug) + flags="$flags -DCMAKE_BUILD_TYPE=Debug" + ;; + --release) + flags="$flags -DCMAKE_BUILD_TYPE=Release" + ;; + --clean) + clean_build=true + ;; + *) + # unknown option + echo "Valid options are:" + echo "--dev / --devel - Build pcsx2 as a Development build." + echo "--debug - Build pcsx2 as a Debug build." + echo "--release - Build pcsx2 as a Release build." + echo "--clean - Do a clean build." + echo "--sdl13 - Use the internal copy of sdl (needed for gsdx to use sdl)." + exit 1;; + esac +done rm install_log.txt diff --git a/cmake/SelectPcsx2Plugins.cmake b/cmake/SelectPcsx2Plugins.cmake index 4796a5344b..f0b6e95eee 100644 --- a/cmake/SelectPcsx2Plugins.cmake +++ b/cmake/SelectPcsx2Plugins.cmake @@ -125,13 +125,13 @@ endif(GTK2_FOUND) # -X11 # -PCSX2 SDL #--------------------------------------- -if(OPENGL_FOUND AND X11_FOUND AND projectSDL) +if(OPENGL_FOUND AND X11_FOUND) set(GSdx TRUE) -else(OPENGL_FOUND AND X11_FOUND AND projectSDL) +else(OPENGL_FOUND AND X11_FOUND) set(GSdx FALSE) message(STATUS "Skip build of GSdx: miss some dependencies") message(STATUS "${msg_dep_gsdx}") -endif(OPENGL_FOUND AND X11_FOUND AND projectSDL) +endif(OPENGL_FOUND AND X11_FOUND) #--------------------------------------- #--------------------------------------- diff --git a/plugins/GSdx/CMakeLists.txt b/plugins/GSdx/CMakeLists.txt index 6071e30562..7c9b213e56 100644 --- a/plugins/GSdx/CMakeLists.txt +++ b/plugins/GSdx/CMakeLists.txt @@ -18,6 +18,9 @@ set(CommonFlags -Wunused-variable -std=c++0x -fno-strict-aliasing + -DOGL_DEBUG # FIXME remove me when code is ready + # Unload of Geometry shader was fixed in Cat 12.3 (ie OpenGL version string: 4.2.11554) + #-DAMD_DRIVER_WORKAROUND ) set(OptimizationFlags @@ -25,22 +28,25 @@ set(OptimizationFlags -DNDEBUG ) +if(projectSDL) + set(SDLFlags -DENABLE_SDL_DEV) +else(projectSDL) + set(SDLFlags "") +endif(projectSDL) + # Debug - Build if(CMAKE_BUILD_TYPE STREQUAL Debug) - # add defines - add_definitions(${CommonFlags} -g -Wall) + add_definitions(${CommonFlags} ${SDLFlags} -DOGL_DEBUG -g -Wall) endif(CMAKE_BUILD_TYPE STREQUAL Debug) # Devel - Build if(CMAKE_BUILD_TYPE STREQUAL Devel) - # add defines - add_definitions(${CommonFlags} ${OptimizationFlags} -g -W) + add_definitions(${CommonFlags} ${SDLFlags} ${OptimizationFlags} -g -W) endif(CMAKE_BUILD_TYPE STREQUAL Devel) # Release - Build if(CMAKE_BUILD_TYPE STREQUAL Release) - # add defines - add_definitions(${CommonFlags} ${OptimizationFlags} -W) + add_definitions(${CommonFlags} ${SDLFlags} ${OptimizationFlags} -W) endif(CMAKE_BUILD_TYPE STREQUAL Release) set(GSdxSources @@ -60,6 +66,7 @@ set(GSdxSources GSCodeBuffer.cpp GSCrc.cpp GSDevice.cpp + GSDeviceOGL.cpp GSDeviceSDL.cpp GSDeviceSW.cpp GSDeviceNull.cpp @@ -77,7 +84,9 @@ set(GSdxSources GSPerfMon.cpp GSRasterizer.cpp GSRenderer.cpp + GSRendererHW.cpp GSRendererNull.cpp + GSRendererOGL.cpp GSRendererSW.cpp GSSetting.cpp GSSetupPrimCodeGenerator.cpp @@ -90,6 +99,9 @@ set(GSdxSources GSTexture.cpp GSTextureCache.cpp GSTextureCacheSW.cpp + GSTextureCacheOGL.cpp + GSTextureFXOGL.cpp + GSTextureOGL.cpp GSTextureNull.cpp GSTextureSW.cpp GSThread.cpp @@ -121,6 +133,7 @@ set(GSdxHeaders GSCodeBuffer.h GSCrc.h GSDevice.h + GSDeviceOGL.h GSDeviceNull.h GSDirtyRect.h GSDrawScanline.h @@ -129,12 +142,15 @@ set(GSdxHeaders GSDrawingEnvironment.h GSDump.h GSFunctionMap.h + GSLinuxLogo.h GSLocalMemory.h GSPerfMon.h GSRasterizer.h GSRenderer.h GSRendererNull.h GSRendererSW.h + GSRendererHW.h + GSRendererOGL.h GSScanlineEnvironment.h GSSetting.h GSSetupPrimCodeGenerator.h @@ -143,6 +159,7 @@ set(GSdxHeaders GSTexture.h GSTextureCache.h GSTextureCacheSW.h + GSTextureCacheOGL.h GSTextureNull.h GSThread.h GSUtil.h @@ -161,33 +178,76 @@ set(GSdxHeaders xbyak/xbyak_util.h ) -# add additional include directories include_directories(.) -# add library -add_library(${Output} SHARED - ${GSdxSources} - ${GSdxHeaders} - ) +add_library(${Output} SHARED ${GSdxSources} ${GSdxHeaders}) -# link target with X11 target_link_libraries(${Output} ${X11_LIBRARIES}) - -# link target with SDL -target_link_libraries(${Output} ${SDL_LIBRARY}) +target_link_libraries(${Output} ${GLEW_LIBRARY}) +target_link_libraries(${Output} ${OPENGL_LIBRARIES}) +if(projectSDL) + target_link_libraries(${Output} ${SDL_LIBRARY}) +endif(projectSDL) if(Linux) - # link target with gtk2 target_link_libraries(${Output} ${GTK2_LIBRARIES}) endif(Linux) -# User flags options if(NOT USER_CMAKE_LD_FLAGS STREQUAL "") target_link_libraries(${Output} "${USER_CMAKE_LD_FLAGS}") endif(NOT USER_CMAKE_LD_FLAGS STREQUAL "") if(PACKAGE_MODE) install(TARGETS ${Output} DESTINATION ${PLUGIN_DIR}) + + foreach(glsl IN ITEMS convert.glsl interlace.glsl merge.glsl tfx.glsl shadeboost.glsl) + install(FILES ${PROJECT_SOURCE_DIR}/plugins/GSdx/res/${glsl} DESTINATION ${PLUGIN_DIR}) + endforeach(glsl IN ITEMS convert.glsl interlace.glsl merge.glsl tfx.glsl shadeboost.glsl) else(PACKAGE_MODE) install(TARGETS ${Output} DESTINATION ${CMAKE_SOURCE_DIR}/bin/plugins) + + foreach(glsl IN ITEMS convert.glsl interlace.glsl merge.glsl tfx.glsl shadeboost.glsl) + install(FILES ${PROJECT_SOURCE_DIR}/plugins/GSdx/res/${glsl} DESTINATION ${CMAKE_SOURCE_DIR}/bin/plugins) + endforeach(glsl IN ITEMS convert.glsl interlace.glsl merge.glsl tfx.glsl shadeboost.glsl) +endif(PACKAGE_MODE) + +################################### Replay Loader +set(Replay pcsx2_GSReplayLoader) +set(Static GSdx-static) + +# We can have separate option for gsdx inside the player. It will only +# cost a 2nd rebuild of gsdx... +#add_definitions(${CommonFlags} ${SDLFlags} ${OptimizationFlags} -W) + +add_library(${Static} STATIC ${GSdxSources} ${GSdxHeaders}) + +add_executable(${Replay} linux_replay.cpp) + +target_link_libraries(${Static} ${OPENGL_LIBRARIES}) +target_link_libraries(${Static} ${X11_LIBRARIES}) +target_link_libraries(${Static} ${GLEW_LIBRARY}) +target_link_libraries(${Static} ${GTK2_LIBRARIES}) +if(projectSDL) + target_link_libraries(${Static} ${SDL_LIBRARY}) +endif(projectSDL) + +target_link_libraries(${Replay} ${Static}) +# Warning others lib must be linked after GSdx... +target_link_libraries(${Replay} ${OPENGL_LIBRARIES}) +target_link_libraries(${Replay} ${X11_LIBRARIES}) +target_link_libraries(${Replay} ${GLEW_LIBRARY}) +target_link_libraries(${Replay} ${GTK2_LIBRARIES}) +if(projectSDL) + target_link_libraries(${Replay} ${SDL_LIBRARY}) +endif(projectSDL) + + +if(NOT USER_CMAKE_LD_FLAGS STREQUAL "") + target_link_libraries(${Replay} "${USER_CMAKE_LD_FLAGS}") +endif(NOT USER_CMAKE_LD_FLAGS STREQUAL "") + +if(PACKAGE_MODE) + install(TARGETS ${Replay} DESTINATION bin) +else(PACKAGE_MODE) + install(TARGETS ${Replay} DESTINATION ${CMAKE_SOURCE_DIR}/bin) endif(PACKAGE_MODE) diff --git a/plugins/GSdx/GPU.cpp b/plugins/GSdx/GPU.cpp index c2764016eb..4df60190c9 100644 --- a/plugins/GSdx/GPU.cpp +++ b/plugins/GSdx/GPU.cpp @@ -118,7 +118,9 @@ EXPORT_C_(int32) GPUopen(void* hWnd) case 0: s_gpu = new GPURendererSW(new GSDevice9(), threads); break; case 1: s_gpu = new GPURendererSW(new GSDevice11(), threads); break; #endif + #ifdef ENABLE_SDL_DEV case 2: s_gpu = new GPURendererSW(new GSDeviceSDL(), threads); break; + #endif case 3: s_gpu = new GPURendererSW(new GSDeviceNull(), threads); break; //case 4: s_gpu = new GPURendererNull(new GSDeviceNull()); break; } diff --git a/plugins/GSdx/GS.cpp b/plugins/GSdx/GS.cpp index 5c8a605075..6b86719fc7 100644 --- a/plugins/GSdx/GS.cpp +++ b/plugins/GSdx/GS.cpp @@ -40,6 +40,9 @@ static HRESULT s_hr = E_FAIL; #else +#include "GSDeviceOGL.h" +#include "GSRendererOGL.h" + #include #include @@ -134,6 +137,7 @@ EXPORT_C_(int) GSinit() #endif +#ifdef ENABLE_SDL_DEV if(!SDL_WasInit(SDL_INIT_VIDEO)) { if(SDL_Init(SDL_INIT_VIDEO) < 0) @@ -141,6 +145,7 @@ EXPORT_C_(int) GSinit() return -1; } } +#endif return 0; } @@ -153,7 +158,9 @@ EXPORT_C GSshutdown() s_renderer = -1; +#ifdef ENABLE_SDL_DEV SDL_Quit(); +#endif #ifdef _WINDOWS @@ -231,12 +238,15 @@ static int _GSopen(void** dsp, char* title, int renderer, int threads = -1) switch(renderer / 3) { default: - #ifdef _WINDOWS - case 0: dev = new GSDevice9(); break; - case 1: dev = new GSDevice11(); break; - #endif - case 2: dev = new GSDeviceSDL(); break; - case 3: dev = new GSDeviceNull(); break; + #ifdef _WINDOWS + case 0: dev = new GSDevice9(); break; + case 1: dev = new GSDevice11(); break; + #endif + #ifdef ENABLE_SDL_DEV + case 2: dev = new GSDeviceSDL(); break; + #endif + case 3: dev = new GSDeviceNull(); break; + case 4: dev = new GSDeviceOGL(); break; } if(dev == NULL) @@ -249,11 +259,13 @@ static int _GSopen(void** dsp, char* title, int renderer, int threads = -1) switch(renderer % 3) { default: - #ifdef _WINDOWS case 0: +#ifdef _WINDOWS s_gs = (renderer / 3) == 0 ? (GSRenderer*)new GSRendererDX9() : (GSRenderer*)new GSRendererDX11(); +#else + s_gs = (GSRenderer*)new GSRendererOGL(); +#endif break; - #endif case 1: s_gs = new GSRendererSW(threads); break; @@ -304,9 +316,10 @@ static int _GSopen(void** dsp, char* title, int renderer, int threads = -1) { s_gs->SetMultithreaded(true); -#ifdef __LINUX__ +#ifdef _LINUX // Get the Xwindow from dsp. - s_gs->m_wnd.Attach((void*)((uint32*)(dsp)+1), false); + if( !s_gs->m_wnd.Attach((void*)((uint32*)(dsp)+1), false) ) + return -1; #else s_gs->m_wnd.Attach(*dsp, false); #endif @@ -350,7 +363,8 @@ EXPORT_C_(int) GSopen2(void** dsp, uint32 flags) int retval = _GSopen(dsp, NULL, renderer); - s_gs->SetAspectRatio(0); // PCSX2 manages the aspect ratios + if (s_gs != NULL) + s_gs->SetAspectRatio(0); // PCSX2 manages the aspect ratios return retval; } @@ -421,12 +435,41 @@ EXPORT_C GSwriteCSR(uint32 csr) EXPORT_C GSreadFIFO(uint8* mem) { +#ifdef _LINUX + // FIXME: double check which thread call this function + // See fifo2 issue below + if (theApp.GetConfig("renderer", 0) / 3 == 4) { + fprintf(stderr, "Disable FIFO1 on opengl\n"); + } + s_gs->m_wnd.AttachContext(); +#endif + s_gs->ReadFIFO(mem, 1); + +#ifdef _LINUX + s_gs->m_wnd.DetachContext(); +#endif } EXPORT_C GSreadFIFO2(uint8* mem, uint32 size) { +#ifdef _LINUX + // FIXME called from EE core thread not MTGS which cause + // invalidate data for opengl + if (theApp.GetConfig("renderer", 0) / 3 == 4) { +#ifdef OGL_DEBUG + fprintf(stderr, "Disable FIFO2(%d) on opengl\n", size); +#endif + //return; + } + s_gs->m_wnd.AttachContext(); +#endif + s_gs->ReadFIFO(mem, size); + +#ifdef _LINUX + s_gs->m_wnd.DetachContext(); +#endif } EXPORT_C GSgifTransfer(const uint8* mem, uint32 size) @@ -1201,3 +1244,217 @@ EXPORT_C GSBenchmark(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow } #endif + +#ifdef _LINUX + +#include +#include // ftime(), struct timeb + +inline unsigned long timeGetTime() +{ + timeb t; + ftime(&t); + + return (unsigned long)(t.time*1000 + t.millitm); +} + +// Note +EXPORT_C GSReplay(char* lpszCmdLine, int renderer) +{ + + +// lpszCmdLine: +// First parameter is the renderer. +// Second parameter is the gs file to load and run. + +//EXPORT_C GSReplay(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow) +#if 0 + int renderer = -1; + + { + char* start = lpszCmdLine; + char* end = NULL; + long n = strtol(lpszCmdLine, &end, 10); + if(end > start) {renderer = n; lpszCmdLine = end;} + } + + while(*lpszCmdLine == ' ') lpszCmdLine++; + + ::SetPriorityClass(::GetCurrentProcess(), HIGH_PRIORITY_CLASS); +#endif + // Allow to easyly switch between SW/HW renderer + renderer = theApp.GetConfig("renderer", 12); + if (renderer != 12 && renderer != 13) + { + fprintf(stderr, "wrong renderer selected %d\n", renderer); + return; + } + + if(FILE* fp = fopen(lpszCmdLine, "rb")) + { + //Console console("GSdx", true); + + GSinit(); + + uint8 regs[0x2000]; + GSsetBaseMem(regs); + + s_vsync = !!theApp.GetConfig("vsync", 0); + + void* hWnd = NULL; + + _GSopen((void**)&hWnd, "", renderer); + + uint32 crc; + fread(&crc, 4, 1, fp); + GSsetGameCRC(crc, 0); + + GSFreezeData fd; + fread(&fd.size, 4, 1, fp); + fd.data = new uint8[fd.size]; + fread(fd.data, fd.size, 1, fp); + GSfreeze(FREEZE_LOAD, &fd); + delete [] fd.data; + + fread(regs, 0x2000, 1, fp); + + long start = ftell(fp); + + GSvsync(1); + + struct Packet {uint8 type, param; uint32 size, addr; vector buff;}; + + list packets; + vector buff; + int type; + + while((type = fgetc(fp)) != EOF) + { + Packet* p = new Packet(); + + p->type = (uint8)type; + + switch(type) + { + case 0: + + p->param = (uint8)fgetc(fp); + + fread(&p->size, 4, 1, fp); + + switch(p->param) + { + case 0: + p->buff.resize(0x4000); + p->addr = 0x4000 - p->size; + fread(&p->buff[p->addr], p->size, 1, fp); + break; + case 1: + case 2: + case 3: + p->buff.resize(p->size); + fread(&p->buff[0], p->size, 1, fp); + break; + } + + break; + + case 1: + + p->param = (uint8)fgetc(fp); + + break; + + case 2: + + fread(&p->size, 4, 1, fp); + + break; + + case 3: + + p->buff.resize(0x2000); + + fread(&p->buff[0], 0x2000, 1, fp); + + break; + } + + packets.push_back(p); + } + + sleep(1); + + //while(IsWindowVisible(hWnd)) + //FIXME map? + int finished = 2; + while(finished > 0) + { + unsigned long start = timeGetTime(); + unsigned long frame_number = 0; + for(auto i = packets.begin(); i != packets.end(); i++) + { + Packet* p = *i; + + switch(p->type) + { + case 0: + + switch(p->param) + { + case 0: GSgifTransfer1(&p->buff[0], p->addr); break; + case 1: GSgifTransfer2(&p->buff[0], p->size / 16); break; + case 2: GSgifTransfer3(&p->buff[0], p->size / 16); break; + case 3: GSgifTransfer(&p->buff[0], p->size / 16); break; + } + + break; + + case 1: + + GSvsync(p->param); + frame_number++; + + break; + + case 2: + + if(buff.size() < p->size) buff.resize(p->size); + + GSreadFIFO2(&buff[0], p->size / 16); + + break; + + case 3: + + memcpy(regs, &p->buff[0], 0x2000); + + break; + } + } + unsigned long end = timeGetTime(); + fprintf(stderr, "The %d frames of the scene was render on %dms\n", frame_number, end - start); + fprintf(stderr, "A means of %fms by frame\n", (float)(end - start)/(float)frame_number); + + sleep(1); + finished--; + } + + + for(auto i = packets.begin(); i != packets.end(); i++) + { + delete *i; + } + + packets.clear(); + + sleep(1); + + GSclose(); + GSshutdown(); + + fclose(fp); + } +} +#endif + diff --git a/plugins/GSdx/GSDeviceOGL.cpp b/plugins/GSdx/GSDeviceOGL.cpp new file mode 100644 index 0000000000..89f50cf17d --- /dev/null +++ b/plugins/GSdx/GSDeviceOGL.cpp @@ -0,0 +1,1524 @@ +/* + * Copyright (C) 2011-2011 Gregory hainaut + * Copyright (C) 2007-2009 Gabest + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GSDeviceOGL.h" + +// TODO performance cost to investigate +// Texture attachment/glDrawBuffer. For the moment it set every draw and potentially multiple time (first time in clear, second time in rendering) +// Attachment 1 is only used with the GL_16UI format + +//#define LOUD_DEBUGGING +//#define PRINT_FRAME_NUMBER +//#define ONLY_LINES + +// It seems dual blending does not work on AMD !!! +//#define DISABLE_DUAL_BLEND + +static uint32 g_draw_count = 0; +static uint32 g_frame_count = 1; + + +GSDeviceOGL::GSDeviceOGL() + : m_free_window(false) + , m_window(NULL) + , m_pipeline(0) + , m_fbo(0) + , m_fbo_read(0) + , m_vb_sr(NULL) + , m_srv_changed(false) + , m_ss_changed(false) +{ + m_msaa = !!theApp.GetConfig("UserHacks", 0) ? theApp.GetConfig("UserHacks_MSAA", 0) : 0; + + memset(&m_merge_obj, 0, sizeof(m_merge_obj)); + memset(&m_interlace, 0, sizeof(m_interlace)); + memset(&m_convert, 0, sizeof(m_convert)); + memset(&m_date, 0, sizeof(m_date)); + memset(&m_state, 0, sizeof(m_state)); + + // Reset the debug file + FILE* f = fopen("Debug.txt","w"); + fclose(f); +} + +GSDeviceOGL::~GSDeviceOGL() +{ + // Clean vertex buffer state + delete (m_vb_sr); + + // Clean m_merge_obj + for (uint i = 0; i < 2; i++) + glDeleteProgram(m_merge_obj.ps[i]); + delete (m_merge_obj.cb); + delete (m_merge_obj.bs); + + // Clean m_interlace + for (uint i = 0; i < 2; i++) + glDeleteProgram(m_interlace.ps[i]); + delete (m_interlace.cb); + + // Clean m_convert + glDeleteProgram(m_convert.vs); + for (uint i = 0; i < 2; i++) + glDeleteProgram(m_convert.ps[i]); + glDeleteSamplers(1, &m_convert.ln); + glDeleteSamplers(1, &m_convert.pt); + delete m_convert.dss; + delete m_convert.bs; + + // Clean m_date + delete m_date.dss; + delete m_date.bs; + + // Clean various opengl allocation + glDeleteProgramPipelines(1, &m_pipeline); + glDeleteFramebuffers(1, &m_fbo); + glDeleteFramebuffers(1, &m_fbo_read); + + // Delete HW FX + delete m_vs_cb; + delete m_ps_cb; + glDeleteSamplers(1, &m_rt_ss); + delete m_vb; + + for (auto it = m_vs.begin(); it != m_vs.end() ; it++) glDeleteProgram(it->second); + for (auto it = m_gs.begin(); it != m_gs.end() ; it++) glDeleteProgram(it->second); + for (auto it = m_ps.begin(); it != m_ps.end() ; it++) glDeleteProgram(it->second); + for (auto it = m_ps_ss.begin(); it != m_ps_ss.end() ; it++) glDeleteSamplers(1, &it->second); + m_vs.clear(); + m_gs.clear(); + m_ps.clear(); + m_ps_ss.clear(); + m_om_dss.clear(); + m_om_bs.clear(); +} + +GSTexture* GSDeviceOGL::CreateSurface(int type, int w, int h, bool msaa, int format) +{ + // A wrapper to call GSTextureOGL, with the different kind of parameter + GSTextureOGL* t = NULL; + t = new GSTextureOGL(type, w, h, msaa, format, m_fbo_read); + + switch(type) + { + case GSTexture::RenderTarget: + ClearRenderTarget(t, 0); + break; + case GSTexture::DepthStencil: + ClearDepth(t, 0); + //FIXME might be need to clear the stencil too + break; + } + return t; +} + +GSTexture* GSDeviceOGL::FetchSurface(int type, int w, int h, bool msaa, int format) +{ + // FIXME: keep DX code. Do not know how work msaa but not important for the moment + // Current config give only 0 or 1 +#if 0 + if(m_msaa < 2) { + msaa = false; + } +#endif + msaa = false; + + return GSDevice::FetchSurface(type, w, h, msaa, format); +} + +bool GSDeviceOGL::Create(GSWnd* wnd) +{ + if (m_window == NULL) { + // FIXME...... + // GLEW's problem is that it calls glGetString(GL_EXTENSIONS) which causes GL_INVALID_ENUM on GL 3.2 forward compatible context as soon as glewInit() is called. It also doesn't fetch the function pointers. The solution is for GLEW to use glGetStringi instead. + // The current version of GLEW is 1.7.0 but they still haven't corrected it. The only fix is to use glewExperimental for now : + //NOTE: I'm not sure experimental work on 1.6 ... + glewExperimental=true; + const int glew_ok = glewInit(); + if (glew_ok != GLEW_OK) + { + // FIXME:proper logging + fprintf(stderr, "Error: Failed to init glew :%s\n", glewGetErrorString(glew_ok)); + return false; + } + // Note: don't rely on glew to avoid to pull glew1.7 + // Instead we just copy/adapt the 10 lines of code + // if (!GLEW_VERSION_4_2) { + // fprintf(stderr, "4.2 is not supported!\n"); + // return false; + // } + const GLubyte* s; + s = glGetString(GL_VERSION); + if (s == NULL) return false; + + GLuint dot = 0; + while (s[dot] != '\0' && s[dot] != '.') dot++; + if (dot == 0) return false; + + GLuint major = s[dot-1]-'0'; + GLuint minor = s[dot+1]-'0'; + // Note: 4.2 crash on latest nvidia drivers! + // So only check 4.1 + // if ( (major < 4) || ( major == 4 && minor < 2 ) ) return false; + // if ( (major < 4) || ( major == 4 && minor < 1 ) ) return false; + if ( (major < 3) || ( major == 3 && minor < 3 ) ) { + fprintf(stderr, "OPENGL 3.3 is not supported\n"); + return false; + } + + if ( !glewIsSupported("GL_ARB_separate_shader_objects")) { + fprintf(stderr, "GL_ARB_separate_shader_objects is not supported\n"); + return false; + } + if ( !glewIsSupported("GL_ARB_shading_language_420pack")) { + fprintf(stderr, "GL_ARB_shading_language_420pack is not supported\n"); + //return false; + } + + + } + + // FIXME disable it when code is ready + glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB); + + m_window = wnd; + + // **************************************************************** + // Various object + // **************************************************************** + glGenProgramPipelines(1, &m_pipeline); + glBindProgramPipeline(m_pipeline); + + glGenFramebuffers(1, &m_fbo); + glGenFramebuffers(1, &m_fbo_read); + + // **************************************************************** + // Vertex buffer state + // **************************************************************** + GSInputLayoutOGL il_convert[2] = + { + {0, 4, GL_FLOAT, GL_FALSE, sizeof(GSVertexPT1), (const GLvoid*)offsetof(struct GSVertexPT1, p) }, + {1, 2, GL_FLOAT, GL_FALSE, sizeof(GSVertexPT1), (const GLvoid*)offsetof(struct GSVertexPT1, t) }, + }; + m_vb_sr = new GSVertexBufferStateOGL(sizeof(GSVertexPT1), il_convert, countof(il_convert)); + + // **************************************************************** + // convert + // **************************************************************** + CompileShaderFromSource("convert.glsl", "vs_main", GL_VERTEX_SHADER, &m_convert.vs); + CompileShaderFromSource("convert.glsl", "gs_main", GL_GEOMETRY_SHADER, &m_convert.gs); + for(int i = 0; i < countof(m_convert.ps); i++) + CompileShaderFromSource("convert.glsl", format("ps_main%d", i), GL_FRAGMENT_SHADER, &m_convert.ps[i]); + + // Note the following object are initialized to 0 so disabled. + // Note: maybe enable blend with a factor of 1 + // m_convert.dss, m_convert.bs + +#if 0 + memset(&dsd, 0, sizeof(dsd)); + + dsd.DepthEnable = false; + dsd.StencilEnable = false; + + hr = m_dev->CreateDepthStencilState(&dsd, &m_convert.dss); + + memset(&bsd, 0, sizeof(bsd)); + + bsd.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; + + hr = m_dev->CreateBlendState(&bsd, &m_convert.bs); +#endif + glGenSamplers(1, &m_convert.ln); + glSamplerParameteri(m_convert.ln, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glSamplerParameteri(m_convert.ln, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glSamplerParameteri(m_convert.ln, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + glSamplerParameteri(m_convert.ln, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glSamplerParameteri(m_convert.ln, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + // FIXME which value for GL_TEXTURE_MIN_LOD + glSamplerParameteri(m_convert.ln, GL_TEXTURE_MAX_LOD, FLT_MAX); + // FIXME: seems there is 2 possibility in opengl + // DX: sd.ComparisonFunc = D3D11_COMPARISON_NEVER; + // glSamplerParameteri(m_convert.ln, GL_TEXTURE_COMPARE_MODE, GL_NONE); + glSamplerParameteri(m_convert.ln, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); + glSamplerParameteri(m_convert.ln, GL_TEXTURE_COMPARE_FUNC, GL_NEVER); + // FIXME: need ogl extension sd.MaxAnisotropy = 16; + + + glGenSamplers(1, &m_convert.pt); + glSamplerParameteri(m_convert.pt, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glSamplerParameteri(m_convert.pt, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glSamplerParameteri(m_convert.pt, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + glSamplerParameteri(m_convert.pt, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glSamplerParameteri(m_convert.pt, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + // FIXME which value for GL_TEXTURE_MIN_LOD + glSamplerParameteri(m_convert.pt, GL_TEXTURE_MAX_LOD, FLT_MAX); + // FIXME: seems there is 2 possibility in opengl + // DX: sd.ComparisonFunc = D3D11_COMPARISON_NEVER; + // glSamplerParameteri(m_convert.pt, GL_TEXTURE_COMPARE_MODE, GL_NONE); + glSamplerParameteri(m_convert.pt, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); + glSamplerParameteri(m_convert.pt, GL_TEXTURE_COMPARE_FUNC, GL_NEVER); + // FIXME: need ogl extension sd.MaxAnisotropy = 16; + + m_convert.dss = new GSDepthStencilOGL(); + m_convert.bs = new GSBlendStateOGL(); + + // **************************************************************** + // merge + // **************************************************************** + m_merge_obj.cb = new GSUniformBufferOGL(1, sizeof(MergeConstantBuffer)); + + for(int i = 0; i < countof(m_merge_obj.ps); i++) + CompileShaderFromSource("merge.glsl", format("ps_main%d", i), GL_FRAGMENT_SHADER, &m_merge_obj.ps[i]); + + m_merge_obj.bs = new GSBlendStateOGL(); + m_merge_obj.bs->EnableBlend(); + m_merge_obj.bs->SetRGB(GL_FUNC_ADD, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + // **************************************************************** + // interlace + // **************************************************************** + m_interlace.cb = new GSUniformBufferOGL(2, sizeof(InterlaceConstantBuffer)); + + for(int i = 0; i < countof(m_interlace.ps); i++) + CompileShaderFromSource("interlace.glsl", format("ps_main%d", i), GL_FRAGMENT_SHADER, &m_interlace.ps[i]); + // **************************************************************** + // Shade boost + // **************************************************************** + m_shadeboost.cb = new GSUniformBufferOGL(6, sizeof(ShadeBoostConstantBuffer)); + + int ShadeBoost_Contrast = theApp.GetConfig("ShadeBoost_Contrast", 50); + int ShadeBoost_Brightness = theApp.GetConfig("ShadeBoost_Brightness", 50); + int ShadeBoost_Saturation = theApp.GetConfig("ShadeBoost_Saturation", 50); + std::string macro = format("#define SB_SATURATION %d\n", ShadeBoost_Saturation) + + format("#define SB_BRIGHTNESS %d\n", ShadeBoost_Brightness) + + format("#define SB_CONTRAST %d\n", ShadeBoost_Contrast); + + CompileShaderFromSource("shadeboost.glsl", "ps_main", GL_FRAGMENT_SHADER, &m_shadeboost.ps, macro); + + // **************************************************************** + // rasterization configuration + // **************************************************************** + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glDisable(GL_CULL_FACE); + glEnable(GL_SCISSOR_TEST); + // FIXME enable it when multisample code will be here + // DX: rd.MultisampleEnable = true; + glDisable(GL_MULTISAMPLE); +#ifdef ONLY_LINES + glLineWidth(5.0); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); +#endif + // Hum I don't know for those options but let's hope there are not activated +#if 0 + rd.FrontCounterClockwise = false; + rd.DepthBias = false; + rd.DepthBiasClamp = 0; + rd.SlopeScaledDepthBias = 0; + rd.DepthClipEnable = false; // ??? + rd.AntialiasedLineEnable = false; +#endif + + // TODO Later + // **************************************************************** + // fxaa (bonus) + // **************************************************************** + // FIXME need to define FXAA_GLSL_130 for the shader + // FIXME need to manually set the index... + // FIXME need dofxaa interface too + // m_fxaa.cb = new GSUniformBufferOGL(3, sizeof(FXAAConstantBuffer)); + //CompileShaderFromSource("fxaa.fx", format("ps_main", i), GL_FRAGMENT_SHADER, &m_fxaa.ps); + + // **************************************************************** + // date + // **************************************************************** + + m_date.dss = new GSDepthStencilOGL(); + m_date.dss->EnableStencil(); + m_date.dss->SetStencil(GL_ALWAYS, GL_REPLACE); + //memset(&dsd, 0, sizeof(dsd)); + + //dsd.DepthEnable = false; + //dsd.StencilEnable = true; + //dsd.StencilReadMask = 1; + //dsd.StencilWriteMask = 1; + + //dsd.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS; + //dsd.FrontFace.StencilPassOp = D3D11_STENCIL_OP_REPLACE; + //dsd.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP; + //dsd.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP; + + //m_dev->CreateDepthStencilState(&dsd, &m_date.dss); + + // FIXME are the blend state really empty + m_date.bs = new GSBlendStateOGL(); + //D3D11_BLEND_DESC blend; + + //memset(&blend, 0, sizeof(blend)); + + //m_dev->CreateBlendState(&blend, &m_date.bs); + + // **************************************************************** + // HW renderer shader + // **************************************************************** + CreateTextureFX(); + + // **************************************************************** + // Finish window setup and backbuffer + // **************************************************************** + if(!GSDevice::Create(wnd)) + return false; + + GSVector4i rect = wnd->GetClientRect(); + Reset(rect.z, rect.w); + +#if 0 + HRESULT hr = E_FAIL; + + DXGI_SWAP_CHAIN_DESC scd; + D3D11_BUFFER_DESC bd; + D3D11_SAMPLER_DESC sd; + D3D11_DEPTH_STENCIL_DESC dsd; + D3D11_RASTERIZER_DESC rd; + D3D11_BLEND_DESC bsd; + + memset(&scd, 0, sizeof(scd)); + + scd.BufferCount = 2; + scd.BufferDesc.Width = 1; + scd.BufferDesc.Height = 1; + scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + //scd.BufferDesc.RefreshRate.Numerator = 60; + //scd.BufferDesc.RefreshRate.Denominator = 1; + scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + scd.OutputWindow = (HWND)m_wnd->GetHandle(); + scd.SampleDesc.Count = 1; + scd.SampleDesc.Quality = 0; + + // Always start in Windowed mode. According to MS, DXGI just "prefers" this, and it's more or less + // required if we want to add support for dual displays later on. The fullscreen/exclusive flip + // will be issued after all other initializations are complete. + + scd.Windowed = TRUE; + + // NOTE : D3D11_CREATE_DEVICE_SINGLETHREADED + // This flag is safe as long as the DXGI's internal message pump is disabled or is on the + // same thread as the GS window (which the emulator makes sure of, if it utilizes a + // multithreaded GS). Setting the flag is a nice and easy 5% speedup on GS-intensive scenes. + + uint32 flags = D3D11_CREATE_DEVICE_SINGLETHREADED; + +#ifdef DEBUG + flags |= D3D11_CREATE_DEVICE_DEBUG; +#endif + + D3D_FEATURE_LEVEL level; + + const D3D_FEATURE_LEVEL levels[] = + { + D3D_FEATURE_LEVEL_11_0, + D3D_FEATURE_LEVEL_10_1, + D3D_FEATURE_LEVEL_10_0, + }; + + hr = D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, flags, levels, countof(levels), D3D11_SDK_VERSION, &scd, &m_swapchain, &m_dev, &level, &m_ctx); + // hr = D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_REFERENCE, NULL, flags, NULL, 0, D3D11_SDK_VERSION, &scd, &m_swapchain, &m_dev, &level, &m_ctx); +#endif + + // **************************************************************** + // The check of capability is done when context is created on openGL + // For the moment don't bother with extension, I just ask the most recent openGL version + // **************************************************************** +#if 0 + if(FAILED(hr)) return false; + + if(!SetFeatureLevel(level, true)) + { + return false; + } + + D3D11_FEATURE_DATA_D3D10_X_HARDWARE_OPTIONS options; + + hr = m_dev->CheckFeatureSupport(D3D11_FEATURE_D3D10_X_HARDWARE_OPTIONS, &options, sizeof(D3D11_FEATURE_D3D10_X_HARDWARE_OPTIONS)); + + // msaa + + for(uint32 i = 2; i <= D3D11_MAX_MULTISAMPLE_SAMPLE_COUNT; i++) + { + uint32 quality[2] = {0, 0}; + + if(SUCCEEDED(m_dev->CheckMultisampleQualityLevels(DXGI_FORMAT_R8G8B8A8_UNORM, i, &quality[0])) && quality[0] > 0 + && SUCCEEDED(m_dev->CheckMultisampleQualityLevels(DXGI_FORMAT_D32_FLOAT_S8X24_UINT, i, &quality[1])) && quality[1] > 0) + { + m_msaa_desc.Count = i; + m_msaa_desc.Quality = std::min(quality[0] - 1, quality[1] - 1); + + if(i >= m_msaa) break; + } + } + + if(m_msaa_desc.Count == 1) + { + m_msaa = 0; + } +#endif + + return true; +} + +bool GSDeviceOGL::Reset(int w, int h) +{ + if(!GSDevice::Reset(w, h)) + return false; + + // TODO + // Opengl allocate the backbuffer with the window. The render is done in the backbuffer when + // there isn't any FBO. Only a dummy texture is created to easily detect when the rendering is done + // in the backbuffer + m_backbuffer = new GSTextureOGL(GSTextureOGL::Backbuffer, w, h, false, 0, m_fbo_read); + + return true; +} + +void GSDeviceOGL::Flip() +{ + // FIXME: disable it when code is working + CheckDebugLog(); + + m_wnd->Flip(); + +#ifdef PRINT_FRAME_NUMBER + fprintf(stderr, "Draw %d (Frame %d)\n", g_draw_count, g_frame_count); +#endif +#ifdef OGL_DEBUG + if (theApp.GetConfig("debug_ogl_dump", 0) != 0) + g_frame_count++; +#endif +} + +void GSDeviceOGL::DebugBB() +{ + bool dump_me = false; + uint32 start = theApp.GetConfig("debug_ogl_dump", 0); + uint32 length = theApp.GetConfig("debug_ogl_dump_length", 5); + if ( (start != 0 && g_frame_count >= start && g_frame_count < (start + length)) ) dump_me = true; + + if (!dump_me) return; + + GLuint fbo_old = m_state.fbo; + OMSetFBO(m_fbo); + + GSVector2i size = m_backbuffer->GetSize(); + GSTexture* rt = CreateRenderTarget(size.x, size.y, false); + + static_cast(rt)->Attach(GL_COLOR_ATTACHMENT0); + + glBlitFramebuffer(0, 0, size.x, size.y, + 0, 0, size.x, size.y, + GL_COLOR_BUFFER_BIT, GL_NEAREST); + + rt->Save(format("/tmp/out_f%d__d%d__bb.bmp", g_frame_count, g_draw_count)); + + delete rt; + OMSetFBO(fbo_old); +} + +void GSDeviceOGL::DebugInput() +{ + bool dump_me = false; + uint32 start = theApp.GetConfig("debug_ogl_dump", 0); + uint32 length = theApp.GetConfig("debug_ogl_dump_length", 5); + if ( (start != 0 && g_frame_count >= start && g_frame_count < (start + length)) ) dump_me = true; + + if (!dump_me) return; + + for (auto i = 0 ; i < 3 ; i++) { + if (m_state.ps_srv[i] != NULL) { + m_state.ps_srv[i]->Save(format("/tmp/in_f%d__d%d__%d.bmp", g_frame_count, g_draw_count, i)); + } + } + //if (m_state.rtv != NULL) m_state.rtv->Save(format("/tmp/target_f%d__d%d__tex.bmp", g_frame_count, g_draw_count)); + //if (m_state.dsv != NULL) m_state.dsv->Save(format("/tmp/ds_in_%d.bmp", g_draw_count)); + + fprintf(stderr, "Draw %d (Frame %d)\n", g_draw_count, g_frame_count); + fprintf(stderr, "vs: %d ; gs: %d ; ps: %d\n", m_state.vs, m_state.gs, m_state.ps); + m_state.vb->debug(); + m_state.bs->debug(); + m_state.dss->debug(); +} + +void GSDeviceOGL::DebugOutput() +{ + CheckDebugLog(); + + bool dump_me = false; + uint32 start = theApp.GetConfig("debug_ogl_dump", 0); + uint32 length = theApp.GetConfig("debug_ogl_dump_length", 5); + if ( (start != 0 && g_frame_count >= start && g_frame_count < (start + length)) ) dump_me = true; + + if (!dump_me) return; + + if (m_state.rtv == m_backbuffer) { + m_state.rtv->Save(format("/tmp/out_f%d__d%d__back.bmp", g_frame_count, g_draw_count)); + } else { + if (m_state.rtv != NULL) m_state.rtv->Save(format("/tmp/out_f%d__d%d__tex.bmp", g_frame_count, g_draw_count)); + } + //if (m_state.dsv != NULL) m_state.dsv->Save(format("/tmp/ds_out_%d.bmp", g_draw_count)); + + fprintf(stderr, "\n"); + //DebugBB(); +} + +void GSDeviceOGL::DrawPrimitive() +{ +#ifdef OGL_DEBUG + DebugInput(); +#endif + + m_state.vb->DrawPrimitive(); + +#ifdef OGL_DEBUG + DebugOutput(); + g_draw_count++; +#endif +} + +void GSDeviceOGL::DrawIndexedPrimitive() +{ +#ifdef OGL_DEBUG + DebugInput(); +#endif + + m_state.vb->DrawIndexedPrimitive(); + +#ifdef OGL_DEBUG + DebugOutput(); + g_draw_count++; +#endif +} + +void GSDeviceOGL::DrawIndexedPrimitive(int offset, int count) +{ + ASSERT(offset + count <= m_index.count); +#ifdef OGL_DEBUG + DebugInput(); +#endif + + m_state.vb->DrawIndexedPrimitive(offset, count); + +#ifdef OGL_DEBUG + DebugOutput(); + g_draw_count++; +#endif +} + +void GSDeviceOGL::ClearRenderTarget(GSTexture* t, const GSVector4& c) +{ + GLuint fbo_old = m_state.fbo; + if (static_cast(t)->IsBackbuffer()) { + // FIXME I really not sure + OMSetFBO(0); + //glClearBufferfv(GL_COLOR, GL_LEFT, c.v); + glClearBufferfv(GL_COLOR, 0, c.v); + // code for the old interface + // glClearColor(c.x, c.y, c.z, c.w); + // glClear(GL_COLOR_BUFFER_BIT); + } else { + // FIXME1 I need to clarify this FBO attachment stuff + // I would like to avoid FBO for a basic clean operation + OMSetFBO(m_fbo); + static_cast(t)->Attach(GL_COLOR_ATTACHMENT0); + glClearBufferfv(GL_COLOR, 0, c.v); + } + OMSetFBO(fbo_old); +} + +void GSDeviceOGL::ClearRenderTarget(GSTexture* t, uint32 c) +{ + GSVector4 color = GSVector4::rgba32(c) * (1.0f / 255); + ClearRenderTarget(t, color); +} + +void GSDeviceOGL::ClearDepth(GSTexture* t, float c) +{ + GLuint fbo_old = m_state.fbo; + // FIXME I need to clarify this FBO attachment stuff + // I would like to avoid FBO for a basic clean operation + OMSetFBO(m_fbo); + static_cast(t)->Attach(GL_DEPTH_STENCIL_ATTACHMENT); + // FIXME can you clean depth and stencil separately + glClearBufferfv(GL_DEPTH, 0, &c); + OMSetFBO(fbo_old); +} + +void GSDeviceOGL::ClearStencil(GSTexture* t, uint8 c) +{ + GLuint fbo_old = m_state.fbo; + // FIXME I need to clarify this FBO attachment stuff + // I would like to avoid FBO for a basic clean operation + OMSetFBO(m_fbo); + static_cast(t)->Attach(GL_DEPTH_STENCIL_ATTACHMENT); + GLint color = c; + // FIXME can you clean depth and stencil separately + glClearBufferiv(GL_STENCIL, 0, &color); + OMSetFBO(fbo_old); +} + +GSTexture* GSDeviceOGL::CreateRenderTarget(int w, int h, bool msaa, int format) +{ + return GSDevice::CreateRenderTarget(w, h, msaa, format ? format : GL_RGBA8); +} + +GSTexture* GSDeviceOGL::CreateDepthStencil(int w, int h, bool msaa, int format) +{ + return GSDevice::CreateDepthStencil(w, h, msaa, format ? format : GL_DEPTH32F_STENCIL8); +} + +GSTexture* GSDeviceOGL::CreateTexture(int w, int h, int format) +{ + return GSDevice::CreateTexture(w, h, format ? format : GL_RGBA8); +} + +GSTexture* GSDeviceOGL::CreateOffscreen(int w, int h, int format) +{ + return GSDevice::CreateOffscreen(w, h, format ? format : GL_RGBA8); +} + +// blit a texture into an offscreen buffer +GSTexture* GSDeviceOGL::CopyOffscreen(GSTexture* src, const GSVector4& sr, int w, int h, int format) +{ + GSTexture* dst = NULL; + + if(format == 0) format = GL_RGBA8; + + if(format != GL_RGBA8 && format != GL_R16UI) + { + ASSERT(0); + + return false; + } + + // FIXME: It is possible to bypass completely offscreen-buffer on opengl but it needs some re-thinking of the code. + // For the moment mimic dx11 + GSTexture* rt = CreateRenderTarget(w, h, false, format); + if(rt) + { + GSVector4 dr(0, 0, w, h); + + if(GSTexture* src2 = src->IsMSAA() ? Resolve(src) : src) + { + StretchRect(src2, sr, rt, dr, m_convert.ps[format == GL_R16UI ? 1 : 0]); + + if(src2 != src) Recycle(src2); + } + + + GSVector4i dor(0, 0, w, h); + dst = CreateOffscreen(w, h, format); + + if (dst) CopyRect(rt, dst, dor); +#if 0 + if(dst) + { + m_ctx->CopyResource(*(GSTexture11*)dst, *(GSTexture11*)rt); + } +#endif + + Recycle(rt); + } + + return dst; + //return rt; +} + +// Copy a sub part of a texture into another +// Several question to answer did texture have same size? +// From a sub-part to the same sub-part +// From a sub-part to a full texture +void GSDeviceOGL::CopyRect(GSTexture* st, GSTexture* dt, const GSVector4i& r) +{ + if(!st || !dt) + { + ASSERT(0); + return; + } + + // GL_NV_copy_image seem like the good extension but not supported on AMD... + // Maybe opengl 4.3 ! + // FIXME check those function work as expected + // FIXME: it is an NVIDIA extension. Hopefully lastest AMD driver support it too. + // An EXT extensions might be release later. + // void CopyImageSubDataNV( + // uint srcName, enum srcTarget, int srcLevel, int srcX, int srcY, int srcZ, + // uint dstName, enum dstTarget, int dstLevel, int dstX, int dstY, int dstZ, + // sizei width, sizei height, sizei depth); + glCopyImageSubDataNV( static_cast(st)->GetID(), static_cast(st)->GetTarget(), + 0, r.x, r.y, 0, + static_cast(dt)->GetID(), static_cast(dt)->GetTarget(), + 0, r.x, r.y, 0, + r.width(), r.height(), 1); + +#if 0 + D3D11_BOX box = {r.left, r.top, 0, r.right, r.bottom, 1}; + m_ctx->CopySubresourceRegion(*(GSTexture11*)dt, 0, 0, 0, 0, *(GSTexture11*)st, 0, &box); +#endif +} + +void GSDeviceOGL::StretchRect(GSTexture* st, const GSVector4& sr, GSTexture* dt, const GSVector4& dr, int shader, bool linear) +{ + StretchRect(st, sr, dt, dr, m_convert.ps[shader], linear); +} + +void GSDeviceOGL::StretchRect(GSTexture* st, const GSVector4& sr, GSTexture* dt, const GSVector4& dr, GLuint ps, bool linear) +{ + StretchRect(st, sr, dt, dr, ps, m_convert.bs, linear); +} + +void GSDeviceOGL::StretchRect(GSTexture* st, const GSVector4& sr, GSTexture* dt, const GSVector4& dr, GLuint ps, GSBlendStateOGL* bs, bool linear) +{ + + if(!st || !dt) + { + ASSERT(0); + return; + } + + // ************************************ + // Init + // ************************************ + + BeginScene(); + + GSVector2i ds = dt->GetSize(); + + // ************************************ + // om + // ************************************ + + OMSetDepthStencilState(m_convert.dss, 0); + OMSetBlendState(bs, 0); + OMSetRenderTargets(dt, NULL); + + // ************************************ + // ia + // ************************************ + + + float left = dr.x * 2 / ds.x - 1.0f; + float top = 1.0f - dr.y * 2 / ds.y; + float right = dr.z * 2 / ds.x - 1.0f; + float bottom = 1.0f - dr.w * 2 / ds.y; + + // Flip y axis only when we render in the backbuffer + // By default everything is render in the wrong order (ie dx). + // 1/ consistency between several pass rendering (interlace) + // 2/ in case some GSdx code expect thing in dx order. + // Only flipping the backbuffer is transparent (I hope)... + GSVector4 flip_sr = sr; + if (static_cast(dt)->IsBackbuffer()) { + flip_sr.y = 1.0f - sr.y; + flip_sr.w = 1.0f - sr.w; + } + + GSVertexPT1 vertices[] = + { + {GSVector4(left, bottom, 0.5f, 1.0f), GSVector2(flip_sr.x, flip_sr.y)}, + {GSVector4(right, bottom, 0.5f, 1.0f), GSVector2(flip_sr.z, flip_sr.y)}, + {GSVector4(left, top, 0.5f, 1.0f), GSVector2(flip_sr.x, flip_sr.w)}, + {GSVector4(right, top, 0.5f, 1.0f), GSVector2(flip_sr.z, flip_sr.w)}, + }; + //fprintf(stderr, "A:%fx%f B:%fx%f\n", left, top, bottom, right); + //fprintf(stderr, "SR: %f %f %f %f\n", sr.x, sr.y, sr.z, sr.w); + + IASetVertexState(m_vb_sr); + IASetVertexBuffer(vertices, 4); + IASetPrimitiveTopology(GL_TRIANGLE_STRIP); + + // ************************************ + // vs + // ************************************ + + VSSetShader(m_convert.vs); + + // ************************************ + // gs + // ************************************ + +#ifdef AMD_DRIVER_WORKAROUND + GSSetShader(m_convert.gs); +#else + GSSetShader(0); +#endif + + // ************************************ + // ps + // ************************************ + + PSSetShaderResources(st, NULL); + PSSetSamplerState(linear ? m_convert.ln : m_convert.pt, 0); + PSSetShader(ps); + + // ************************************ + // Draw + // ************************************ + DrawPrimitive(); + + // ************************************ + // End + // ************************************ + + EndScene(); + + PSSetShaderResources(NULL, NULL); +} + +void GSDeviceOGL::DoMerge(GSTexture* st[2], GSVector4* sr, GSTexture* dt, GSVector4* dr, bool slbg, bool mmod, const GSVector4& c) +{ + ClearRenderTarget(dt, c); + + if(st[1] && !slbg) + { + StretchRect(st[1], sr[1], dt, dr[1], m_merge_obj.ps[0]); + } + + if(st[0]) + { + SetUniformBuffer(m_merge_obj.cb); + m_merge_obj.cb->upload(&c.v); + + StretchRect(st[0], sr[0], dt, dr[0], m_merge_obj.ps[mmod ? 1 : 0], m_merge_obj.bs); + } +} + +void GSDeviceOGL::DoInterlace(GSTexture* st, GSTexture* dt, int shader, bool linear, float yoffset) +{ + GSVector4 s = GSVector4(dt->GetSize()); + + GSVector4 sr(0, 0, 1, 1); + GSVector4 dr(0.0f, yoffset, s.x, s.y + yoffset); + + InterlaceConstantBuffer cb; + + cb.ZrH = GSVector2(0, 1.0f / s.y); + cb.hH = s.y / 2; + + SetUniformBuffer(m_interlace.cb); + m_interlace.cb->upload(&cb); + + StretchRect(st, sr, dt, dr, m_interlace.ps[shader], linear); +} + +void GSDeviceOGL::DoShadeBoost(GSTexture* st, GSTexture* dt) +{ + GSVector2i s = dt->GetSize(); + + GSVector4 sr(0, 0, 1, 1); + GSVector4 dr(0, 0, s.x, s.y); + + ShadeBoostConstantBuffer cb; + + cb.rcpFrame = GSVector4(1.0f / s.x, 1.0f / s.y, 0.0f, 0.0f); + cb.rcpFrameOpt = GSVector4::zero(); + + SetUniformBuffer(m_shadeboost.cb); + m_shadeboost.cb->upload(&cb); + + StretchRect(st, sr, dt, dr, m_shadeboost.ps, m_shadeboost.cb); +} + +void GSDeviceOGL::SetupDATE(GSTexture* rt, GSTexture* ds, const GSVertexPT1* vertices, bool datm) +{ + const GSVector2i& size = rt->GetSize(); + + if(GSTexture* t = CreateRenderTarget(size.x, size.y, rt->IsMSAA())) + { + // sfex3 (after the capcom logo), vf4 (first menu fading in), ffxii shadows, rumble roses shadows, persona4 shadows + + BeginScene(); + + ClearStencil(ds, 0); + + // om + + OMSetDepthStencilState(m_date.dss, 1); + OMSetBlendState(m_date.bs, 0); + OMSetRenderTargets(t, ds); + + // ia + + IASetVertexState(m_vb_sr); + IASetVertexBuffer(vertices, 4); + IASetPrimitiveTopology(GL_TRIANGLE_STRIP); + + // vs + + VSSetShader(m_convert.vs); + + // gs + +#ifdef AMD_DRIVER_WORKAROUND + GSSetShader(m_convert.gs); +#else + GSSetShader(0); +#endif + + + // ps + + GSTexture* rt2 = rt->IsMSAA() ? Resolve(rt) : rt; + + PSSetShaderResources(rt2, NULL); + PSSetSamplerState(m_convert.pt, 0); + PSSetShader(m_convert.ps[datm ? 2 : 3]); + + // + + DrawPrimitive(); + + // + + EndScene(); + + Recycle(t); + + if(rt2 != rt) Recycle(rt2); + } +} + +// copy a multisample texture to a non-texture multisample. On opengl you need 2 FBO with different level of +// sample and then do a blit. Headach expected to for the moment just drop MSAA... +GSTexture* GSDeviceOGL::Resolve(GSTexture* t) +{ + ASSERT(t != NULL && t->IsMSAA()); +#if 0 + + if(GSTexture* dst = CreateRenderTarget(t->GetWidth(), t->GetHeight(), false, t->GetFormat())) + { + dst->SetScale(t->GetScale()); + + m_ctx->ResolveSubresource(*(GSTexture11*)dst, 0, *(GSTexture11*)t, 0, (DXGI_FORMAT)t->GetFormat()); + + return dst; + } + + return NULL; +#endif + return NULL; +} + +void GSDeviceOGL::EndScene() +{ + m_state.vb->EndScene(); +} + +void GSDeviceOGL::SetUniformBuffer(GSUniformBufferOGL* cb) +{ + if (m_state.cb != cb) { + m_state.cb = cb; + cb->bind(); + } +} + +void GSDeviceOGL::IASetVertexState(GSVertexBufferStateOGL* vb) +{ + if (vb == NULL) vb = m_vb; + + if (m_state.vb != vb) { + m_state.vb = vb; + vb->bind(); + } +} + +void GSDeviceOGL::IASetVertexBuffer(const void* vertices, size_t count) +{ + m_state.vb->UploadVB(vertices, count); +} + +bool GSDeviceOGL::IAMapVertexBuffer(void** vertex, size_t stride, size_t count) +{ + return m_state.vb->MapVB(vertex, count); +} + +void GSDeviceOGL::IAUnmapVertexBuffer() +{ + m_state.vb->UnmapVB(); +} + +void GSDeviceOGL::IASetIndexBuffer(const void* index, size_t count) +{ + m_state.vb->UploadIB(index, count); +} + +void GSDeviceOGL::IASetPrimitiveTopology(GLenum topology) +{ + m_state.vb->SetTopology(topology); +} + +void GSDeviceOGL::VSSetShader(GLuint vs) +{ + if(m_state.vs != vs) + { + m_state.vs = vs; + glUseProgramStages(m_pipeline, GL_VERTEX_SHADER_BIT, vs); + } +} + +void GSDeviceOGL::GSSetShader(GLuint gs) +{ + if(m_state.gs != gs) + { + m_state.gs = gs; + glUseProgramStages(m_pipeline, GL_GEOMETRY_SHADER_BIT, gs); + } +} + +void GSDeviceOGL::PSSetShaderResources(GSTexture* sr0, GSTexture* sr1) +{ + PSSetShaderResource(0, sr0); + PSSetShaderResource(1, sr1); + //PSSetShaderResource(2, NULL); +} + +void GSDeviceOGL::PSSetShaderResource(int i, GSTexture* sr) +{ + GSTextureOGL* srv = static_cast(sr); + + if(m_state.ps_srv[i] != srv) + { + m_state.ps_srv[i] = srv; + + m_srv_changed = true; + } +} + +void GSDeviceOGL::PSSetSamplerState(GLuint ss0, GLuint ss1, GLuint ss2) +{ + if(m_state.ps_ss[0] != ss0 || m_state.ps_ss[1] != ss1) + //if(m_state.ps_ss[0] != ss0 || m_state.ps_ss[1] != ss1 || m_state.ps_ss[2] != ss2) + { + m_state.ps_ss[0] = ss0; + m_state.ps_ss[1] = ss1; + //m_state.ps_ss[2] = ss2; + + m_ss_changed = true; + } +} + +void GSDeviceOGL::PSSetShader(GLuint ps) +{ + if(m_state.ps != ps) + { + m_state.ps = ps; + glUseProgramStages(m_pipeline, GL_FRAGMENT_SHADER_BIT, ps); + } + +// Sampler and texture must be set at the same time +// 1/ select the texture unit +// glActiveTexture(GL_TEXTURE0 + 1); +// 2/ bind the texture +// glBindTexture(GL_TEXTURE_2D , brickTexture); +// 3/ sets the texture sampler in GLSL (could be useless with layout stuff) +// glUniform1i(brickSamplerId , 1); +// 4/ set the sampler state +// glBindSampler(1 , sampler); + if (m_srv_changed || m_ss_changed) { + for (uint i=0 ; i < 1; i++) { + if (m_state.ps_srv[i] != NULL) { + m_state.ps_srv[i]->EnableUnit(i); + glBindSampler(i, m_state.ps_ss[i]); + } + } + } +} + +void GSDeviceOGL::OMSetFBO(GLuint fbo, GLenum buffer) +{ + if (m_state.fbo != fbo) { + m_state.fbo = fbo; + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + // FIXME DEBUG + //if (fbo) fprintf(stderr, "FB status %x\n", glCheckFramebufferStatus(GL_FRAMEBUFFER)); + } + + if (m_state.draw != buffer) { + m_state.draw = buffer; + glDrawBuffer(buffer); + } + +} + +void GSDeviceOGL::OMSetDepthStencilState(GSDepthStencilOGL* dss, uint8 sref) +{ + uint ref = sref; + + if(m_state.dss != dss) { + m_state.dss = dss; + m_state.sref = sref; + + dss->SetupDepth(); + dss->SetupStencil(sref); + + } else if (m_state.sref != sref) { + m_state.sref = sref; + + dss->SetupStencil(sref); + } +} + +void GSDeviceOGL::OMSetBlendState(GSBlendStateOGL* bs, float bf) +{ + if( m_state.bs != bs || (m_state.bf != bf && bs->HasConstantFactor()) ) + { + m_state.bs = bs; + m_state.bf = bf; + + bs->SetupBlend(bf); + } +} + +void GSDeviceOGL::OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector4i* scissor) +{ + // Hum, need to separate 2 case, Render target fbo and render target backbuffer + // Or maybe blit final result to the backbuffer + m_state.rtv = static_cast(rt); + m_state.dsv = static_cast(ds); + + if (static_cast(rt)->IsBackbuffer()) { + assert(ds == NULL); // no depth-stencil without FBO + + OMSetFBO(0); + + } else { + assert(rt != NULL); // a render target must exists + + // FIXME DEBUG special case for GL_R16UI + if (rt->GetFormat() == GL_R16UI) { + OMSetFBO(m_fbo, GL_COLOR_ATTACHMENT1); + static_cast(rt)->Attach(GL_COLOR_ATTACHMENT1); + } else { + OMSetFBO(m_fbo, GL_COLOR_ATTACHMENT0); + static_cast(rt)->Attach(GL_COLOR_ATTACHMENT0); + } + + if (ds != NULL) + static_cast(ds)->Attach(GL_DEPTH_STENCIL_ATTACHMENT); + } + + if(m_state.viewport != rt->GetSize()) + { + m_state.viewport = rt->GetSize(); + glViewport(0, 0, rt->GetWidth(), rt->GetHeight()); + } + + GSVector4i r = scissor ? *scissor : GSVector4i(rt->GetSize()).zwxy(); + + if(!m_state.scissor.eq(r)) + { + m_state.scissor = r; + glScissor( r.x, r.y, r.width(), r.height() ); + } +} + +void GSDeviceOGL::CompileShaderFromSource(const std::string& glsl_file, const std::string& entry, GLenum type, GLuint* program, const std::string& macro_sel) +{ + // ***************************************************** + // Build a header string + // ***************************************************** + // First select the version (must be the first line so we need to generate it + std::string version = "#version 330\n#extension GL_ARB_shading_language_420pack: require\n#extension GL_ARB_separate_shader_objects : require\n"; + //std::string version = "#version 420\n"; + + // Allow to puts several shader in 1 files + std::string shader_type; + switch (type) { + case GL_VERTEX_SHADER: + shader_type = "#define VERTEX_SHADER 1\n"; + break; + case GL_GEOMETRY_SHADER: + shader_type = "#define GEOMETRY_SHADER 1\n"; + break; + case GL_FRAGMENT_SHADER: + shader_type = "#define FRAGMENT_SHADER 1\n"; + break; + default: assert(0); + } + + // Select the entry point ie the main function + std::string entry_main = format("#define %s main\n", entry.c_str()); + + std::string header = version + shader_type + entry_main + macro_sel; +#ifdef DISABLE_DUAL_BLEND + header += "#define DISABLE_DUAL_BLEND 1\n"; +#endif + + // ***************************************************** + // Read the source file + // ***************************************************** + std::string source; + std::string line; + // Each linux distributions have his rules for path so we give them the possibility to + // change it with compilation flags. -- Gregory +#ifdef PLUGIN_DIR_COMPILATION +#define xPLUGIN_DIR_str(s) PLUGIN_DIR_str(s) +#define PLUGIN_DIR_str(s) #s + const std::string shader_file = string(xPLUGIN_DIR_str(PLUGIN_DIR_COMPILATION)) + '/' + glsl_file; +#else + const std::string shader_file = string("plugins/") + glsl_file; +#endif + std::ifstream myfile(shader_file.c_str()); + if (myfile.is_open()) { + while ( myfile.good() ) + { + getline (myfile,line); + source += line; + source += '\n'; + } + myfile.close(); + } else { + fprintf(stderr, "Error opening %s: ", shader_file.c_str()); + } + + + // Note it is better to separate header and source file to have the good line number + // in the glsl compiler report + const char** sources_array = (const char**)malloc(2*sizeof(char*)); + char* source_str = (char*)malloc(source.size() + 1); + char* header_str = (char*)malloc(header.size() + 1); + sources_array[0] = header_str; + sources_array[1] = source_str; + + source.copy(source_str, source.size(), 0); + source_str[source.size()] = '\0'; + header.copy(header_str, header.size(), 0); + header_str[header.size()] = '\0'; + + *program = glCreateShaderProgramv(type, 2, sources_array); + free(source_str); + free(header_str); + free(sources_array); + + if (theApp.GetConfig("debug_ogl_shader", 1) == 1) { + // Print a nice debug log + GLint log_length = 0; + glGetProgramiv(*program, GL_INFO_LOG_LENGTH, &log_length); + + char* log = (char*)malloc(log_length); + glGetProgramInfoLog(*program, log_length, NULL, log); + + fprintf(stderr, "%s (entry %s, prog %d) :", glsl_file.c_str(), entry.c_str(), *program); + fprintf(stderr, "\n%s", macro_sel.c_str()); + fprintf(stderr, "%s\n", log); + free(log); + } +} + +void GSDeviceOGL::CheckDebugLog() +{ + unsigned int count = 64; // max. num. of messages that will be read from the log + int bufsize = 2048; + unsigned int* sources = new unsigned int[count]; + unsigned int* types = new unsigned int[count]; + unsigned int* ids = new unsigned int[count]; + unsigned int* severities = new unsigned int[count]; + int* lengths = new int[count]; + char* messageLog = new char[bufsize]; + + unsigned int retVal = glGetDebugMessageLogARB(count, bufsize, sources, types, ids, severities, lengths, messageLog); + + if(retVal > 0) + { + unsigned int pos = 0; + for(unsigned int i=0; i 2) assert(0); +} + +// (A - B) * C + D +// A: Cs/Cd/0 +// B: Cs/Cd/0 +// C: As/Ad/FIX +// D: Cs/Cd/0 + +// bogus: 0100, 0110, 0120, 0200, 0210, 0220, 1001, 1011, 1021 +// tricky: 1201, 1211, 1221 + +// Source.rgb = float3(1, 1, 1); +// 1201 Cd*(1 + As) => Source * Dest color + Dest * Source alpha +// 1211 Cd*(1 + Ad) => Source * Dest color + Dest * Dest alpha +// 1221 Cd*(1 + F) => Source * Dest color + Dest * Factor + +// Copy Dx blend table and convert it to ogl +#define D3DBLENDOP_ADD GL_FUNC_ADD +#define D3DBLENDOP_SUBTRACT GL_FUNC_SUBTRACT +#define D3DBLENDOP_REVSUBTRACT GL_FUNC_REVERSE_SUBTRACT + +#define D3DBLEND_ONE GL_ONE +#define D3DBLEND_ZERO GL_ZERO +#define D3DBLEND_INVDESTALPHA GL_ONE_MINUS_DST_ALPHA +#define D3DBLEND_DESTALPHA GL_DST_ALPHA +#define D3DBLEND_DESTCOLOR GL_DST_COLOR +#define D3DBLEND_BLENDFACTOR GL_CONSTANT_COLOR +#define D3DBLEND_INVBLENDFACTOR GL_ONE_MINUS_CONSTANT_COLOR + +#ifdef DISABLE_DUAL_BLEND + #define D3DBLEND_SRCALPHA GL_SRC_ALPHA + #define D3DBLEND_INVSRCALPHA GL_ONE_MINUS_SRC_ALPHA +#else + #define D3DBLEND_SRCALPHA GL_SRC1_ALPHA + #define D3DBLEND_INVSRCALPHA GL_ONE_MINUS_SRC1_ALPHA +#endif + +const GSDeviceOGL::D3D9Blend GSDeviceOGL::m_blendMapD3D9[3*3*3*3] = +{ + {0, D3DBLENDOP_ADD, D3DBLEND_ONE, D3DBLEND_ZERO}, // 0000: (Cs - Cs)*As + Cs ==> Cs + {0, D3DBLENDOP_ADD, D3DBLEND_ZERO, D3DBLEND_ONE}, // 0001: (Cs - Cs)*As + Cd ==> Cd + {0, D3DBLENDOP_ADD, D3DBLEND_ZERO, D3DBLEND_ZERO}, // 0002: (Cs - Cs)*As + 0 ==> 0 + {0, D3DBLENDOP_ADD, D3DBLEND_ONE, D3DBLEND_ZERO}, // 0010: (Cs - Cs)*Ad + Cs ==> Cs + {0, D3DBLENDOP_ADD, D3DBLEND_ZERO, D3DBLEND_ONE}, // 0011: (Cs - Cs)*Ad + Cd ==> Cd + {0, D3DBLENDOP_ADD, D3DBLEND_ZERO, D3DBLEND_ZERO}, // 0012: (Cs - Cs)*Ad + 0 ==> 0 + {0, D3DBLENDOP_ADD, D3DBLEND_ONE, D3DBLEND_ZERO}, // 0020: (Cs - Cs)*F + Cs ==> Cs + {0, D3DBLENDOP_ADD, D3DBLEND_ZERO, D3DBLEND_ONE}, // 0021: (Cs - Cs)*F + Cd ==> Cd + {0, D3DBLENDOP_ADD, D3DBLEND_ZERO, D3DBLEND_ZERO}, // 0022: (Cs - Cs)*F + 0 ==> 0 + {1, D3DBLENDOP_SUBTRACT, D3DBLEND_SRCALPHA, D3DBLEND_SRCALPHA}, //*0100: (Cs - Cd)*As + Cs ==> Cs*(As + 1) - Cd*As + {0, D3DBLENDOP_ADD, D3DBLEND_SRCALPHA, D3DBLEND_INVSRCALPHA}, // 0101: (Cs - Cd)*As + Cd ==> Cs*As + Cd*(1 - As) + {0, D3DBLENDOP_SUBTRACT, D3DBLEND_SRCALPHA, D3DBLEND_SRCALPHA}, // 0102: (Cs - Cd)*As + 0 ==> Cs*As - Cd*As + {1, D3DBLENDOP_SUBTRACT, D3DBLEND_DESTALPHA, D3DBLEND_DESTALPHA}, //*0110: (Cs - Cd)*Ad + Cs ==> Cs*(Ad + 1) - Cd*Ad + {0, D3DBLENDOP_ADD, D3DBLEND_DESTALPHA, D3DBLEND_INVDESTALPHA}, // 0111: (Cs - Cd)*Ad + Cd ==> Cs*Ad + Cd*(1 - Ad) + {0, D3DBLENDOP_SUBTRACT, D3DBLEND_DESTALPHA, D3DBLEND_DESTALPHA}, // 0112: (Cs - Cd)*Ad + 0 ==> Cs*Ad - Cd*Ad + {1, D3DBLENDOP_SUBTRACT, D3DBLEND_BLENDFACTOR, D3DBLEND_BLENDFACTOR}, //*0120: (Cs - Cd)*F + Cs ==> Cs*(F + 1) - Cd*F + {0, D3DBLENDOP_ADD, D3DBLEND_BLENDFACTOR, D3DBLEND_INVBLENDFACTOR}, // 0121: (Cs - Cd)*F + Cd ==> Cs*F + Cd*(1 - F) + {0, D3DBLENDOP_SUBTRACT, D3DBLEND_BLENDFACTOR, D3DBLEND_BLENDFACTOR}, // 0122: (Cs - Cd)*F + 0 ==> Cs*F - Cd*F + {1, D3DBLENDOP_ADD, D3DBLEND_SRCALPHA, D3DBLEND_ZERO}, //*0200: (Cs - 0)*As + Cs ==> Cs*(As + 1) + {0, D3DBLENDOP_ADD, D3DBLEND_SRCALPHA, D3DBLEND_ONE}, // 0201: (Cs - 0)*As + Cd ==> Cs*As + Cd + {0, D3DBLENDOP_ADD, D3DBLEND_SRCALPHA, D3DBLEND_ZERO}, // 0202: (Cs - 0)*As + 0 ==> Cs*As + {1, D3DBLENDOP_ADD, D3DBLEND_DESTALPHA, D3DBLEND_ZERO}, //*0210: (Cs - 0)*Ad + Cs ==> Cs*(Ad + 1) + {0, D3DBLENDOP_ADD, D3DBLEND_DESTALPHA, D3DBLEND_ONE}, // 0211: (Cs - 0)*Ad + Cd ==> Cs*Ad + Cd + {0, D3DBLENDOP_ADD, D3DBLEND_DESTALPHA, D3DBLEND_ZERO}, // 0212: (Cs - 0)*Ad + 0 ==> Cs*Ad + {1, D3DBLENDOP_ADD, D3DBLEND_BLENDFACTOR, D3DBLEND_ZERO}, //*0220: (Cs - 0)*F + Cs ==> Cs*(F + 1) + {0, D3DBLENDOP_ADD, D3DBLEND_BLENDFACTOR, D3DBLEND_ONE}, // 0221: (Cs - 0)*F + Cd ==> Cs*F + Cd + {0, D3DBLENDOP_ADD, D3DBLEND_BLENDFACTOR, D3DBLEND_ZERO}, // 0222: (Cs - 0)*F + 0 ==> Cs*F + {0, D3DBLENDOP_ADD, D3DBLEND_INVSRCALPHA, D3DBLEND_SRCALPHA}, // 1000: (Cd - Cs)*As + Cs ==> Cd*As + Cs*(1 - As) + {1, D3DBLENDOP_REVSUBTRACT, D3DBLEND_SRCALPHA, D3DBLEND_SRCALPHA}, //*1001: (Cd - Cs)*As + Cd ==> Cd*(As + 1) - Cs*As + {0, D3DBLENDOP_REVSUBTRACT, D3DBLEND_SRCALPHA, D3DBLEND_SRCALPHA}, // 1002: (Cd - Cs)*As + 0 ==> Cd*As - Cs*As + {0, D3DBLENDOP_ADD, D3DBLEND_INVDESTALPHA, D3DBLEND_DESTALPHA}, // 1010: (Cd - Cs)*Ad + Cs ==> Cd*Ad + Cs*(1 - Ad) + {1, D3DBLENDOP_REVSUBTRACT, D3DBLEND_DESTALPHA, D3DBLEND_DESTALPHA}, //*1011: (Cd - Cs)*Ad + Cd ==> Cd*(Ad + 1) - Cs*Ad + {0, D3DBLENDOP_REVSUBTRACT, D3DBLEND_DESTALPHA, D3DBLEND_DESTALPHA}, // 1012: (Cd - Cs)*Ad + 0 ==> Cd*Ad - Cs*Ad + {0, D3DBLENDOP_ADD, D3DBLEND_INVBLENDFACTOR, D3DBLEND_BLENDFACTOR}, // 1020: (Cd - Cs)*F + Cs ==> Cd*F + Cs*(1 - F) + {1, D3DBLENDOP_REVSUBTRACT, D3DBLEND_BLENDFACTOR, D3DBLEND_BLENDFACTOR},//*1021: (Cd - Cs)*F + Cd ==> Cd*(F + 1) - Cs*F + {0, D3DBLENDOP_REVSUBTRACT, D3DBLEND_BLENDFACTOR, D3DBLEND_BLENDFACTOR},// 1022: (Cd - Cs)*F + 0 ==> Cd*F - Cs*F + {0, D3DBLENDOP_ADD, D3DBLEND_ONE, D3DBLEND_ZERO}, // 1100: (Cd - Cd)*As + Cs ==> Cs + {0, D3DBLENDOP_ADD, D3DBLEND_ZERO, D3DBLEND_ONE}, // 1101: (Cd - Cd)*As + Cd ==> Cd + {0, D3DBLENDOP_ADD, D3DBLEND_ZERO, D3DBLEND_ZERO}, // 1102: (Cd - Cd)*As + 0 ==> 0 + {0, D3DBLENDOP_ADD, D3DBLEND_ONE, D3DBLEND_ZERO}, // 1110: (Cd - Cd)*Ad + Cs ==> Cs + {0, D3DBLENDOP_ADD, D3DBLEND_ZERO, D3DBLEND_ONE}, // 1111: (Cd - Cd)*Ad + Cd ==> Cd + {0, D3DBLENDOP_ADD, D3DBLEND_ZERO, D3DBLEND_ZERO}, // 1112: (Cd - Cd)*Ad + 0 ==> 0 + {0, D3DBLENDOP_ADD, D3DBLEND_ONE, D3DBLEND_ZERO}, // 1120: (Cd - Cd)*F + Cs ==> Cs + {0, D3DBLENDOP_ADD, D3DBLEND_ZERO, D3DBLEND_ONE}, // 1121: (Cd - Cd)*F + Cd ==> Cd + {0, D3DBLENDOP_ADD, D3DBLEND_ZERO, D3DBLEND_ZERO}, // 1122: (Cd - Cd)*F + 0 ==> 0 + {0, D3DBLENDOP_ADD, D3DBLEND_ONE, D3DBLEND_SRCALPHA}, // 1200: (Cd - 0)*As + Cs ==> Cs + Cd*As + {2, D3DBLENDOP_ADD, D3DBLEND_DESTCOLOR, D3DBLEND_SRCALPHA}, //#1201: (Cd - 0)*As + Cd ==> Cd*(1 + As) // ffxii main menu background glow effect + {0, D3DBLENDOP_ADD, D3DBLEND_ZERO, D3DBLEND_SRCALPHA}, // 1202: (Cd - 0)*As + 0 ==> Cd*As + {0, D3DBLENDOP_ADD, D3DBLEND_ONE, D3DBLEND_DESTALPHA}, // 1210: (Cd - 0)*Ad + Cs ==> Cs + Cd*Ad + {2, D3DBLENDOP_ADD, D3DBLEND_DESTCOLOR, D3DBLEND_DESTALPHA}, //#1211: (Cd - 0)*Ad + Cd ==> Cd*(1 + Ad) + {0, D3DBLENDOP_ADD, D3DBLEND_ZERO, D3DBLEND_DESTALPHA}, // 1212: (Cd - 0)*Ad + 0 ==> Cd*Ad + {0, D3DBLENDOP_ADD, D3DBLEND_ONE, D3DBLEND_BLENDFACTOR}, // 1220: (Cd - 0)*F + Cs ==> Cs + Cd*F + {2, D3DBLENDOP_ADD, D3DBLEND_DESTCOLOR, D3DBLEND_BLENDFACTOR}, //#1221: (Cd - 0)*F + Cd ==> Cd*(1 + F) + {0, D3DBLENDOP_ADD, D3DBLEND_ZERO, D3DBLEND_BLENDFACTOR}, // 1222: (Cd - 0)*F + 0 ==> Cd*F + {0, D3DBLENDOP_ADD, D3DBLEND_INVSRCALPHA, D3DBLEND_ZERO}, // 2000: (0 - Cs)*As + Cs ==> Cs*(1 - As) + {0, D3DBLENDOP_REVSUBTRACT, D3DBLEND_SRCALPHA, D3DBLEND_ONE}, // 2001: (0 - Cs)*As + Cd ==> Cd - Cs*As + {0, D3DBLENDOP_REVSUBTRACT, D3DBLEND_SRCALPHA, D3DBLEND_ZERO}, // 2002: (0 - Cs)*As + 0 ==> 0 - Cs*As + {0, D3DBLENDOP_ADD, D3DBLEND_INVDESTALPHA, D3DBLEND_ZERO}, // 2010: (0 - Cs)*Ad + Cs ==> Cs*(1 - Ad) + {0, D3DBLENDOP_REVSUBTRACT, D3DBLEND_DESTALPHA, D3DBLEND_ONE}, // 2011: (0 - Cs)*Ad + Cd ==> Cd - Cs*Ad + {0, D3DBLENDOP_REVSUBTRACT, D3DBLEND_DESTALPHA, D3DBLEND_ZERO}, // 2012: (0 - Cs)*Ad + 0 ==> 0 - Cs*Ad + {0, D3DBLENDOP_ADD, D3DBLEND_INVBLENDFACTOR, D3DBLEND_ZERO}, // 2020: (0 - Cs)*F + Cs ==> Cs*(1 - F) + {0, D3DBLENDOP_REVSUBTRACT, D3DBLEND_BLENDFACTOR, D3DBLEND_ONE}, // 2021: (0 - Cs)*F + Cd ==> Cd - Cs*F + {0, D3DBLENDOP_REVSUBTRACT, D3DBLEND_BLENDFACTOR, D3DBLEND_ZERO}, // 2022: (0 - Cs)*F + 0 ==> 0 - Cs*F + {0, D3DBLENDOP_SUBTRACT, D3DBLEND_ONE, D3DBLEND_SRCALPHA}, // 2100: (0 - Cd)*As + Cs ==> Cs - Cd*As + {0, D3DBLENDOP_ADD, D3DBLEND_ZERO, D3DBLEND_INVSRCALPHA}, // 2101: (0 - Cd)*As + Cd ==> Cd*(1 - As) + {0, D3DBLENDOP_SUBTRACT, D3DBLEND_ZERO, D3DBLEND_SRCALPHA}, // 2102: (0 - Cd)*As + 0 ==> 0 - Cd*As + {0, D3DBLENDOP_SUBTRACT, D3DBLEND_ONE, D3DBLEND_DESTALPHA}, // 2110: (0 - Cd)*Ad + Cs ==> Cs - Cd*Ad + {0, D3DBLENDOP_ADD, D3DBLEND_ZERO, D3DBLEND_INVDESTALPHA}, // 2111: (0 - Cd)*Ad + Cd ==> Cd*(1 - Ad) + {0, D3DBLENDOP_SUBTRACT, D3DBLEND_ONE, D3DBLEND_DESTALPHA}, // 2112: (0 - Cd)*Ad + 0 ==> 0 - Cd*Ad + {0, D3DBLENDOP_SUBTRACT, D3DBLEND_ONE, D3DBLEND_BLENDFACTOR}, // 2120: (0 - Cd)*F + Cs ==> Cs - Cd*F + {0, D3DBLENDOP_ADD, D3DBLEND_ZERO, D3DBLEND_INVBLENDFACTOR}, // 2121: (0 - Cd)*F + Cd ==> Cd*(1 - F) + {0, D3DBLENDOP_SUBTRACT, D3DBLEND_ONE, D3DBLEND_BLENDFACTOR}, // 2122: (0 - Cd)*F + 0 ==> 0 - Cd*F + {0, D3DBLENDOP_ADD, D3DBLEND_ONE, D3DBLEND_ZERO}, // 2200: (0 - 0)*As + Cs ==> Cs + {0, D3DBLENDOP_ADD, D3DBLEND_ZERO, D3DBLEND_ONE}, // 2201: (0 - 0)*As + Cd ==> Cd + {0, D3DBLENDOP_ADD, D3DBLEND_ZERO, D3DBLEND_ZERO}, // 2202: (0 - 0)*As + 0 ==> 0 + {0, D3DBLENDOP_ADD, D3DBLEND_ONE, D3DBLEND_ZERO}, // 2210: (0 - 0)*Ad + Cs ==> Cs + {0, D3DBLENDOP_ADD, D3DBLEND_ZERO, D3DBLEND_ONE}, // 2211: (0 - 0)*Ad + Cd ==> Cd + {0, D3DBLENDOP_ADD, D3DBLEND_ZERO, D3DBLEND_ZERO}, // 2212: (0 - 0)*Ad + 0 ==> 0 + {0, D3DBLENDOP_ADD, D3DBLEND_ONE, D3DBLEND_ZERO}, // 2220: (0 - 0)*F + Cs ==> Cs + {0, D3DBLENDOP_ADD, D3DBLEND_ZERO, D3DBLEND_ONE}, // 2221: (0 - 0)*F + Cd ==> Cd + {0, D3DBLENDOP_ADD, D3DBLEND_ZERO, D3DBLEND_ZERO}, // 2222: (0 - 0)*F + 0 ==> 0 +}; diff --git a/plugins/GSdx/GSDeviceOGL.h b/plugins/GSdx/GSDeviceOGL.h new file mode 100644 index 0000000000..96eebf4b58 --- /dev/null +++ b/plugins/GSdx/GSDeviceOGL.h @@ -0,0 +1,922 @@ +/* + * Copyright (C) 2011-2011 Gregory hainaut + * Copyright (C) 2007-2009 Gabest + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#pragma once + +#include +#include "GSDevice.h" +#include "GSTextureOGL.h" +#include "GSdx.h" + +class GSBlendStateOGL { + // Note: You can also select the index of the draw buffer for which to set the blend setting + // We will keep basic the first try + bool m_enable; + GLenum m_equation_RGB; + GLenum m_equation_ALPHA; + GLenum m_func_sRGB; + GLenum m_func_dRGB; + GLenum m_func_sALPHA; + GLenum m_func_dALPHA; + bool m_r_msk; + bool m_b_msk; + bool m_g_msk; + bool m_a_msk; + bool constant_factor; + float debug_factor; + +public: + + GSBlendStateOGL() : m_enable(false) + , m_equation_RGB(0) + , m_equation_ALPHA(GL_FUNC_ADD) + , m_func_sRGB(0) + , m_func_dRGB(0) + , m_func_sALPHA(GL_ONE) + , m_func_dALPHA(GL_ZERO) + , m_r_msk(GL_TRUE) + , m_b_msk(GL_TRUE) + , m_g_msk(GL_TRUE) + , m_a_msk(GL_TRUE) + , constant_factor(false) + {} + + void SetRGB(GLenum op, GLenum src, GLenum dst) + { + m_equation_RGB = op; + m_func_sRGB = src; + m_func_dRGB = dst; + if (IsConstant(src) || IsConstant(dst)) constant_factor = true; + } + + void SetALPHA(GLenum op, GLenum src, GLenum dst) + { + m_equation_ALPHA = op; + m_func_sALPHA = src; + m_func_dALPHA = dst; + } + + void SetMask(bool r, bool g, bool b, bool a) { m_r_msk = r; m_g_msk = g; m_b_msk = b; m_a_msk = a; } + + void RevertOp() + { + if(m_equation_RGB == GL_FUNC_ADD) + m_equation_RGB = GL_FUNC_REVERSE_SUBTRACT; + else if(m_equation_RGB == GL_FUNC_REVERSE_SUBTRACT) + m_equation_RGB = GL_FUNC_ADD; + } + + void EnableBlend() { m_enable = true;} + + bool IsConstant(GLenum factor) { return ((factor == GL_CONSTANT_COLOR) || (factor == GL_ONE_MINUS_CONSTANT_COLOR)); } + + bool HasConstantFactor() { return constant_factor; } + + void SetupColorMask() + { + glColorMask(m_r_msk, m_g_msk, m_b_msk, m_a_msk); + } + + void SetupBlend(float factor) + { + SetupColorMask(); + + if (m_enable) { + glEnable(GL_BLEND); + if (HasConstantFactor()) { + debug_factor = factor; + glBlendColor(factor, factor, factor, 0); + } + + glBlendEquationSeparate(m_equation_RGB, m_equation_ALPHA); + glBlendFuncSeparate(m_func_sRGB, m_func_dRGB, m_func_sALPHA, m_func_dALPHA); + } else { + glDisable(GL_BLEND); + } + } + + char* NameOfParam(GLenum p) + { + switch (p) { + case GL_FUNC_ADD: return "ADD"; + case GL_FUNC_SUBTRACT: return "SUB"; + case GL_FUNC_REVERSE_SUBTRACT: return "REV SUB"; + case GL_ONE: return "ONE"; + case GL_ZERO: return "ZERO"; + case GL_SRC1_ALPHA: return "SRC1 ALPHA"; + case GL_SRC_ALPHA: return "SRC ALPHA"; + case GL_ONE_MINUS_DST_ALPHA: return "1 - DST ALPHA"; + case GL_DST_ALPHA: return "DST ALPHA"; + case GL_DST_COLOR: return "DST COLOR"; + case GL_ONE_MINUS_SRC1_ALPHA: return "1 - SRC1 ALPHA"; + case GL_ONE_MINUS_SRC_ALPHA: return "1 - SRC ALPHA"; + case GL_CONSTANT_COLOR: return "CST"; + case GL_ONE_MINUS_CONSTANT_COLOR: return "1 - CST"; + default: return "UKN"; + } + return "UKN"; + } + + void debug() + { + if (!m_enable) return; + fprintf(stderr,"Blend op: %s; src:%s; dst:%s\n", NameOfParam(m_equation_RGB), NameOfParam(m_func_sRGB), NameOfParam(m_func_dRGB)); + if (HasConstantFactor()) fprintf(stderr, "Blend constant: %f\n", debug_factor); + fprintf(stderr,"Mask. R:%d B:%d G:%d A:%d\n", m_r_msk, m_b_msk, m_g_msk, m_a_msk); + } +}; + +class GSDepthStencilOGL { + bool m_depth_enable; + GLenum m_depth_func; + GLboolean m_depth_mask; + // Note front face and back might be split but it seems they have same parameter configuration + bool m_stencil_enable; + GLuint m_stencil_mask; + GLuint m_stencil_func; + GLuint m_stencil_ref; + GLuint m_stencil_sfail_op; + GLuint m_stencil_spass_dfail_op; + GLuint m_stencil_spass_dpass_op; + + char* NameOfParam(GLenum p) + { + switch(p) { + case GL_NEVER: return "NEVER"; + case GL_ALWAYS: return "ALWAYS"; + case GL_GEQUAL: return "GEQUAL"; + case GL_GREATER: return "GREATER"; + case GL_KEEP: return "KEEP"; + case GL_EQUAL: return "EQUAL"; + case GL_REPLACE: return "REPLACE"; + default: return "UKN"; + } + return "UKN"; + } + +public: + + GSDepthStencilOGL() : m_depth_enable(false) + , m_depth_func(0) + , m_depth_mask(0) + , m_stencil_enable(false) + , m_stencil_mask(1) + , m_stencil_func(0) + , m_stencil_ref(0) + , m_stencil_sfail_op(GL_KEEP) + , m_stencil_spass_dfail_op(GL_KEEP) + , m_stencil_spass_dpass_op(GL_KEEP) + {} + + void EnableDepth() { m_depth_enable = true; } + void EnableStencil() { m_stencil_enable = true; } + + void SetDepth(GLenum func, GLboolean mask) { m_depth_func = func; m_depth_mask = mask; } + void SetStencil(GLuint func, GLuint pass) { m_stencil_func = func; m_stencil_spass_dpass_op = pass; } + + void SetupDepth() + { + if (m_depth_enable) { + glEnable(GL_DEPTH_TEST); + glDepthFunc(m_depth_func); + glDepthMask(m_depth_mask); + } else + glDisable(GL_DEPTH_TEST); + } + + void SetupStencil(uint8 sref) + { + uint ref = sref; + if (m_stencil_enable) { + glEnable(GL_STENCIL_TEST); + glStencilFunc(m_stencil_func, ref, m_stencil_mask); + glStencilOp(m_stencil_sfail_op, m_stencil_spass_dfail_op, m_stencil_spass_dpass_op); + } else + glDisable(GL_STENCIL_TEST); + } + + void debug() { debug_depth(); debug_stencil(); } + + void debug_depth() + { + if (!m_depth_enable) return; + fprintf(stderr, "Depth %s. Mask %x\n", NameOfParam(m_depth_func), m_depth_mask); + } + + void debug_stencil() + { + if (!m_stencil_enable) return; + fprintf(stderr, "Stencil %s. Both pass op %s\n", NameOfParam(m_stencil_func), NameOfParam(m_stencil_spass_dpass_op)); + } +}; + +class GSUniformBufferOGL { + GLuint buffer; // data object + GLuint index; // GLSL slot + uint size; // size of the data + const GLenum target; + +public: + GSUniformBufferOGL(GLuint index, uint size) : index(index) + , size(size) + ,target(GL_UNIFORM_BUFFER) + { + glGenBuffers(1, &buffer); + bind(); + allocate(); + attach(); + } + + void bind() + { + glBindBuffer(target, buffer); + } + + void allocate() + { + glBufferData(target, size, NULL, GL_STREAM_DRAW); + } + + void attach() + { + glBindBufferBase(target, index, buffer); + } + + void upload(const void* src) + { + uint32 flags = GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT; + uint8* dst = (uint8*) glMapBufferRange(target, 0, size, flags); + memcpy(dst, src, size); + glUnmapBuffer(target); + } + + ~GSUniformBufferOGL() { + glDeleteBuffers(1, &buffer); + } +}; + +struct GSInputLayoutOGL { + GLuint index; + GLint size; + GLenum type; + GLboolean normalize; + GLsizei stride; + const GLvoid* offset; +}; + +class GSVertexBufferStateOGL { + class GSBufferOGL { + size_t m_stride; + size_t m_start; + size_t m_count; + size_t m_limit; + GLenum m_target; + GLuint m_buffer; + size_t m_default_size; + + public: + GSBufferOGL(GLenum target, size_t stride) : + m_stride(stride) + , m_start(0) + , m_count(0) + , m_limit(0) + , m_target(target) + { + glGenBuffers(1, &m_buffer); + // Opengl works best with 1-4MB buffer. + m_default_size = 2 * 1024 * 1024 / m_stride; + } + + ~GSBufferOGL() { glDeleteBuffers(1, &m_buffer); } + + void allocate() { allocate(m_default_size); } + + void allocate(size_t new_limit) + { + m_start = 0; + m_limit = new_limit; + glBufferData(m_target, m_limit * m_stride, NULL, GL_STREAM_DRAW); + } + + void bind() + { + glBindBuffer(m_target, m_buffer); + } + + void upload(const void* src, uint32 count) + { + // Upload the data to the buffer + void* dst; + if (Map(&dst, count)) { + // FIXME which one to use + // GSVector4i::storent(dst, src, m_count * m_stride); + memcpy(dst, src, m_stride*m_count); + Unmap(); + } + } + + bool Map(void** pointer, uint32 count ) { +#ifdef OGL_DEBUG + GLint b_size = -1; + glGetBufferParameteriv(m_target, GL_BUFFER_SIZE, &b_size); + + if (b_size <= 0) return false; +#endif + + m_count = count; + + // Note: For an explanation of the map flag + // see http://www.opengl.org/wiki/Buffer_Object_Streaming + uint32 map_flags = GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT; + + // Current GPU buffer is really too small need to allocate a new one + if (m_count > m_limit) { + allocate(std::max(m_count * 3 / 2, m_default_size)); + + } else if (m_count > (m_limit - m_start) ) { + // Not enough left free room. Just go back at the beginning + m_start = 0; + + // Tell the driver that it can orphan previous buffer and restart from a scratch buffer. + // Technically the buffer will not be accessible by the application anymore but the + // GL will effectively remove it when draws call are finised. + map_flags |= GL_MAP_INVALIDATE_BUFFER_BIT; + } else { + // Tell the driver that it doesn't need to contain any valid buffer data, and that you promise to write the entire range you map + map_flags |= GL_MAP_INVALIDATE_RANGE_BIT; + } + + // Upload the data to the buffer + *pointer = (uint8*) glMapBufferRange(m_target, m_stride*m_start, m_stride*m_count, map_flags); + //fprintf(stderr, "Map %x from %d to %d\n", *pointer, m_start, m_start+m_count); +#ifdef OGL_DEBUG + if (*pointer == NULL) { + fprintf(stderr, "CRITICAL ERROR map failed for vb!!!\n"); + return false; + } +#endif + return true; + } + + void Unmap() { glUnmapBuffer(m_target); } + + void EndScene() + { + m_start += m_count; + m_count = 0; + } + + void Draw(GLenum mode) + { + glDrawArrays(mode, m_start, m_count); + } + + void Draw(GLenum mode, GLint basevertex) + { + glDrawElementsBaseVertex(mode, m_count, GL_UNSIGNED_INT, (void*)(m_start * m_stride), basevertex); + } + + void Draw(GLenum mode, GLint basevertex, int offset, int count) + { + glDrawElementsBaseVertex(mode, count, GL_UNSIGNED_INT, (void*)((m_start + offset) * m_stride), basevertex); + } + + size_t GetStart() { return m_start; } + + void debug() + { + fprintf(stderr, "data buffer: start %d, count %d\n", m_start, m_count); + } + + } *m_vb, *m_ib; + + GLuint m_va; + GLenum m_topology; + +public: + GSVertexBufferStateOGL(size_t stride, GSInputLayoutOGL* layout, uint32 layout_nbr) + { + glGenVertexArrays(1, &m_va); + + m_vb = new GSBufferOGL(GL_ARRAY_BUFFER, stride); + m_ib = new GSBufferOGL(GL_ELEMENT_ARRAY_BUFFER, sizeof(uint32)); + + bind(); + // Note: index array are part of the VA state so it need to be bind only once. + m_ib->bind(); + + m_vb->allocate(); + m_ib->allocate(); + set_internal_format(layout, layout_nbr); + } + + void bind() + { + glBindVertexArray(m_va); + m_vb->bind(); + } + + void set_internal_format(GSInputLayoutOGL* layout, uint32 layout_nbr) + { + for (int i = 0; i < layout_nbr; i++) { + // Note this function need both a vertex array object and a GL_ARRAY_BUFFER buffer + glEnableVertexAttribArray(layout[i].index); + switch (layout[i].type) { + case GL_UNSIGNED_SHORT: + case GL_UNSIGNED_INT: + // Rule: when shader use integral (not normalized) you must use glVertexAttribIPointer (note the extra I) + glVertexAttribIPointer(layout[i].index, layout[i].size, layout[i].type, layout[i].stride, layout[i].offset); + break; + default: + glVertexAttribPointer(layout[i].index, layout[i].size, layout[i].type, layout[i].normalize, layout[i].stride, layout[i].offset); + break; + } + } + } + + void EndScene() + { + m_vb->EndScene(); + m_ib->EndScene(); + } + + void DrawPrimitive() { m_vb->Draw(m_topology); } + + void DrawIndexedPrimitive() { m_ib->Draw(m_topology, m_vb->GetStart() ); } + + void DrawIndexedPrimitive(int offset, int count) { m_ib->Draw(m_topology, m_vb->GetStart(), offset, count ); } + + void SetTopology(GLenum topology) { m_topology = topology; } + + void UploadVB(const void* vertices, size_t count) { m_vb->upload(vertices, count); } + + void UploadIB(const void* index, size_t count) { m_ib->upload(index, count); } + + bool MapVB(void **pointer, size_t count) { return m_vb->Map(pointer, count); } + + void UnmapVB() { m_vb->Unmap(); } + + ~GSVertexBufferStateOGL() + { + glDeleteVertexArrays(1, &m_va); + } + + void debug() + { + string topo; + switch (m_topology) { + case GL_POINTS: + topo = "point"; + break; + case GL_LINES: + topo = "line"; + break; + case GL_TRIANGLES: + topo = "triangle"; + break; + case GL_TRIANGLE_STRIP: + topo = "triangle strip"; + break; + } + m_vb->debug(); + m_ib->debug(); + fprintf(stderr, "primitives of %s\n", topo.c_str()); + + } +}; + +class GSDeviceOGL : public GSDevice +{ + public: + __aligned(struct, 32) VSConstantBuffer + { + GSVector4 VertexScale; + GSVector4 VertexOffset; + GSVector4 TextureScale; + + VSConstantBuffer() + { + VertexScale = GSVector4::zero(); + VertexOffset = GSVector4::zero(); + TextureScale = GSVector4::zero(); + } + + __forceinline bool Update(const VSConstantBuffer* cb) + { + GSVector4i* a = (GSVector4i*)this; + GSVector4i* b = (GSVector4i*)cb; + + GSVector4i b0 = b[0]; + GSVector4i b1 = b[1]; + GSVector4i b2 = b[2]; + + if(!((a[0] == b0) & (a[1] == b1) & (a[2] == b2)).alltrue()) + { + a[0] = b0; + a[1] = b1; + a[2] = b2; + + return true; + } + + return false; + } + }; + + struct VSSelector + { + union + { + struct + { + uint32 bppz:2; + uint32 tme:1; + uint32 fst:1; + uint32 logz:1; + uint32 rtcopy:1; + uint32 wildhack:2; + }; + + uint32 key; + }; + + operator uint32() {return key & 0x3f;} + + VSSelector() : key(0) {} + }; + + __aligned(struct, 32) PSConstantBuffer + { + GSVector4 FogColor_AREF; + GSVector4 HalfTexel; + GSVector4 WH; + GSVector4 MinMax; + GSVector4 MinF_TA; + GSVector4i MskFix; + + PSConstantBuffer() + { + FogColor_AREF = GSVector4::zero(); + HalfTexel = GSVector4::zero(); + WH = GSVector4::zero(); + MinMax = GSVector4::zero(); + MinF_TA = GSVector4::zero(); + MskFix = GSVector4i::zero(); + } + + __forceinline bool Update(const PSConstantBuffer* cb) + { + GSVector4i* a = (GSVector4i*)this; + GSVector4i* b = (GSVector4i*)cb; + + GSVector4i b0 = b[0]; + GSVector4i b1 = b[1]; + GSVector4i b2 = b[2]; + GSVector4i b3 = b[3]; + GSVector4i b4 = b[4]; + GSVector4i b5 = b[5]; + + if(!((a[0] == b0) /*& (a[1] == b1)*/ & (a[2] == b2) & (a[3] == b3) & (a[4] == b4) & (a[5] == b5)).alltrue()) // if WH matches HalfTexel does too + { + a[0] = b0; + a[1] = b1; + a[2] = b2; + a[3] = b3; + a[4] = b4; + a[5] = b5; + + return true; + } + + return false; + } + }; + + struct GSSelector + { + union + { + struct + { + uint32 iip:1; + uint32 prim:2; + }; + + uint32 key; + }; + + operator uint32() {return key & 0x7;} + + GSSelector() : key(0) {} + }; + + struct PSSelector + { + union + { + struct + { + uint32 fst:1; + uint32 wms:2; + uint32 wmt:2; + uint32 fmt:3; + uint32 aem:1; + uint32 tfx:3; + uint32 tcc:1; + uint32 atst:3; + uint32 fog:1; + uint32 clr1:1; + uint32 fba:1; + uint32 aout:1; + uint32 rt:1; + uint32 ltf:1; + uint32 colclip:2; + uint32 date:2; + uint32 spritehack:1; + }; + + uint32 key; + }; + + operator uint32() {return key & 0x3ffffff;} + + PSSelector() : key(0) {} + }; + + struct PSSamplerSelector + { + union + { + struct + { + uint32 tau:1; + uint32 tav:1; + uint32 ltf:1; + }; + + uint32 key; + }; + + operator uint32() {return key & 0x7;} + + PSSamplerSelector() : key(0) {} + }; + + struct OMDepthStencilSelector + { + union + { + struct + { + uint32 ztst:2; + uint32 zwe:1; + uint32 date:1; + uint32 fba:1; + }; + + uint32 key; + }; + + operator uint32() {return key & 0x1f;} + + OMDepthStencilSelector() : key(0) {} + }; + + struct OMBlendSelector + { + union + { + struct + { + uint32 abe:1; + uint32 a:2; + uint32 b:2; + uint32 c:2; + uint32 d:2; + uint32 wr:1; + uint32 wg:1; + uint32 wb:1; + uint32 wa:1; + uint32 negative:1; + }; + + struct + { + uint32 _pad:1; + uint32 abcd:8; + uint32 wrgba:4; + }; + + uint32 key; + }; + + operator uint32() {return key & 0x3fff;} + + OMBlendSelector() : key(0) {} + + bool IsCLR1() const + { + return (key & 0x19f) == 0x93; // abe == 1 && a == 1 && b == 2 && d == 1 + } + }; + + struct D3D9Blend {int bogus, op, src, dst;}; + static const D3D9Blend m_blendMapD3D9[3*3*3*3]; + + private: + uint32 m_msaa; // Level of Msaa + + bool m_free_window; + GSWnd* m_window; + + GLuint m_pipeline; // pipeline to attach program shader + GLuint m_fbo; // frame buffer container + GLuint m_fbo_read; // frame buffer container only for reading + + GSVertexBufferStateOGL* m_vb; // vb_state for HW renderer + GSVertexBufferStateOGL* m_vb_sr; // vb_state for StretchRect + + struct { + GLuint ps[2]; // program object + GSUniformBufferOGL* cb; // uniform buffer object + GSBlendStateOGL* bs; + } m_merge_obj; + + struct { + GLuint ps[4]; // program object + GSUniformBufferOGL* cb; // uniform buffer object + } m_interlace; + + struct { + GLuint vs; // program object + GLuint ps[8]; // program object + GLuint ln; // sampler object + GLuint pt; // sampler object + GLuint gs; + GSDepthStencilOGL* dss; + GSBlendStateOGL* bs; + } m_convert; + + struct { + GLuint ps; + GSUniformBufferOGL *cb; + } m_fxaa; + + struct { + GSDepthStencilOGL* dss; + GSBlendStateOGL* bs; + } m_date; + + struct + { + GLuint ps; + GSUniformBufferOGL *cb; + } m_shadeboost; + + + + struct { + GSVertexBufferStateOGL* vb; + GLuint vs; // program + GSUniformBufferOGL* cb; // uniform current buffer + GLuint gs; // program + // FIXME texture binding. Maybe not equivalent for the state but the best I could find. + GSTextureOGL* ps_srv[3]; + // ID3D11ShaderResourceView* ps_srv[3]; + GLuint ps; // program + GLuint ps_ss[3]; // sampler + GSVector2i viewport; + GSVector4i scissor; + GSDepthStencilOGL* dss; + uint8 sref; + GSBlendStateOGL* bs; + float bf; + // FIXME texture attachment in the FBO + // ID3D11RenderTargetView* rtv; + // ID3D11DepthStencilView* dsv; + GSTextureOGL* rtv; + GSTextureOGL* dsv; + GLuint fbo; + GLenum draw; + } m_state; + + bool m_srv_changed; + bool m_ss_changed; + + hash_map m_vs; + hash_map m_gs; + hash_map m_ps; + hash_map m_ps_ss; + hash_map m_om_dss; + hash_map m_om_bs; + + GLuint m_palette_ss; + GLuint m_rt_ss; + + GSUniformBufferOGL* m_vs_cb; + GSUniformBufferOGL* m_ps_cb; + + VSConstantBuffer m_vs_cb_cache; + PSConstantBuffer m_ps_cb_cache; + + protected: + GSTexture* CreateSurface(int type, int w, int h, bool msaa, int format); + GSTexture* FetchSurface(int type, int w, int h, bool msaa, int format); + void DoMerge(GSTexture* st[2], GSVector4* sr, GSTexture* dt, GSVector4* dr, bool slbg, bool mmod, const GSVector4& c); + void DoInterlace(GSTexture* st, GSTexture* dt, int shader, bool linear, float yoffset = 0); + void DoShadeBoost(GSTexture* st, GSTexture* dt); + + public: + GSDeviceOGL(); + virtual ~GSDeviceOGL(); + + void CheckDebugLog(); + static void DebugOutputToFile(unsigned int source, unsigned int type, unsigned int id, unsigned int severity, const char* message); + void DebugOutput(); + void DebugInput(); + void DebugBB(); + + bool HasStencil() { return true; } + bool HasDepth32() { return true; } + + bool Create(GSWnd* wnd); + bool Reset(int w, int h); + void Flip(); + + void DrawPrimitive(); + void DrawIndexedPrimitive(); + void DrawIndexedPrimitive(int offset, int count); + + void ClearRenderTarget(GSTexture* t, const GSVector4& c); + void ClearRenderTarget(GSTexture* t, uint32 c); + void ClearDepth(GSTexture* t, float c); + void ClearStencil(GSTexture* t, uint8 c); + + GSTexture* CreateRenderTarget(int w, int h, bool msaa, int format = 0); + GSTexture* CreateDepthStencil(int w, int h, bool msaa, int format = 0); + GSTexture* CreateTexture(int w, int h, int format = 0); + GSTexture* CreateOffscreen(int w, int h, int format = 0); + + GSTexture* CopyOffscreen(GSTexture* src, const GSVector4& sr, int w, int h, int format = 0); + + void CopyRect(GSTexture* st, GSTexture* dt, const GSVector4i& r); + void StretchRect(GSTexture* st, const GSVector4& sr, GSTexture* dt, const GSVector4& dr, int shader = 0, bool linear = true); + void StretchRect(GSTexture* st, const GSVector4& sr, GSTexture* dt, const GSVector4& dr, GLuint ps, bool linear = true); + void StretchRect(GSTexture* st, const GSVector4& sr, GSTexture* dt, const GSVector4& dr, GLuint ps, GSBlendStateOGL* bs, bool linear = true); + + void SetupDATE(GSTexture* rt, GSTexture* ds, const GSVertexPT1* vertices, bool datm); + + GSTexture* Resolve(GSTexture* t); + + void CompileShaderFromSource(const std::string& glsl_file, const std::string& entry, GLenum type, GLuint* program, const std::string& macro_sel = ""); + + void EndScene(); + + void IASetPrimitiveTopology(GLenum topology); + void IASetVertexBuffer(const void* vertices, size_t count); + bool IAMapVertexBuffer(void** vertex, size_t stride, size_t count); + void IAUnmapVertexBuffer(); + void IASetIndexBuffer(const void* index, size_t count); + void IASetVertexState(GSVertexBufferStateOGL* vb = NULL); + + void SetUniformBuffer(GSUniformBufferOGL* cb); + + void VSSetShader(GLuint vs); + void GSSetShader(GLuint gs); + + void PSSetShaderResources(GSTexture* sr0, GSTexture* sr1); + void PSSetShaderResource(int i, GSTexture* sr); + void PSSetSamplerState(GLuint ss0, GLuint ss1, GLuint ss2 = 0); + void PSSetShader(GLuint ps); + + void OMSetFBO(GLuint fbo, GLenum buffer = GL_COLOR_ATTACHMENT0); + void OMSetDepthStencilState(GSDepthStencilOGL* dss, uint8 sref); + void OMSetBlendState(GSBlendStateOGL* bs, float bf); + void OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector4i* scissor = NULL); + + + void CreateTextureFX(); + void SetupIA(const void* vertex, int vertex_count, const uint32* index, int index_count, int prim); + void SetupVS(VSSelector sel, const VSConstantBuffer* cb); + void SetupGS(GSSelector sel); + void SetupPS(PSSelector sel, const PSConstantBuffer* cb, PSSamplerSelector ssel); + void SetupOM(OMDepthStencilSelector dssel, OMBlendSelector bsel, uint8 afix); +}; diff --git a/plugins/GSdx/GSDeviceSDL.cpp b/plugins/GSdx/GSDeviceSDL.cpp index be0a15ecdf..1fccc4b386 100644 --- a/plugins/GSdx/GSDeviceSDL.cpp +++ b/plugins/GSdx/GSDeviceSDL.cpp @@ -22,6 +22,7 @@ #include "stdafx.h" #include "GSDeviceSDL.h" +#ifdef ENABLE_SDL_DEV GSDeviceSDL::GSDeviceSDL() : m_free_window(false) , m_window(NULL) @@ -230,3 +231,4 @@ void GSDeviceSDL::Flip() { SDL_RenderPresent(m_renderer); } +#endif diff --git a/plugins/GSdx/GSDeviceSDL.h b/plugins/GSdx/GSDeviceSDL.h index 324d41c2c8..6a61ce30f2 100644 --- a/plugins/GSdx/GSDeviceSDL.h +++ b/plugins/GSdx/GSDeviceSDL.h @@ -21,6 +21,8 @@ #pragma once +#ifdef ENABLE_SDL_DEV + #include "GSDeviceSW.h" #include "../../3rdparty/SDL-1.3.0-5387/include/SDL.h" @@ -52,4 +54,5 @@ public: bool Reset(int w, int h); void Present(GSTexture* st, GSTexture* dt, const GSVector4& dr, int shader = 0); void Flip(); -}; \ No newline at end of file +}; +#endif diff --git a/plugins/GSdx/GSLinuxDialog.cpp b/plugins/GSdx/GSLinuxDialog.cpp index f2dca41b65..8d168c1ccd 100644 --- a/plugins/GSdx/GSLinuxDialog.cpp +++ b/plugins/GSdx/GSLinuxDialog.cpp @@ -22,6 +22,14 @@ #include "stdafx.h" #include #include "GSdx.h" +#include "GSLinuxLogo.h" + +GtkWidget *msaa_combo_box, *render_combo_box, *filter_combo_box; +GtkWidget *shadeboost_check, *paltex_check, *fba_check, *aa_check, *native_res_check; +GtkWidget *sb_contrast, *sb_brightness, *sb_saturation; +GtkWidget *resx_spin, *resy_spin; + +GtkWidget *hack_alpha_check, *hack_offset_check, *hack_skipdraw_spin, *hack_msaa_check, *hack_sprite_check, * hack_wild_check, *hack_enble_check; static void SysMessage(const char *fmt, ...) { @@ -40,16 +48,184 @@ static void SysMessage(const char *fmt, ...) gtk_widget_destroy (dialog); } +GtkWidget* CreateRenderComboBox() +{ + GtkWidget *render_combo_box; + int renderer_box_position = 0; + + render_combo_box = gtk_combo_box_new_text (); + + for(size_t i = 6; i < theApp.m_gs_renderers.size(); i++) + { + const GSSetting& s = theApp.m_gs_renderers[i]; + + string label = s.name; + + if(!s.note.empty()) label += format(" (%s)", s.note.c_str()); + + // Add some tags to ease users selection + switch (i) { + // better use opengl instead of SDL + case 6: + label += " (deprecated)"; + break; + + // (dev only) for any NULL stuff + case 7: + case 8: + case 9: + label += " (debug only)"; + break; + + // opengl harware is not yet finished + case 10: + label += " (experimental)"; + break; + default: + break; + } + + gtk_combo_box_append_text(GTK_COMBO_BOX(render_combo_box), label.c_str()); + } + + switch (theApp.GetConfig("renderer", 0)) { + // Note the value are based on m_gs_renderers vector on GSdx.cpp + case 7 : renderer_box_position = 0; break; + case 8 : renderer_box_position = 1; break; + case 10: renderer_box_position = 2; break; + case 11: renderer_box_position = 3; break; + case 12: renderer_box_position = 4; break; + case 13: renderer_box_position = 5; break; + } + gtk_combo_box_set_active(GTK_COMBO_BOX(render_combo_box), renderer_box_position); + return render_combo_box; +} + +GtkWidget* CreateInterlaceComboBox() +{ + GtkWidget *combo_box; + combo_box = gtk_combo_box_new_text (); + + for(size_t i = 0; i < theApp.m_gs_interlace.size(); i++) + { + const GSSetting& s = theApp.m_gs_interlace[i]; + + string label = s.name; + + if(!s.note.empty()) label += format(" (%s)", s.note.c_str()); + + gtk_combo_box_append_text(GTK_COMBO_BOX(combo_box), label.c_str()); + } + + gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), theApp.GetConfig("interlace", 0)); + return combo_box; +} + +GtkWidget* CreateMsaaComboBox() +{ + GtkWidget *combo_box; + combo_box = gtk_combo_box_new_text (); + + // For now, let's just put in the same vaues that show up in the windows combo box. + gtk_combo_box_append_text(GTK_COMBO_BOX(combo_box), "Custom"); + gtk_combo_box_append_text(GTK_COMBO_BOX(combo_box), "2x"); + gtk_combo_box_append_text(GTK_COMBO_BOX(combo_box), "3x"); + gtk_combo_box_append_text(GTK_COMBO_BOX(combo_box), "4x"); + gtk_combo_box_append_text(GTK_COMBO_BOX(combo_box), "5x"); + gtk_combo_box_append_text(GTK_COMBO_BOX(combo_box), "6x"); + + gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), theApp.GetConfig("msaa", 0)); + return combo_box; +} + +GtkWidget* CreateFilterComboBox() +{ + GtkWidget *combo_box; + combo_box = gtk_combo_box_new_text (); + + gtk_combo_box_append_text(GTK_COMBO_BOX(combo_box), "Off"); + gtk_combo_box_append_text(GTK_COMBO_BOX(combo_box), "Normal"); + gtk_combo_box_append_text(GTK_COMBO_BOX(combo_box), "Forced"); + + gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), theApp.GetConfig("filter", 0)); + return combo_box; +} + +void toggle_widget_states( GtkWidget *widget, gpointer callback_data ) +{ + int render_type; + bool hardware_render = false, software_render = false, sdl_render = false, null_render = false; + + render_type = gtk_combo_box_get_active(GTK_COMBO_BOX(render_combo_box)); + hardware_render = (render_type == 1 || render_type == 4 || render_type == 7 || render_type == 13); + + if (hardware_render) + { + gtk_widget_set_sensitive(filter_combo_box, true); + gtk_widget_set_sensitive(shadeboost_check, true); + gtk_widget_set_sensitive(paltex_check, true); + gtk_widget_set_sensitive(fba_check, true); + gtk_widget_set_sensitive(native_res_check, true); + + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(native_res_check))) + { + gtk_widget_set_sensitive(msaa_combo_box, false); + gtk_widget_set_sensitive(resx_spin, false); + gtk_widget_set_sensitive(resy_spin, false); + } + else + { + gtk_widget_set_sensitive(msaa_combo_box, true); + + if (gtk_combo_box_get_active(GTK_COMBO_BOX(msaa_combo_box)) == 0) + { + gtk_widget_set_sensitive(resx_spin, true); + gtk_widget_set_sensitive(resy_spin, true); + } + else + { + gtk_widget_set_sensitive(resx_spin, false); + gtk_widget_set_sensitive(resy_spin, false); + } + } + + gtk_widget_set_sensitive(sb_brightness,true); + gtk_widget_set_sensitive(sb_saturation,true); + gtk_widget_set_sensitive(sb_contrast,true); + } + else + { + gtk_widget_set_sensitive(filter_combo_box, false); + gtk_widget_set_sensitive(shadeboost_check, false); + gtk_widget_set_sensitive(paltex_check, false); + gtk_widget_set_sensitive(fba_check, false); + + gtk_widget_set_sensitive(native_res_check, false); + gtk_widget_set_sensitive(msaa_combo_box, false); + gtk_widget_set_sensitive(resx_spin, false); + gtk_widget_set_sensitive(resy_spin, false); + + gtk_widget_set_sensitive(sb_brightness,false); + gtk_widget_set_sensitive(sb_saturation,false); + gtk_widget_set_sensitive(sb_contrast,false); + } +} + bool RunLinuxDialog() { GtkWidget *dialog; - GtkWidget *main_frame, *main_box; - GtkWidget *render_label, *render_combo_box; - GtkWidget *interlace_label, *interlace_combo_box; - GtkWidget *swthreads_label, *swthreads_text; - GtkWidget *filter_check, *logz_check, *paltex_check, *fba_check, *aa_check, *win_check; + GtkWidget *main_box, *res_box, *hw_box, *sw_box; + GtkWidget *native_box, *msaa_box, *resxy_box, *renderer_box, *interlace_box, *threads_box, *filter_box; + GtkWidget *hw_table, *res_frame, *hw_frame, *sw_frame; + GtkWidget *interlace_combo_box, *threads_spin; + GtkWidget *interlace_label, *threads_label, *native_label, *msaa_label, *rexy_label, *render_label, *filter_label; + + GtkWidget *hack_table, *hack_skipdraw_label, *hack_box, *hack_frame; int return_value; - + + GdkPixbuf* logo_pixmap; + GtkWidget *logo_image; + /* Create the widgets */ dialog = gtk_dialog_new_with_buttons ( "GSdx Config", @@ -61,107 +237,245 @@ bool RunLinuxDialog() GTK_RESPONSE_REJECT, NULL); + // The main area for the whole dialog box. main_box = gtk_vbox_new(false, 5); - main_frame = gtk_frame_new ("GSdx Config"); - gtk_container_add (GTK_CONTAINER(main_frame), main_box); - + + // The Internal resolution frame and container. + res_box = gtk_vbox_new(false, 5); + res_frame = gtk_frame_new ("OpenGL Internal Resolution (can cause glitches)"); + gtk_container_add(GTK_CONTAINER(res_frame), res_box); + + // The hardware mode frame, container, and table. + hw_box = gtk_vbox_new(false, 5); + hw_frame = gtk_frame_new ("Hardware Mode Settings"); + gtk_container_add(GTK_CONTAINER(hw_frame), hw_box); + hw_table = gtk_table_new(5,2, false); + gtk_container_add(GTK_CONTAINER(hw_box), hw_table); + + // The software mode frame and container. (It doesn't have enough in it for a table.) + sw_box = gtk_vbox_new(false, 5); + sw_frame = gtk_frame_new ("Software Mode Settings"); + gtk_container_add(GTK_CONTAINER(sw_frame), sw_box); + + // The hack frame and container. + hack_box = gtk_hbox_new(false, 5); + hack_frame = gtk_frame_new ("Hacks"); + gtk_container_add(GTK_CONTAINER(hack_frame), hack_box); + hack_table = gtk_table_new(3,3, false); + gtk_container_add(GTK_CONTAINER(hack_box), hack_table); + + // Grab a logo, to make things look nice. + logo_pixmap = gdk_pixbuf_from_pixdata(&gsdx_ogl_logo, false, NULL); + logo_image = gtk_image_new_from_pixbuf(logo_pixmap); + gtk_box_pack_start(GTK_BOX(main_box), logo_image, true, true, 0); + + // Create the renderer combo box and label, and stash them in a box. render_label = gtk_label_new ("Renderer:"); - render_combo_box = gtk_combo_box_new_text (); + render_combo_box = CreateRenderComboBox(); + renderer_box = gtk_hbox_new(false, 5); + // Use gtk_box_pack_start instead of gtk_container_add so it lines up nicely. + gtk_box_pack_start(GTK_BOX(renderer_box), render_label, false, false, 5); + gtk_box_pack_start(GTK_BOX(renderer_box), render_combo_box, false, false, 5); + + // Create the interlace combo box and label, and stash them in a box. + interlace_label = gtk_label_new ("Interlacing (F5):"); + interlace_combo_box = CreateInterlaceComboBox(); + interlace_box = gtk_hbox_new(false, 5); + gtk_box_pack_start(GTK_BOX(interlace_box), interlace_label, false, false, 5); + gtk_box_pack_start(GTK_BOX(interlace_box), interlace_combo_box, false, false, 5); - for(size_t i = 6; i < theApp.m_gs_renderers.size(); i++) - { - const GSSetting& s = theApp.m_gs_renderers[i]; + // Create the filter combo box. + filter_label = gtk_label_new ("Texture Filtering:"); + filter_combo_box = CreateFilterComboBox(); + filter_box = gtk_hbox_new(false, 5); + gtk_box_pack_start(GTK_BOX(filter_box), filter_label, false, false, 5); + gtk_box_pack_start(GTK_BOX(filter_box), filter_combo_box, false, false, 0); + + // Create the threading spin box and label, and stash them in a box. (Yes, we do a lot of that.) + threads_label = gtk_label_new("Extra rendering threads:"); + threads_spin = gtk_spin_button_new_with_range(0,100,1); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(threads_spin), theApp.GetConfig("extrathreads", 0)); + threads_box = gtk_hbox_new(false, 0); + gtk_box_pack_start(GTK_BOX(threads_box), threads_label, false, false, 5); + gtk_box_pack_start(GTK_BOX(threads_box), threads_spin, false, false, 5); - string label = s.name; + // A bit of funkiness for the resolution box. + native_label = gtk_label_new("Original PS2 Resolution: "); + native_res_check = gtk_check_button_new_with_label("Native"); + native_box = gtk_hbox_new(false, 5); + gtk_box_pack_start(GTK_BOX(native_box), native_label, false, false, 5); + gtk_box_pack_start(GTK_BOX(native_box), native_res_check, false, false, 5); + + msaa_label = gtk_label_new("Or Use Scaling (broken):"); + msaa_combo_box = CreateMsaaComboBox(); + msaa_box = gtk_hbox_new(false, 5); + gtk_box_pack_start(GTK_BOX(msaa_box), msaa_label, false, false, 5); + gtk_box_pack_start(GTK_BOX(msaa_box), msaa_combo_box, false, false, 5); + + rexy_label = gtk_label_new("Custom Resolution:"); + resx_spin = gtk_spin_button_new_with_range(256,8192,1); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(resx_spin), theApp.GetConfig("resx", 1024)); + resy_spin = gtk_spin_button_new_with_range(256,8192,1); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(resy_spin), theApp.GetConfig("resy", 1024)); + resxy_box = gtk_hbox_new(false, 5); + gtk_box_pack_start(GTK_BOX(resxy_box), rexy_label, false, false, 5); + gtk_box_pack_start(GTK_BOX(resxy_box), resx_spin, false, false, 5); + gtk_box_pack_start(GTK_BOX(resxy_box), resy_spin, false, false, 5); - if(!s.note.empty()) label += format(" (%s)", s.note.c_str()); + // Create our hack settings. + hack_alpha_check = gtk_check_button_new_with_label("Alpha Hack"); + hack_offset_check = gtk_check_button_new_with_label("Offset Hack"); + hack_skipdraw_label = gtk_label_new("Skipdraw:"); + hack_skipdraw_spin = gtk_spin_button_new_with_range(0,1000,1); + hack_enble_check = gtk_check_button_new_with_label("Enable User Hacks"); + hack_wild_check = gtk_check_button_new_with_label("Wild arm Hack"); + hack_sprite_check = gtk_check_button_new_with_label("Sprite Hack"); + hack_msaa_check = gtk_check_button_new_with_label("Msaa Hack"); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(hack_skipdraw_spin), theApp.GetConfig("UserHacks_SkipDraw", 0)); + // Tables are strange. The numbers are for their position: left, right, top, bottom. + gtk_table_attach_defaults(GTK_TABLE(hack_table), hack_alpha_check, 0, 1, 0, 1); + gtk_table_attach_defaults(GTK_TABLE(hack_table), hack_offset_check, 1, 2, 0, 1); + gtk_table_attach_defaults(GTK_TABLE(hack_table), hack_sprite_check, 0, 1, 1, 2); + gtk_table_attach_defaults(GTK_TABLE(hack_table), hack_wild_check, 1, 2, 1, 2); + // Note: MSAA is not implemented yet. I disable it to make the table square + //gtk_table_attach_defaults(GTK_TABLE(hack_table), hack_msaa_check, 2, 3, 1, 2); + gtk_table_attach_defaults(GTK_TABLE(hack_table), hack_skipdraw_label, 0, 1, 2, 3); + gtk_table_attach_defaults(GTK_TABLE(hack_table), hack_skipdraw_spin, 1, 2, 2, 3); - gtk_combo_box_append_text(GTK_COMBO_BOX(render_combo_box), label.c_str()); - } - - gtk_combo_box_set_active(GTK_COMBO_BOX(render_combo_box), 0); - gtk_container_add(GTK_CONTAINER(main_box), render_label); - gtk_container_add(GTK_CONTAINER(main_box), render_combo_box); - - - interlace_label = gtk_label_new ("Interlace:"); - interlace_combo_box = gtk_combo_box_new_text (); - - for(size_t i = 0; i < theApp.m_gs_interlace.size(); i++) - { - const GSSetting& s = theApp.m_gs_interlace[i]; - - string label = s.name; - - if(!s.note.empty()) label += format(" (%s)", s.note.c_str()); - - gtk_combo_box_append_text(GTK_COMBO_BOX(interlace_combo_box), label.c_str()); - } - - gtk_combo_box_set_active(GTK_COMBO_BOX(interlace_combo_box), theApp.GetConfig("interlace", 0)); - gtk_container_add(GTK_CONTAINER(main_box), interlace_label); - gtk_container_add(GTK_CONTAINER(main_box), interlace_combo_box); - - swthreads_label = gtk_label_new("Extra sw renderer threads:"); - swthreads_text = gtk_entry_new(); - char buf[5]; - sprintf(buf, "%d", theApp.GetConfig("extrathreads", 0)); - - gtk_entry_set_text(GTK_ENTRY(swthreads_text), buf); - gtk_container_add(GTK_CONTAINER(main_box), swthreads_label); - gtk_container_add(GTK_CONTAINER(main_box), swthreads_text); - - - filter_check = gtk_check_button_new_with_label("Texture Filtering"); - logz_check = gtk_check_button_new_with_label("Logarithmic Z"); + // Create our checkboxes. + shadeboost_check = gtk_check_button_new_with_label("Shade boost"); paltex_check = gtk_check_button_new_with_label("Allow 8 bit textures"); fba_check = gtk_check_button_new_with_label("Alpha correction (FBA)"); - aa_check = gtk_check_button_new_with_label("Edge anti-aliasing"); - win_check = gtk_check_button_new_with_label("Disable Effects Processing"); - - gtk_container_add(GTK_CONTAINER(main_box), filter_check); - gtk_container_add(GTK_CONTAINER(main_box), logz_check); - gtk_container_add(GTK_CONTAINER(main_box), paltex_check); - gtk_container_add(GTK_CONTAINER(main_box), fba_check); - gtk_container_add(GTK_CONTAINER(main_box), aa_check); - gtk_container_add(GTK_CONTAINER(main_box), win_check); - - // Filter should be 3 states, not 2. - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(filter_check), theApp.GetConfig("filter", 1)); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(logz_check), theApp.GetConfig("logz", 1)); + aa_check = gtk_check_button_new_with_label("Edge anti-aliasing (AA1)"); + + // Set the checkboxes. + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(shadeboost_check), theApp.GetConfig("shadeboost", 1)); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(paltex_check), theApp.GetConfig("paltex", 0)); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(fba_check), theApp.GetConfig("fba", 1)); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(aa_check), theApp.GetConfig("aa1", 0)); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(win_check), theApp.GetConfig("windowed", 1)); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(native_res_check), theApp.GetConfig("nativeres", 0)); + + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(hack_alpha_check), theApp.GetConfig("UserHacks_AlphaHack", 0)); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(hack_offset_check), theApp.GetConfig("UserHacks_HalfPixelOffset", 0)); - gtk_container_add (GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), main_frame); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(hack_enble_check), theApp.GetConfig("UserHacks", 0)); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(hack_msaa_check), theApp.GetConfig("UserHacks_MSAA", 0)); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(hack_wild_check), theApp.GetConfig("UserHacks_WildHack", 0)); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(hack_sprite_check), theApp.GetConfig("UserHacks_SpriteHack", 0)); + + // Shadeboost scale + sb_brightness = gtk_hscale_new_with_range(0, 200, 10); + GtkWidget* sb_brightness_label = gtk_label_new("Shade Boost Brightness"); + gtk_scale_set_value_pos(GTK_SCALE(sb_brightness), GTK_POS_RIGHT); + gtk_range_set_value(GTK_RANGE(sb_brightness), theApp.GetConfig("ShadeBoost_Brightness", 50)); + + sb_contrast = gtk_hscale_new_with_range(0, 200, 10); + GtkWidget* sb_contrast_label = gtk_label_new("Shade Boost Contrast"); + gtk_scale_set_value_pos(GTK_SCALE(sb_contrast), GTK_POS_RIGHT); + gtk_range_set_value(GTK_RANGE(sb_contrast), theApp.GetConfig("ShadeBoost_Contrast", 50)); + + sb_saturation = gtk_hscale_new_with_range(0, 200, 10); + GtkWidget* sb_saturation_label = gtk_label_new("Shade Boost Saturation"); + gtk_scale_set_value_pos(GTK_SCALE(sb_saturation), GTK_POS_RIGHT); + gtk_range_set_value(GTK_RANGE(sb_saturation), theApp.GetConfig("ShadeBoost_Saturation", 50)); + + // Populate all those boxes we created earlier with widgets. + gtk_container_add(GTK_CONTAINER(res_box), native_box); + gtk_container_add(GTK_CONTAINER(res_box), msaa_box); + gtk_container_add(GTK_CONTAINER(res_box), resxy_box); + + gtk_container_add(GTK_CONTAINER(sw_box), threads_box); + gtk_container_add(GTK_CONTAINER(sw_box), aa_check); + + // Tables are strange. The numbers are for their position: left, right, top, bottom. + gtk_table_attach_defaults(GTK_TABLE(hw_table), filter_box, 0, 1, 0, 1); + gtk_table_attach_defaults(GTK_TABLE(hw_table), shadeboost_check, 1, 2, 0, 1); + gtk_table_attach_defaults(GTK_TABLE(hw_table), paltex_check, 0, 1, 1, 2); + gtk_table_attach_defaults(GTK_TABLE(hw_table), fba_check, 1, 2, 1, 2); + gtk_table_attach_defaults(GTK_TABLE(hw_table), sb_brightness_label, 0, 1, 2, 3); + gtk_table_attach_defaults(GTK_TABLE(hw_table), sb_brightness, 1, 2, 2, 3); + gtk_table_attach_defaults(GTK_TABLE(hw_table), sb_contrast_label, 0, 1, 3, 4); + gtk_table_attach_defaults(GTK_TABLE(hw_table), sb_contrast, 1, 2, 3, 4); + gtk_table_attach_defaults(GTK_TABLE(hw_table), sb_saturation_label, 0, 1, 4, 5); + gtk_table_attach_defaults(GTK_TABLE(hw_table), sb_saturation, 1, 2, 4, 5); + + + // Put everything in the big box. + gtk_container_add(GTK_CONTAINER(main_box), renderer_box); + gtk_container_add(GTK_CONTAINER(main_box), interlace_box); + gtk_container_add(GTK_CONTAINER(main_box), res_frame); + gtk_container_add(GTK_CONTAINER(main_box), hw_frame); + gtk_container_add(GTK_CONTAINER(main_box), sw_frame); + + if (!!theApp.GetConfig("UserHacks", 0)) + { + gtk_container_add(GTK_CONTAINER(main_box), hack_frame); + } + + g_signal_connect(render_combo_box, "changed", G_CALLBACK(toggle_widget_states), NULL); + g_signal_connect(msaa_combo_box, "changed", G_CALLBACK(toggle_widget_states), NULL); + g_signal_connect(native_res_check, "toggled", G_CALLBACK(toggle_widget_states), NULL); + // Put the box in the dialog and show it to the world. + gtk_container_add (GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), main_box); gtk_widget_show_all (dialog); - + toggle_widget_states(NULL, NULL); return_value = gtk_dialog_run (GTK_DIALOG (dialog)); if (return_value == GTK_RESPONSE_ACCEPT) { + int mode_height = 0, mode_width = 0; + + mode_width = theApp.GetConfig("ModeWidth", 640); + mode_height = theApp.GetConfig("ModeHeight", 480); + theApp.SetConfig("ModeHeight", mode_height); + theApp.SetConfig("ModeWidth", mode_width); + // Get all the settings from the dialog box. + if (gtk_combo_box_get_active(GTK_COMBO_BOX(render_combo_box)) != -1) { + // FIXME test current opengl version supported through glxinfo (OpenGL version string:) + // Warn the user if 4.2 is not supported and switch back to basic SDL renderer + switch (gtk_combo_box_get_active(GTK_COMBO_BOX(render_combo_box))) { + // Note the value are based on m_gs_renderers vector on GSdx.cpp + case 0: theApp.SetConfig("renderer", 7); break; + case 1: theApp.SetConfig("renderer", 8); break; + case 2: theApp.SetConfig("renderer", 10); break; + case 3: theApp.SetConfig("renderer", 11); break; + case 4: theApp.SetConfig("renderer", 12); break; + case 5: theApp.SetConfig("renderer", 13); break; + } + } - #if 0 - // I'll put the right variable names in later. - if (gtk_combo_box_get_active(GTK_COMBO_BOX(render_combo_box)) != -1) - renderer = gtk_combo_box_get_active(GTK_COMBO_BOX(render_combo_box)); - - // Crash, for some interlace options if (gtk_combo_box_get_active(GTK_COMBO_BOX(interlace_combo_box)) != -1) - theApp.SetConfig( "interlace", (int)gtk_combo_box_get_active(GTK_COMBO_BOX(interlace_combo_box)) ); - #endif + theApp.SetConfig( "interlace", (int)gtk_combo_box_get_active(GTK_COMBO_BOX(interlace_combo_box))); + theApp.SetConfig("extrathreads", (int)gtk_spin_button_get_value(GTK_SPIN_BUTTON(threads_spin))); - theApp.SetConfig("extrathreads", atoi((char*)gtk_entry_get_text(GTK_ENTRY(swthreads_text))) ); + theApp.SetConfig("filter", (int)gtk_combo_box_get_active(GTK_COMBO_BOX(filter_combo_box))); + theApp.SetConfig("shadeboost", (int)gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(shadeboost_check))); + theApp.SetConfig("paltex", (int)gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(paltex_check))); + theApp.SetConfig("fba", (int)gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(fba_check))); + theApp.SetConfig("aa1", (int)gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(aa_check))); + theApp.SetConfig("nativeres", (int)gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(native_res_check))); + + theApp.SetConfig("ShadeBoost_Saturation", (int)gtk_range_get_value(GTK_RANGE(sb_saturation))); + theApp.SetConfig("ShadeBoost_Brightness", (int)gtk_range_get_value(GTK_RANGE(sb_brightness))); + theApp.SetConfig("ShadeBoost_Contrast", (int)gtk_range_get_value(GTK_RANGE(sb_contrast))); - theApp.SetConfig("filter", (int)gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(filter_check))); - theApp.SetConfig("logz", (int)gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(logz_check))); - theApp.SetConfig("paltex", (int)gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(paltex_check))); - theApp.SetConfig("fba", (int)gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(fba_check))); - theApp.SetConfig("aa1", (int)gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(aa_check))); - theApp.SetConfig("windowed", (int)gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(win_check))); + theApp.SetConfig("msaa", (int)gtk_combo_box_get_active(GTK_COMBO_BOX(msaa_combo_box))); + theApp.SetConfig("resx", (int)gtk_spin_button_get_value(GTK_SPIN_BUTTON(resx_spin))); + theApp.SetConfig("resy", (int)gtk_spin_button_get_value(GTK_SPIN_BUTTON(resy_spin))); + + theApp.SetConfig("UserHacks_SkipDraw", (int)gtk_spin_button_get_value(GTK_SPIN_BUTTON(hack_skipdraw_spin))); + theApp.SetConfig("UserHacks_HalfPixelOffset", (int)gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(hack_offset_check))); + theApp.SetConfig("UserHacks_AlphaHack", (int)gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(hack_alpha_check))); + + theApp.SetConfig("UserHacks_MSAA", (int)gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(hack_msaa_check))); + theApp.SetConfig("UserHacks_WildHack", (int)gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(hack_wild_check))); + theApp.SetConfig("UserHacks_SpriteHack", (int)gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(hack_sprite_check))); + theApp.SetConfig("UserHacks", (int)gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(hack_enble_check))); + + // Let's just be windowed for the moment. + theApp.SetConfig("windowed", 1); gtk_widget_destroy (dialog); diff --git a/plugins/GSdx/GSLinuxLogo.h b/plugins/GSdx/GSLinuxLogo.h new file mode 100644 index 0000000000..1f976d4320 --- /dev/null +++ b/plugins/GSdx/GSLinuxLogo.h @@ -0,0 +1,2649 @@ +/* GdkPixbuf RGBA C-Source image dump 1-byte-run-length-encoded */ + +#include + +static const GdkPixdata gsdx_ogl_logo = { + 0x47646b50, /* Pixbuf magic: 'GdkP' */ + 24 + 71939, /* header length + pixel_data length */ + 0x2010002, /* pixdata_type */ + 1048, /* rowstride */ + 262, /* width */ + 71, /* height */ + /* pixel_data: */ + (unsigned char*)"\177\306M\223\377\322H\237\377\310E\227\377\303=\222\377\2513\200\377" + "\2521o\377\241)j\377\257&f\377\256*g\377\242(u\377\245\37g\377\244%l" + "\377\254/e\377\244\37`\377\2261c\377\217+e\377\2468i\377\2516q\377\225" + "@v\377\2216{\377\232,z\377\2328z\377\216.t\377\2200v\377\216/s\377\205" + "7v\377|-s\377|;a\377v,i\377\204*o\377~5v\377r-x\377\2031\204\377}6\177" + "\377z:}\377|\203\377cB\201\377r0t\377o6r\377" + "`D\205\377gA\205\377[\77\205\377^E}\377PFw\377U<\203\377W/\205\377L&" + "}\377\77$o\377\77/z\377H0t\377R(w\377R)v\377F0y\377L+k\377F0~\377D.\215" + "\377@+\204\377@7\210\37789\201\3776D{\377\216\377*C\227\377\35<\231\377#2\210\377'$z\377$" + "5~\37734\200\377>4\225\377A1\221\377@+\230\377<3\231\377:8\244\377A6" + "\243\37785\224\377G1\227\377>%\244\377=(\236\3776,\232\3771\36\243\377" + "2\30\224\3778\11\226\377\77\17\241\377@\35\222\3770\22\233\377/\23\234" + "\377-\32\242\3776\31\247\3771\16\246\3771\15\227\377/\22\234\377>\22" + "\232\3776\1\214\377A\0\214\377;\20\220\377C\14}\3779\5\210\3775\17}\377" + ":\10\204\377N\5\225\377D\0\204\377R\0\210\377^\2\215\377T\0\217\377S" + "\4\235\377Y\12\234\377T\14\216\377T\26\215\377R\23\226\377L\23\213\377" + "S\13\225\377Y\33\230\377S\3\214\377G\23\210\377J\33\204\377O\32\202\377" + "P\0v\377V\0p\377D\0m\377V\0q\377G\6}\377V\0j\377X\6w\377Y\20i\377K\1" + "r\377R\0{\377T\6l\377O\0m\377V\3x\377Z\14x\377S\11\211\377F\13\216\377" + "J\3\214\377Q\31\217\377R\40\231\377V*\241\377i\35\243\377Z/\242\377[" + "&\243\377Q)\254\377[&\252\377W1\252\377e+\240\377_4\244\377`0\250\377" + "]$\257\377g-\261\377e)\267\377c/\300\377j$\260\377k:\262\377sE\306\377" + "jK\273\377u\77\301\377pL\273\377zF\303\377mE\310\377r=\305\377x5\311" + "\377\177B\302\377\207/\313\377\2067\301\377x4\317\377\202>\322\377\201" + "+\311\377\1776\333\377\1778\324\377};\317\377\2029\317\377\2217\317\377" + "\2041\314\377\204>\306\377\203=\323\377\2160\324\377\231=\321\377\234" + "B\302\377\234>\327\377\246C\316\377\237%\337\377\2270\341\377\230\77" + "\352\377\245\77\355\377s\250L\345\377\262R\335\377\257S\353\377\255K" + "\344\377\253<\332\377\2110\265\377g\12\244\377:\0\200\377\2760\216\377" + "\311B\202\377\263C{\377\231\37k\377\177\2O\377q\0""8\377k\0""3\377e\0" + "6\377\\\0%\377[\0""2\377`\0\34\377]\0\36\377V\0#\377\\\0%\377X\0%\377" + "O\0#\377U\0!\377K\0%\377T\0""3\377Q\0+\377O\0*\377Q\0""1\377@\0\40\377" + "F\0\40\3775\0*\377.\0.\3771\0\37\377A\0\32\377:\0\"\3774\0""9\3772\0" + "@\377-\0@\3770\0E\377F\0B\377.\6-\3771\5.\377A\0.\377A\0B\377=\17C\377" + "<\4A\377=\13=\377I\22;\377F\11\77\377M\0""7\377E\11K\377C\15A\377P\13" + "3\377J\23""9\377E\24""2\377I\11""8\377M\4+\377>\2.\377J\0-\377M\0>\377" + "B\0<\377<\0D\3773\3A\377A\10I\377C\7""5\377<\7-\377N\11""2\377H\0""4" + "\377G\0&\377F\0""7\377D\0\77\377I\0""8\377<\0'\377M\0""4\377C\0:\377" + "6\0I\377A\0:\377N\0M\377A\0L\3779\0E\377F\0I\3777\0O\377/\0I\377;\0[" + "\377:\0Z\3776\0L\377.\0O\377.\2G\3771\0N\377'\2R\377-\0N\377\37\0I\377" + "\27\0E\377%\10:\377\33\10""9\377#\0<\377&\3H\377!\11A\377\"\0""1\377" + "\36\0#\377\31\6""2\377\12\5""4\377\34\4@\377\34\2C\377\21\3G\377\20\0" + "B\377\25\0;\377\16\0""2\377\20\0""2\377\13\0:\377\1\0""0\377\0\0""4\377" + "\0\0""9\377\202\0\0<\377'\0\0""1\377\1\0""7\377\0\0C\377\0\0E\377\0\0" + "8\377\2\0\77\377\0\0""8\377\0\0=\377\0\0""9\377\0\11F\377\0\0T\377\0" + "\10S\377\0\12L\377\0\16R\377\0\15""4\377\0\16""9\377\0\20K\377\0\26C" + "\377\0\30O\377\0\37A\377\0\40<\377\0\20D\377\0\13A\377\0\13E\377\0\2" + "\77\377\0\0\77\377\0\1@\377\0\0""5\377\0\0""8\377\0\0""0\377\0\0""3\377" + "\0\0C\377\0\0H\377\0\0J\377\0\0H\377\0\0O\377\0\0A\377\0\0M\377\6\0S" + "\377\202\0\0S\377\2\2\0K\377\0\0S\377\202\0\0Z\377}\0\0L\377\0\0N\377" + "\0\0I\377\0\0Z\377\0\0U\377\0\0_\377\0\0P\377\0\0I\377\0\0R\377\0\0O" + "\377\0\0P\377\0\0U\377\0\0A\377\0\0N\377\0\0K\377\0\0J\377\7\0@\377\4" + "\0P\377\7\0T\377\30\0T\377\17\0M\377\16\0S\377\0\0S\377\5\0N\377\3\0" + "\77\377\1\0I\377\0\0H\377\5\0E\377\0\0E\377\12\0N\377\0\0F\377\15\0\77" + "\377\13\0*\377\23\0,\377\4\0\36\377\0\0-\377\13\0""1\377\33\0-\377\12" + "\0%\377\12\0*\377\22\0""0\377\15\0'\377\32\0/\377\15\0%\377\20\0'\377" + "\7\0""5\377\11\0""1\377\22\0:\377\10\0B\377\0\0D\377\2\0P\377\0\0N\377" + "\3\0R\377\36\0f\377\"\0a\377\35\0]\377\13\0h\377\26\0f\377\27\0d\377" + "\40\0c\377\27\0V\377\13\0`\377\25\0X\377\"\0i\377'\0y\377\36\0w\377$" + "\0x\377#\0\200\377+\0z\3771\2\203\377%\0x\3772\0\202\3776\0\204\3777" + "\12\201\377*\21\211\3778\0\224\377:\5{\377;\0{\3777\0\211\3779\4\206" + "\377I\4\215\377K\0\214\377H\0\221\377D\4\223\377J\0\217\377@\1\226\377" + "2\0}\3772\0|\377I\0\210\377=\0\214\377H\1\223\377W\0\204\377T\0\221\377" + "O\0\205\377N\0\220\377Y\0\234\377T\0\214\377T\0\215\377U\0\224\377Z\0" + "\236\377s\24\251\377~!\266\377\2154\266\377\2347\277\377\205*\306\377" + "Y\15\236\3776\0y\3777\0`\377\233$v\377\255)p\377\227\23]\377g\0<\377" + ",\0\0\3777\0\0\3771\0\0\377-\0\11\3775\0\0\3778\0\0\377)\0\0\377*\0\0" + "\377\"\0\0\377&\0\0\377/\0\0\377%\0\0\377\35\0\0\377\202\31\0\0\377\7" + "\34\0\0\377\21\0\0\377\10\0\0\377\26\0\0\377\14\0\0\377\26\0\0\377\10" + "\0\0\377\202\7\0\0\377\77\2\0\0\377\13\0\0\377\4\0\21\377\0\0\17\377" + "\2\0\7\377\5\0\2\377\0\0\0\377\10\0\0\377\0\0\10\377\0\0\17\377\0\0\27" + "\377\16\0\15\377\11\0\7\377\17\0\11\377\12\0\17\377\20\0\17\377\27\0" + "\24\377\24\0\17\377\3\0\6\377\7\0\17\377\34\0\15\377\27\0\15\377\21\0" + "\0\377\37\0\1\377\17\0\0\377!\0\11\377\22\0\4\377\17\0\25\377\27\0\1" + "\377\31\0\3\377\32\0\6\377\35\0\0\377\22\0\0\377\25\0\26\377\21\0\13" + "\377\13\0\27\377\5\0\27\377\15\0\3\377\23\0\11\377\10\0\11\377\15\0\11" + "\377\15\0\27\377\10\0\17\377\1\0$\377\22\0'\377\14\0\35\377\7\0#\377" + "\13\0\40\377\0\0\"\377\13\0\26\377\0\0\37\377\0\0\23\377\1\0\21\377\0" + "\0\17\377\0\0\"\377\0\0\35\377\0\0\21\377\0\0\16\377\0\0\23\377\0\0\4" + "\377\0\0\10\377\0\0\5\377\0\0\12\377\204\0\0\0\377\2\0\0\21\377\0\0\14" + "\377\202\0\0\0\377\2\0\0\6\377\0\0\2\377\203\0\0\0\377\3\0\0\5\377\0" + "\0\17\377\0\0\26\377\203\0\0\0\377\17\0\0\3\377\0\0\13\377\0\0\15\377" + "\0\0\5\377\0\0\14\377\0\0\11\377\0\0\20\377\0\0\25\377\0\0\27\377\0\0" + "\16\377\0\0\12\377\0\0\22\377\0\0\12\377\0\0\10\377\0\0\7\377\206\0\0" + "\0\377\2\0\0\2\377\0\0\11\377\202\0\0\0\377\2\0\0\16\377\0\0\2\377\203" + "\0\0\0\377\36\0\0\11\377\0\0\5\377\0\0\12\377\0\0\16\377\0\0\24\377\0" + "\0\27\377\0\0\31\377\0\0\23\377\0\0\32\377\0\0\20\377\0\0\24\377\0\0" + "\31\377\0\0\16\377\0\0\23\377\0\0\13\377\0\0\22\377\0\0\27\377\0\0'\377" + "\0\0\24\377\0\0\33\377\0\0\31\377\0\0\21\377\0\0\24\377\0\0\36\377\0" + "\0\27\377\0\0\36\377\0\0\32\377\0\0\10\377\0\0\21\377\0\0\20\377\202" + "\0\0\17\377\12\0\0\15\377\0\0\30\377\0\0\36\377\0\0\22\377\0\0\17\377" + "\0\0\20\377\0\0\"\377\0\0\26\377\0\0\10\377\0\0\16\377\202\0\0\21\377" + "\3\0\0\2\377\0\0\17\377\0\0\3\377\214\0\0\0\377\11\0\0\4\377\0\0\20\377" + "\0\0\12\377\0\0\21\377\0\0\32\377\0\0\27\377\0\0'\377\0\0\40\377\0\0" + "\34\377\202\0\0)\377\177\0\0'\377\0\0""4\377\0\0+\377\0\0-\377\0\0/\377" + "\0\0(\377\0\0""1\377\0\0""6\377\0\0G\377\0\0I\377\0\0>\377\0\0N\377\0" + "\0U\377\0\0M\377\0\0O\377\0\0Q\377\0\0O\377\7\0T\377\4\0M\377\4\0V\377" + "\13\0V\377\17\0N\377\17\0M\377\21\0Q\377\30\0^\377\31\0_\377\1\0P\377" + "\10\0Q\377\15\0N\377\0\0G\377\14\0M\377\17\0U\377\2\0X\377\0\0V\377\15" + "\0Z\377\31\0X\377\37\0a\377\20\0T\377\25\0]\377\14\0^\377\17\0_\377\30" + "\0Z\377+\0f\377!\0e\377\34\0c\377\40\0c\377>\0\201\377M\7\233\377P\4" + "\227\377:\0x\377)\0f\377/\0o\377\214\3W\377y\0N\377`\0\35\3775\0\0\377" + "4\0\0\377,\0\0\377+\0\0\3776\0\0\3771\0\0\377)\0\0\3776\0\0\377#\0\0" + "\3776\0\0\377)\0\0\377*\0\0\377%\0\0\377+\0\0\377\30\0\0\377!\0\0\377" + "-\0\0\377\24\0\0\377\13\0\4\377\22\0\0\377\23\0\11\377\23\0\0\377\16" + "\0\0\377\0\0\0\377\0\0\2\377\0\0\0\377\1\0\1\377\2\0\7\377\15\0\26\377" + "\2\0\14\377\0\0\0\377\0\0\5\377\0\0\17\377\0\0\6\377\11\0\13\377\7\0" + "\23\377\25\0\30\377\17\0\4\377\6\0\10\377\23\0\32\377\17\0\36\377\17" + "\0\20\377\15\0\22\377\22\0\31\377\15\0\34\377\17\0\23\377\32\0\24\377" + "\27\0\21\377\15\0\5\377\21\0\11\377\30\0\11\377\22\0\6\377\21\0\21\377" + "\17\0\15\377\27\0\20\377\22\0\16\377\30\0\16\377\10\0\11\377\6\0\27\377" + "\0\0\24\377\0\0\37\377\0\0\10\377\25\0\31\377\17\0\21\377\23\0\26\377" + "\21\0\26\377\12\0\30\377\0\0\35\377\3\0$\377\0\0*\377\0\0#\377\0\0*\377" + "\4\0\0,\377\5\0\36\377\0\0\34\377\0\0)\377\202\0\0\25\377\10\0\0!\377" + "\0\0'\377\0\0\32\377\0\0\25\377\0\0\17\377\0\0\26\377\0\0\5\377\0\0\22" + "\377\202\0\0\14\377\204\0\0\0\377\5\0\0\1\377\0\0\2\377\0\0\7\377\0\0" + "\13\377\0\0\14\377\203\0\0\0\377\202\0\0\3\377\204\0\0\0\377\15\0\0\5" + "\377\0\0\2\377\0\0\6\377\0\0\23\377\0\0\13\377\0\0\12\377\0\0\6\377\0" + "\0\0\377\0\0\13\377\0\0\11\377\0\0\5\377\0\0\7\377\0\0\10\377\213\0\0" + "\0\377\3\0\0\2\377\0\0\0\377\0\0\5\377\203\0\0\0\377\1\0\0\1\377\202" + "\0\0\3\377\24\0\0\10\377\0\0\0\377\0\0\21\377\0\0\17\377\0\0\33\377\0" + "\0\37\377\0\0\22\377\0\0\10\377\0\0\16\377\0\0\33\377\0\0\34\377\0\0" + "\32\377\0\0\36\377\0\0\31\377\0\0\26\377\0\0\25\377\0\0\21\377\0\0\12" + "\377\0\0\24\377\0\0\36\377\202\0\0\34\377\7\0\0\31\377\0\0\20\377\0\0" + "\6\377\0\0\15\377\0\0\30\377\0\0\21\377\0\0\20\377\202\0\0\23\377\11" + "\0\0\25\377\0\0\13\377\0\0\10\377\0\0\4\377\0\0\16\377\0\0\12\377\0\0" + "\15\377\0\0\0\377\0\0\5\377\203\0\0\0\377\5\0\0\2\377\0\0\0\377\0\0\1" + "\377\0\0\0\377\0\0\5\377\211\0\0\0\377\177\0\0\14\377\0\0\11\377\0\0" + "\20\377\0\0\17\377\0\0\33\377\0\0\22\377\0\0\25\377\0\0\"\377\0\0*\377" + "\0\0\36\377\0\0(\377\0\0#\377\0\0/\377\0\0""2\377\0\0+\377\0\0(\377\0" + "\0.\377\0\0\77\377\0\0F\377\0\0P\377\0\0E\377\0\0F\377\0\0L\377\0\0J" + "\377\0\0N\377\0\0T\377\0\0I\377\0\0J\377\0\0L\377\0\0N\377\0\0G\377\10" + "\0P\377\0\0Z\377\0\0R\377\4\0T\377\0\0Y\377\5\0Q\377\7\0U\377\13\0T\377" + "\0\0W\377\0\0[\377\0\0R\377\3\0G\377\6\0K\377\5\0X\377\0\0E\377\25\0" + "Z\377\10\0S\377\4\0b\377\23\0R\377\13\0V\377\32\0Z\377%\0[\377\27\0V" + "\377\37\0h\377!\0X\377*\0V\377(\0e\3779\0m\3771\0m\3775\0v\377+\0q\377" + "3\0o\377s\0""6\377k\0\25\377;\0\2\3777\0\0\377A\7\1\377M\24\0\377;\22" + "\1\377-\31\0\3775\15\0\3770\25\0\377F\13\0\377L\20\0\377@\32\0\377E\23" + "\0\377C\20\5\377@\21\13\377:\31\10\377/\25\10\3778\37\11\377,\36\14\377" + "\32\25\6\377\25\22\7\377\32\21\25\377\25\24\34\377\10\35\7\377\11+\1" + "\377\4!\12\377\4\37\6\377\10(\13\377\0*\25\377\17+\25\377\6'\20\377\16" + "'\27\377\12)\35\377\6.\20\377\3)\27\377\16''\377\14.)\377\15""7\26\377" + "\20""8\11\377#7\26\377!S\36\377$Z\"\377\34X\37\377\32X2\377\37W#\377" + "\30d.\377\26e3\377\20h\33\377\21T\36\377\33[\32\377\23S\31\377\"X/\377" + "\26I\35\377\12@\37\377\11""6\27\377\31-#\377\14$\26\377\25/!\377\27""7" + "\"\377\16.!\377\6,\16\377\25&\37\377\25'*\377@\37%\34\377\15/\26\377" + "\25""2\24\377\33&%\377\17+3\377\2\37-\377\2""09\377\5(1\377\0""4=\377" + "\0""6>\377\0B0\377\0I3\377\0K-\377\1""8'\377\0\77)\377\11I1\377\3<&\377" + "\0""61\377\0E2\377\0:\32\377\0O&\377\4P$\377\0I\32\377\0M\30\377\0G%" + "\377\0D\13\377\0""8\20\377\0.\33\377\0""0\12\377\0,\6\377\0=\20\377\0" + "-\22\377\0""0\5\377\0/\16\377\0)\13\377\0!\10\377\0(\10\377\0""1\11\377" + "\0""1\34\377\0-\15\377\0\33\0\377\0.\1\377\0%\0\377\0#\0\377\0'\14\377" + "\0\32\6\377\0\36\0\377\0\31\15\377\0+\12\377\0&\0\377\0*\0\377\0(\0\377" + "\0!\11\377\0'\11\377\0""6\13\377\0/\0\377\0""7\0\377\0=\0\377\0;\0\377" + "\0/\0\377\0>\0\377\0N\0\377\0T\0\377\0L\0\377\202\0B\0\377,\0<\0\377" + "\0+\0\377\0""1\0\377\0:\0\377\0/\0\377\0""1\0\377\0(\0\377\0)\0\377\0" + "\27\10\377\0\37\0\377\0&\0\377\0\40\5\377\0\26\11\377\0\36\12\377\0\37" + "\26\377\0\20\26\377\0\23\14\377\0\31\37\377\0\0\26\377\0\7\10\377\0\0" + "\10\377\0\1\27\377\0\5\12\377\0\23\26\377\0\0\13\377\0\0\32\377\0\0\23" + "\377\0\0\0\377\0\0\10\377\0\0\21\377\0\0(\377\0\0\34\377\0\0\30\377\0" + "\0\21\377\0\0\25\377\0\0\36\377\0\23\35\377\0\30\31\377\0\"\37\377\0" + "\32\31\377\0\0\23\377\0\0\21\377\0\0\26\377\0\0\16\377\203\0\0\0\377" + "\3\0\0\2\377\0\0\4\377\0\0\1\377\205\0\0\0\377\2\0\0\7\377\0\0\1\377" + "\203\0\0\0\377\35\0\7\3\377\0\26\0\377\0+\3\377\0""4\17\377\0L\11\377" + "\0M\23\377\0G(\377\4^'\377\5]&\377\5[+\377\0^$\377\4R\33\377\0E\36\377" + "\0M(\377\0L+\377\0N3\377\6G/\377\0>0\377\0):\377\0%7\377\0\30""4\377" + "\0\21""4\377\0\32/\377\0\26""5\377\0\27<\377\0\20@\377\0\14\77\377\0" + "\0\77\377\0\14H\377\202\0\2L\377\177\0\3[\377\0\0J\377\0\15R\377\0\20" + "L\377\0\30T\377\0$M\377\0#R\377\2;P\377\11YY\377\0mj\377\16lv\377+xu" + "\377\37kq\377\25Mi\377\0;e\377\6%W\377\0\37_\377\3&e\377\0""9a\377\4" + "0`\377\5&V\377\0/R\377\4EZ\377\7;U\377\17""4Y\377\0""5`\377\3A[\377\10" + ":a\377\23-W\377\34$^\377\36+`\377&#d\377&\35\\\377\35!b\377-&g\377.5" + "t\3775=u\377.\0g\377+\0u\377/\0q\3773\0r\377k\0""7\377B\0\17\377,\0\0" + "\3774\0\0\377>\21\0\3771\15\0\3774\30\5\377+\31\7\377>\21\5\377<\37\7" + "\377I\26\12\377H\26\1\377=\21\15\377R\27\22\377C,\20\377>&\13\3777\32" + "\22\3770\32\6\377'\30\20\377/\34\21\377\35\34\15\377\32\35\31\377\33" + "\24\11\377\36\31\26\377\16\30\1\377\20\33\1\377\0\36\14\377\2\33\0\377" + "\10!\6\377\3(\16\377\0""4\36\377\10(\21\377\15""1\33\377\7""7\23\377" + "\13.\"\377\24*/\377\17)&\377#3'\377\27;\15\377\32\77\20\377&I\21\377" + "#F\24\377\30P'\377\32^0\377\25c+\377\27S)\377!].\377\40b(\377\24h$\377" + "\27i-\377%[&\377\37Y-\377\20L'\377\21P1\377\13=.\377\22A*\377\3""8#\377" + "\15>,\377\6""3!\377\13;+\377\32\40""3\377\21""3'\377\26\"-\377\32#&\377" + "\24.+\377\24,3\377\13+*\377\1\35\34\377\12#$\377\4\",\377\26-.\377\5" + "E6\377\2F-\377\0""6.\377\0;+\377\14F5\377\12K<\377\15@.\377\1F(\377\0" + "=/\377\0Q-\377\1F,\377\0A#\377\0C0\377\0E1\377\0D\35\377\33\1<\37\377" + "\0;\40\377\0@\37\377\0""2\22\377\0:\26\377\0-\20\377\0+\30\377\0)\10" + "\377\0""3\22\377\0*\10\377\0""1\14\377\0""4\2\377\0*\10\377\0""1\0\377" + "\0-\12\377\0""0\0\377\0\"\0\377\0""4\2\377\0.\16\377\0(\11\377\0*\3\377" + "\0(\6\377\0+\7\377\0)\12\377\0\34\15\377\0#\4\377\0+\0\377\202\0""1\0" + "\377\25\0""3\0\377\0.\0\377\0""1\0\377\0""2\0\377\0>\2\377\0E\0\377\0" + "3\0\377\0@\0\377\0J\0\377\0H\0\377\0O\0\377\0L\0\377\0M\0\377\0H\0\377" + "\0F\0\377\0B\0\377\0G\0\377\0K\0\377\0<\4\377\0""2\0\377\0*\0\377\202" + "\0+\0\377\21\0""4\0\377\0""5\0\377\0(\0\377\0\32\0\377\0\40\12\377\0" + ",\13\377\0(\5\377\0)\21\377\0\40\15\377\0'\22\377\0\35\21\377\0\31\4" + "\377\0\6\14\377\0\22\14\377\0\27\5\377\0\27\14\377\0\17\5\377\202\0\0" + "\0\377\21\0\0\3\377\0\0\33\377\0\0\32\377\0\0\30\377\0\0\16\377\0\0\12" + "\377\0\0\3\377\0\0\12\377\0\0\35\377\0\11\15\377\0!\13\377\0\32\14\377" + "\0\14\15\377\0\0\10\377\0\0\26\377\0\0\0\377\0\0\4\377\203\0\0\0\377" + "\1\0\0\3\377\210\0\0\0\377\177\0\0\10\377\0\0\0\377\0\7\0\377\0\0\0\377" + "\0\5\0\377\0\24\11\377\0%\10\377\0=\20\377\0\77\11\377\0B\22\377\10N" + "\24\377\0U$\377\0h7\377\0c\36\377\0f\33\377\0U1\377\0Q0\377\0Q2\377\0" + "V/\377\0Q.\377\0K;\377\2Q\77\377\0JB\377\0A9\377\0%6\377\0\35""7\377" + "\0\32.\377\0\35""2\377\0\20""4\377\0\10""4\377\0\1E\377\0\6>\377\0\0" + ";\377\0\7L\377\0\4Q\377\0\1I\377\0\6M\377\0\4]\377\0\23[\377\0\32`\377" + "\0\30e\377\0\"X\377\0+Y\377\0IZ\377\2vk\377\24{q\377\30s\177\377\14r" + "u\377\11_n\377\4Il\377\0,j\377\0""3i\377\0""6\\\377\0""3e\377\0-Z\377" + "\0""7c\377\0NX\377\0\\K\377\6Lc\377\20@`\377\4Kd\377\21M]\377\10Ue\377" + "\14Kf\377\33=Y\377\30""7X\377\30.`\377\35'f\377\21\36m\377\30'o\377\35" + ",\202\377,=|\377)\0n\377'\0}\377-\0z\377-\5x\377k\0)\377O\0\15\377A\0" + "\0\3778\0\5\3772\10\21\3778\16\6\377;\31\13\3777\32\30\377\77\33\26\377" + "C(\20\377=#\40\3778\31\24\3771!\5\377<*\31\3772+\17\377=/\17\3774\37" + "\12\3770)\11\377'\32\7\377\40\27\21\377\24\35\23\377\34!\16\377\34*\26" + "\377\10)&\377\23-\14\377\10\"\5\377\22\"\4\377\14-\1\377\32+\30\377\22" + "<\27\377\20%\40\377\5(\33\377\20-\30\377\16.\17\377\14""6\25\377\16""4" + "\33\377\16=\15\377\20/\27\377\21=\23\377\34\77\25\377\34>!\377!H%\377" + "\32K&\377\16N\36\377\14[\30\377\16Y*\377\16k\30\377\14d\30\377\15j)\377" + "\11i!\377\20b*\377V\25a0\377\21R;\377\23T3\377\12L-\377\6O+\377\15E1" + "\377\26>>\377\10;\77\377\20""2.\377\2""4,\377\11/(\377\0""8,\377\0""5" + "9\377\0'9\377\0\35""1\377\0'\"\377\2(1\377\7)+\377\0""6'\377\12""1!\377" + "\12""76\377\0""7-\377\0""74\377\2H5\377\0G.\377\16D,\377\2G)\377\6=." + "\377\13K2\377\20>'\377\1=$\377\0D-\377\0T(\377\0J.\377\0""7\"\377\0=" + "!\377\0""4\27\377\0""7\36\377\0""2\35\377\0""7!\377\0""6\23\377\0+\2" + "\377\0""7\22\377\0'\14\377\0\37\21\377\0-\6\377\0%\0\377\0""6\0\377\0" + ")\1\377\0""1\6\377\0""0\15\377\0""1\16\377\0;\30\377\0""7\24\377\0D\6" + "\377\0\77\4\377\0""8\0\377\0;\0\377\0-\0\377\0\35\0\377\0#\0\377\0!\0" + "\377\0""2\0\377\0""3\0\377\0!\0\377\0/\0\377\0,\0\377\0=\0\377\0@\0\377" + "\0=\0\377\0L\0\377\0D\0\377\0E\0\377\0S\0\377\0""5\0\377\0""7\0\377\0" + "C\0\377\0@\0\377\0D\0\377\0V\0\377\0B\0\377\0<\0\377\0""5\0\377\0""2" + "\0\377\0""1\0\377\0&\0\377\202\0,\0\377\24\0\40\0\377\0*\3\377\0(\0\377" + "\0,\0\377\0&\5\377\0%\1\377\0""1\0\377\0\34\7\377\0!\0\377\0\33\0\377" + "\0&\0\377\0\25\0\377\0\32\0\377\0\32\5\377\0\32\0\377\0\27\0\377\0\26" + "\0\377\0\0\15\377\0\13\22\377\0\0\6\377\205\0\0\0\377\6\0\0\10\377\0" + "\0\2\377\0\4\2\377\0\20\2\377\0\16\16\377\0\10\23\377\202\0\0\0\377\1" + "\0\0\4\377\204\0\0\0\377\2\0\0\1\377\0\0\3\377\205\0\0\0\377\3\0\0\7" + "\377\0\0\17\377\0\0\3\377\203\0\0\0\377\177\0\14\0\377\0\36\13\377\0" + "'\14\377\0-\27\377\0""6\24\377\0B\33\377\0S\37\377\0[\"\377\0\\#\377" + "\0o(\377\0p,\377\0h8\377\0n2\377\0X9\377\0b4\377\6n8\377\14cF\377\0g" + "K\377\0b<\377\0UI\377\0;=\377\0(1\377\0%+\377\0\30""0\377\0\32:\377\0" + "\13E\377\0\26<\377\0\7/\377\0\7@\377\0\20O\377\0\14G\377\0\24M\377\0" + "\15W\377\0\20^\377\0\36`\377\0\27e\377\0%`\377\0)[\377\0Ij\377\0la\377" + "\12\177j\377\31\221v\377\26\214{\377\2zy\377\14_p\377\3N^\377\0\77a\377" + "\6/k\377\0""3l\377\0""2Y\377\0""4c\377\0B]\377\0KX\377\0Ye\377\0Vd\377" + "\2N]\377\0Xa\377\0^i\377\5Q^\377\23FW\377$Ab\377\21;a\377!7[\377\32""8" + "i\377\36&n\377\24""5o\377)5v\377#:\203\377\40\25u\377,\13f\3771\15l\377" + ":\4\177\377s\0'\377N\0\15\3770\0\11\3779\0\5\377.\22\17\377,\24\25\377" + "2\36\25\377+\37#\37723\26\377<4\37\377>\"\22\3770*\20\377*\34\22\377" + "\36!\20\377(\40\31\377\37\35\10\377\35\15\0\377\35\23\11\377\17\30\30" + "\377\5\22\26\377\24\34\27\377\26!\27\377\10""8\33\377\12""3(\377\22""3" + "\21\377\13""5\14\377\26""8\27\377\16,\32\377\7""4!\377\0,\31\377\0'\36" + "\377\0$\24\377\0\40\13\377\2(\36\377\0""6\23\377\15D\23\377\13""4#\377" + "\17""2\36\377\27""8\34\377\27""8\"\377\24=\35\377\34B\35\377\35\\-\377" + "\24f$\377\3b%\377\15d\40\377\10l-\377\14o\26\377\0^\40\377\1]%\377\16" + "^2\377\5Y1\377\5^3\377\0]7\377\0O1\377=\0I-\377\0D6\377\3C-\377\25""7" + "1\377\27""5;\377\5/=\377\10""8.\377\11""21\377\2-/\377\2\";\377\12(," + "\377\0-)\377\0\"0\377\4.,\377\10,!\377\13""8\34\377\0""8$\377\0>*\377" + "\0""85\377\0D0\377\0E0\377\5G4\377\3=(\377\4E'\377\2W(\377\11Q-\377\16" + "E(\377\2H%\377\1J*\377\0\77%\377\1;#\377\0E!\377\0@\37\377\0""1&\377" + "\0""3\23\377\0""8\17\377\0'\10\377\0*\11\377\0\30!\377\0'\20\377\0""5" + "\23\377\0/\7\377\0""1\0\377\0""5\2\377\0""8\20\377\0-\14\377\0A\15\377" + "\0=\16\377\0""4\12\377\0""1\12\377\0J\0\377\0;\0\377\0""3\0\377\0""2" + "\0\377\0-\0\377\0""0\0\377\0""2\0\377\0\"\0\377\0+\0\377\0'\0\377\0/" + "\0\377\202\0,\0\377\35\0""2\0\377\0""0\0\377\0""4\0\377\0F\0\377\0""8" + "\0\377\0@\0\377\0=\0\377\0""7\0\377\0""2\0\377\0D\0\377\0:\0\377\0B\0" + "\377\0""0\0\377\0D\0\377\0P\0\377\0L\0\377\0:\0\377\0""6\0\377\0.\0\377" + "\0-\0\377\0.\0\377\0*\0\377\0-\0\377\0""0\0\377\0:\0\377\0""3\0\377\0" + "/\0\377\0""8\0\377\0)\0\377\203\0*\0\377\11\0\30\2\377\0\17\0\377\0#" + "\0\377\0\"\0\377\0\33\0\377\0\16\0\377\0\21\11\377\0\14\5\377\0\0\1\377" + "\205\0\0\0\377\7\0\0\1\377\0\0\3\377\0\5\0\377\0\10\16\377\0\0\2\377" + "\0\0\4\377\0\0\1\377\216\0\0\0\377$\0\0\10\377\0\0\0\377\0\0\11\377\0" + "\0\20\377\0\0\0\377\0\14\2\377\0\26\16\377\0\40\16\377\0""4\16\377\0" + "1\11\377\0+\23\377\0>\31\377\0`\34\377\0f(\377\0n8\377\0r<\377\15l6\377" + "\3s\77\377\11u>\377\0}=\377\0yG\377\0{;\377\0~\77\377\0t;\377\0p7\377" + "\0oC\377\0Q5\377\0""9.\377\0#.\377\0\26""8\377\0\25+\377\0\24""6\377" + "\0\15;\377\0\11G\377\0\35D\377\0\34Q\377\202\0*R\377\177\0""2]\377\0" + ",^\377\0""2b\377\0.W\377\0=g\377\0Kh\377\0ae\377\3\211t\377\15\214p\377" + "\27zp\377\5mm\377\0cp\377\0X\\\377\0Un\377\0Co\377\0L]\377\0q\377\36Lu\377*'\203\377$!z\377&\23l\377&\26q\377e\0""8\377H\0\25" + "\377*\0\0\377&\0\16\377,\15\40\3770\34\17\377$\33\25\377(\26\34\377)" + "\21\33\377\"'\22\377\36)\31\377\37+\25\377\23\33\7\377\17\31\26\377\34" + "\36\13\377\36&\12\377\16\40\34\377\24#.\377\11\33\26\377\0\40\40\377" + "\17\35&\377\21\40\35\377\0,\25\377\0!\"\377\11\"\35\377\3%'\377\16!&" + "\377\5\"\36\377\3.\37\377\0)\30\377\0!\23\377\0&\34\377\0""1\31\377\0" + "/\35\377\7""6\33\377\2""6\30\377\0""8\33\377\5>\27\377\0\77!\377\0>\40" + "\377\23R\40\377\27E\37\377\0Z\33\377\0\\\31\377\3b/\377\0h\36\377\0g" + ")\377\0\\0\377\1n6\377\3Z)\377\11Z6\377\0R4\377\0U-\377\0^;\377\0\\-" + "\377\0O\"\377\5L0\377\5B*\377\1=5\377\2/3\377\0,-\377\0-*\377\5,2\377" + "\30)7\377\24'0\377\0""0&\377\0--\377\0$%\377\3$)\377\0,.\377\0""1%\377" + "\0.#\377\0""3,\377\0B\36\377\0<(\377\0:\"\377\0I!\377\0Q*\377\0W$\377" + "\13K\32\377\0;\31\377\0H\25\377\0I\23\377\0D\13\377\1E\27\377\0F\36\377" + "\0J\36\377\0>\30\377\35\0D\13\377\0/\20\377\0""0\10\377\0'\13\377\0." + "\26\377\0""0\20\377\0""9\31\377\0&\16\377\0#\20\377\0+\22\377\0\"\6\377" + "\0-\20\377\0""8\3\377\0""0\0\377\0""1\23\377\0A\2\377\0F\0\377\0""1\0" + "\377\0\77\0\377\0@\0\377\0I\0\377\0:\0\377\0<\0\377\0I\0\377\0E\0\377" + "\0B\0\377\0""5\0\377\0)\0\377\0$\0\377\202\0\35\0\377'\0-\0\377\0>\0" + "\377\0""9\0\377\0""4\0\377\0-\0\377\0'\0\377\0C\0\377\0E\0\377\0\77\0" + "\377\0F\0\377\0=\0\377\0D\0\377\0C\0\377\0""6\0\377\0=\0\377\0Q\0\377" + "\0R\0\377\0@\0\377\0F\0\377\0;\0\377\0:\0\377\0""7\0\377\0+\0\377\0;" + "\0\377\0M\0\377\0;\0\377\0\77\0\377\0<\0\377\0/\0\377\0%\0\377\0\25\0" + "\377\0\30\0\377\0\"\0\377\0,\0\377\0\31\0\377\0$\0\377\0\34\0\377\0\20" + "\0\377\0\26\0\377\213\0\0\0\377\1\0\4\0\377\204\0\0\0\377\4\0\0\13\377" + "\0\0\2\377\0\0\14\377\0\0\3\377\211\0\0\0\377\177\0\0\13\377\0\6\0\377" + "\0\16\0\377\0\31\0\377\0""1\7\377\0'\13\377\0""5\15\377\0""2\21\377\0" + ";\17\377\0=\14\377\0N\40\377\0K\40\377\0`1\377\0i2\377\0n6\377\0q;\377" + "\0p:\377\0\202:\377\0wB\377\0{7\377\0\2056\377\0\205F\377\0\200E\377" + "\0yI\377\0`2\377\0O6\377\0""17\377\0\21""8\377\0\31""3\377\0\17<\377" + "\0\17A\377\0\21E\377\0#K\377\0*M\377\0-Y\377\0""6P\377\0""9O\377\0""6" + "Z\377\0""8H\377\0DM\377\0NY\377\0Dg\377\0j_\377\0\215`\377\0\212i\377" + "\0\226u\377\0xm\377\0gj\377\0^b\377\0T_\377\0Vg\377\0Mf\377\0B`\377\0" + "\77c\377\0Ci\377\0G]\377\0L`\377\0Xc\377\0]a\377\4\\h\377\2Vi\377\2Y" + "h\377\20Xx\377\20Mw\377\17Nt\377\5\77v\377\0>\203\377\0E\202\377\0=~" + "\377\5:\202\377\23""6\200\3771O\242\377\32""2\222\377\3\36u\377\26\23" + "|\377g\0\77\377H\0&\377*\0\30\377-\0\25\377.\14\34\377,\11\"\377#\13" + "\40\377&\11%\377(\32\26\377\35\21\17\377\26\32\26\377\32\27\25\377\16" + "\34\13\377\10\25\17\377\27\34\24\377\21\30\31\377\5\36\33\377\20\26\31" + "\377\11\27\36\377\15\14$\377\6\17\31\377\13\25\32\377\1\36\26\377\6\27" + "$\377\11\34\40\377\11\36\32\377\1%(\377\10\34%\377\0$\24\377\0'\25\377" + "\0""0\34\377\0""2$\377\0%'\377\0.\"\377\0""4+\377\0/\"\377\0C)\377\0" + ":+\377\4<.\377\0:'\377\2B7\377\0J0\377\0R+\377\0[/\377\0l-\377\0i*\377" + "\14b8\377\7h\77\377\1b=\377\0\\8\377\0\\+\377\0^7\377>\4a0\377\10b3\377" + "\0T+\377\0M/\377\0J.\377\0E4\377\0=2\377\1""71\377\7;*\377\20@)\377\5" + "94\377\4/6\377\0/2\377\7""4!\377\4%\40\377\11/(\377\3.+\377\0-&\377\0" + "1*\377\3/\37\377\0-(\377\0+&\377\0\77\"\377\0V\35\377\3_\35\377\0\\#" + "\377\2W\40\377\3D\15\377\0H\13\377\0N%\377\0@+\377\0B\22\377\0S\27\377" + "\0N\35\377\0A#\377\0J\17\377\0>\10\377\0""9\0\377\0A\10\377\0>\12\377" + "\0""2\13\377\0+\15\377\0""7\34\377\0\"\27\377\0#\20\377\0!\14\377\0\"" + "\16\377\0(\12\377\0.\0\377\0<\17\377\0J\7\377\0@\22\377\0A\10\377\0I" + "\0\377\0<\0\377\0""7\0\377\0<\0\377\0F\0\377\0O\0\377\0G\0\377\0R\0\377" + "\0T\0\377\202\0A\0\377\1\0""6\0\377\202\0=\0\377\4\0+\0\377\0""6\0\377" + "\0(\0\377\0""5\0\377\202\0A\0\377\2\0D\0\377\0F\0\377\202\0C\0\377\26" + "\0""7\0\377\0=\0\377\0@\0\377\0E\0\377\0G\0\377\0M\0\377\0S\0\377\0E" + "\0\377\0A\0\377\0""3\0\377\0""2\0\377\0C\0\377\0B\0\377\0I\0\377\0O\0" + "\377\0I\0\377\0C\0\377\0>\0\377\0.\0\377\0%\0\377\0\36\0\377\0#\0\377" + "\202\0$\0\377\6\0\35\0\377\0\30\0\377\0\21\0\377\0\22\0\377\0\14\0\377" + "\0\1\0\377\210\0\0\0\377\202\0\13\0\377\212\0\0\0\377\3\0\0\15\377\0" + "\0\0\377\0\0\10\377\205\0\0\0\377\177\0\16\0\377\0\30\0\377\0\22\0\377" + "\0\40\14\377\0%\2\377\0\77\26\377\0@\16\377\0""8\27\377\0;\4\377\0D\10" + "\377\0B\37\377\0""9\37\377\0S(\377\0Y/\377\0l8\377\0p\77\377\0\1778\377" + "\0\202D\377\0|5\377\0x:\377\0\200F\377\0\213H\377\0\220K\377\0\211@\377" + "\0\201@\377\0a\77\377\0CB\377\0""2\77\377\0\32E\377\0\32>\377\0\31H\377" + "\0\"J\377\0\30P\377\0%Q\377\0*P\377\0$Q\377\0""2U\377\0/W\377\0FS\377" + "\0Ab\377\0Dg\377\0T[\377\0pd\377\0\222a\377\0\232\201\377\0\240\177\377" + "\0\215y\377\0pr\377\0gm\377\0U\\\377\0Ce\377\0\77i\377\0@o\377\0Hp\377" + "\0\0\377\0;\0\377\0B\0\377\0H\13\377\0E\17\377\0""5\16\377\0,\23\377" + "\0*\26\377\0&\15\377\0""7\0\377\0""8\3\377\0=\15\377\0@\26\377\0;\30" + "\377\0E\17\377\0F\10\377\0R\0\377\0J\0\377\0B\0\377\0G\0\377\0D\0\377" + "\0;\0\377\0N\0\377\0f\0\377\0d\0\377\0g\0\377\0O\0\377\0V\0\377\0J\0" + "\377\0""3\0\377\0;\0\377\0""2\0\377\0""7\0\377\0""0\0\377\0""4\0\377" + "\0>\0\377\0C\0\377\0:\0\377\0P\0\377\0\77\0\377\0""9\0\377\0<\0\377\0" + "@\0\377\0G\0\377\0N\0\377\0K\0\377\0O\0\377\0E\0\377\0:\0\377\0""8\0" + "\377\0""6\0\377\0""9\0\377\0""6\0\377\0:\0\377\0D\0\377\0E\0\377\0I\0" + "\377\0A\0\377\0""3\0\377\0;\0\377\202\0""1\0\377\13\0'\0\377\0)\0\377" + "\0""0\0\377\0\"\0\377\0\31\0\377\0\22\0\377\0\24\0\377\0\20\0\377\0\17" + "\0\377\0\21\0\377\0\5\0\377\203\0\3\0\377\6\0\11\0\377\0\16\0\377\0\5" + "\0\377\0\11\0\377\0\0\0\377\0\2\0\377\203\0\0\0\377\3\0\2\0\377\0\0\0" + "\377\0\0\2\377\203\0\0\0\377\1\0\0\7\377\203\0\0\0\377\2\0\0\1\377\0" + "\1\2\377\202\1\1\3\377\177\2\21\4\377\2\20\4\377\2/\4\377\1*\2\377\1" + "4\1\377\0""5\0\377\0""3\13\377\0-\0\377\0;\13\377\0""4\12\377\0""5\13" + "\377\0>\20\377\0Q\27\377\0d\34\377\0f\33\377\0y2\377\0\201*\377\0\206" + "*\377\0\206+\377\0\2149\377\0\220<\377\0\231;\377\0\223B\377\0\2154\377" + "\0|<\377\0`<\377\0T\77\377\0>/\377\0""49\377\0\36""1\377\0!>\377\0\21" + "B\377\0\31L\377\0\35F\377\0#;\377\0(=\377\0/P\377\0@O\377\0RU\377\0T" + "T\377\0QX\377\0[S\377\0mV\377\0\200m\377\0\224{\377\0\227\201\377\0\216" + "y\377\0n[\377\0U^\377\0S]\377\0MY\377\0Dh\377\0Ft\377\0Ck\377\0F_\377" + "\0Er\377\0Cr\377\0Js\377\0Oo\377\0Nh\377\0`e\377\0Ua\377\0Oe\377\0Po" + "\377\0Pq\377\0V|\377\0F\177\377\0Wm\377\0L{\377\0>x\377\0:\202\377$Q" + "\255\377\11/\213\377\0\17v\377\0\37k\377`\11G\377C\0!\377\35\0\17\377" + "\37\0\2\377'\5\"\377%\12$\377!\0\25\377\34\0\30\377\30\1\22\377\31\0" + "\33\377\11\12\22\377\13\25\23\377\13\4\16\377\15\6\26\377\16\26$\377" + "\6\32+\377\0\24\35\377\0\40%\377\7\31*\377\7\33%\377\16\35*\377\7\37" + "\37\377\21\25""4\377\20+*\377\0#+\377\16\"#\377\0&*\377\0\34!\377\0""3" + "(\377\0""5&\377\0,3\377\0""56\377\0%\37\377\0*+\377\0+%\377\0)'\377\0" + "7\"\377\0""6+\377\0>-\377\0""3'\377\0:+\377\0@'\377\0`.\377\0\\4\377" + "\0_*\377\4g$\377\5t0\377\16o;\377\5h,\377\10k-\377\7n@\377\15p6\377F" + "\0c(\377\3j)\377\0U+\377\0H#\377\0R+\377\0@$\377\0>&\377\3""02\377\2" + "/(\377\11,&\377\0-\37\377\4""01\377\6""5%\377\6-/\377\4""3*\377\0!*\377" + "\0\15""0\377\1\40$\377\2!'\377\0\40$\377\4%\40\377\0.\40\377\0""6\31" + "\377\0A\30\377\0N\27\377\0M\35\377\0M\23\377\0B\35\377\0M'\377\0A\27" + "\377\0@\24\377\0B\20\377\0C\13\377\0O\3\377\0J\4\377\0K\16\377\0I\14" + "\377\0S\7\377\0A\7\377\0""9\11\377\0B\12\377\0""4\4\377\0'\15\377\0+" + "\21\377\0""5\15\377\0:\5\377\0""6\0\377\0J\10\377\0G\6\377\0G\11\377" + "\0\77\7\377\0X\0\377\0V\0\377\0G\0\377\0I\0\377\0C\0\377\0;\0\377\0G" + "\0\377\0O\0\377\0f\0\377\0^\0\377\0Z\0\377\0]\0\377\0P\0\377\0L\0\377" + "\0B\0\377\0>\0\377\0""1\0\377\0.\0\377\0""5\0\377\202\0""4\0\377-\0+" + "\0\377\0>\0\377\0""8\0\377\0""5\0\377\0B\0\377\0E\0\377\0>\0\377\0@\0" + "\377\0=\0\377\0L\0\377\0C\0\377\0F\0\377\0;\0\377\0\77\0\377\0B\0\377" + "\0""5\0\377\0<\0\377\0""9\0\377\0H\0\377\0\77\0\377\0=\0\377\0/\0\377" + "\0""6\0\377\0""1\0\377\0""8\0\377\0""6\1\377\0,\0\377\0/\0\377\0(\0\377" + "\0\33\0\377\0'\0\377\0\40\0\377\0\34\0\377\0\36\0\377\0\12\0\377\0\24" + "\0\377\0\26\0\377\0\32\0\377\0\17\0\377\0\20\0\377\0\22\0\377\0\15\0" + "\377\0\14\0\377\0\22\0\377\0\6\0\377\213\0\0\0\377\177\2\4\6\377\5\7" + "\13\377\10\17\25\377\13\26\36\377\20\36,\377\13$7\377\20,8\377\15""0" + "#\377\7/\35\377\5=\12\377\4/\27\377\3""4\14\377\2(\22\377\0-\11\377\0" + "8\4\377\0=\10\377\0H\15\377\0""6\14\377\0<\12\377\0\77\14\377\0V\10\377" + "\0T\30\377\0y\27\377\0u\32\377\0\210\31\377\0\205\25\377\0\221\25\377" + "\0\2153\377\0\2201\377\0\202)\377\0z/\377\0b4\377\0R%\377\0T)\377\0>" + "&\377\0""51\377\0*6\377\0\35-\377\0\35""0\377\0\36<\377\0!6\377\0'4\377" + "\0,9\377\0""43\377\0@D\377\0KD\377\0CU\377\0VW\377\0OQ\377\0qg\377\0" + "\201]\377\0\217v\377\0\210q\377\0\203g\377\0rV\377\0jM\377\0Ke\377\0" + "Ge\377\0>k\377\0Ek\377\0Df\377\0\0\377\0""5\0\377\0D\0\377\0G\0\377\0E\0\377" + "\0A\0\377\0:\0\377\202\0>\0\377\1\0<\0\377\202\0C\0\377\36\0<\0\377\0" + "B\0\377\0""4\0\377\0.\0\377\0<\0\377\0""0\0\377\0;\0\377\0=\0\377\0""2" + "\0\377\0""1\0\377\0,\0\377\0F\0\377\0""5\0\377\0.\0\377\0!\0\377\0\"" + "\0\377\0\30\0\377\0\25\0\377\0&\0\377\0$\0\377\0\27\0\377\0\26\0\377" + "\0\36\0\377\0$\0\377\0\22\0\377\0\12\0\377\0\27\0\377\0\7\0\377\0\3\12" + "\377\0\0\2\377\205\0\0\0\377z\6\23\34\377\20%/\377\11\30\37\377\7\26" + "\27\377\12(0\377\14A4\377\15""1B\377\13""8=\377\12""83\377\7,\33\377" + "\10""3\22\377\6>\30\377\4<\37\377\5;\37\377\5@\32\377\4@\26\377\2>\4" + "\377\0M\0\377\0A\17\377\0E\20\377\0<\16\377\0C\14\377\0@\16\377\0S\10" + "\377\0U\12\377\0^\32\377\0b\24\377\0u\26\377\0\206\25\377\0|\30\377\0" + "\206\34\377\0\211\33\377\0\203!\377\0m\40\377\0_\25\377\0F\21\377\0H" + "'\377\0D\37\377\0A,\377\0/-\377\0-*\377\0)-\377\0%'\377\0\40/\377\0&" + ",\377\0+-\377\0:\77\377\0;I\377\0\77G\377\0DB\377\0HB\377\0SR\377\0r" + "K\377\0tb\377\0\200o\377\0\201g\377\0\200b\377\0gc\377\0ii\377\0k[\377" + "\0Ul\377\0Jb\377\0F]\377\0""9e\377\0""5a\377\0""7g\377\0Cv\377\0""0v" + "\377\0Dg\377\0@j\377\0Qo\377\0\77u\377\0R\204\377\0K\204\377\0R\203\377" + "\0Xl\377\0[j\377\0Oi\377\0Gd\377\0Mw\377\0Ch\377\27V\242\377\0""6z\377" + "\0\35q\377\0\35|\377\\\16""3\3774\0\34\377\34\0\5\377\25\0\4\377\21\34" + "\24\377\25\40\20\377\23&%\377\13\31\40\377\25\13\37\377\26\25!\377\26" + "\23\33\377\23\23\31\377\20\23&\377\22\27\35\377\15\26\22\377\5\20#\377" + "\0\32\25\377\15$!\377\4\23*\377\0\27""4\377\5'1\377\10\37A\377\5\34*" + "\377\5#,\377\16""0.\377\12""2.\377\0-+\377\0.5\377\0""5+\377\0,+\377" + "\0.@\377\0)5\377\0+(\377\0*(\377\0+1\377\0+%\377\0-\37\377\202\0\0\377\0" + "B\0\377\0\77\0\377\0J\0\377\0R\0\377\0Q\0\377\0G\0\377\0O\0\377\0L\0" + "\377\0M\0\377\0=\0\377\0\77\0\377\0;\0\377\0""7\0\377\0@\0\377\0""5\0" + "\377\0+\0\377\0""8\0\377\0""2\0\377\0=\0\377\0G\0\377\0F\0\377\0;\0\377" + "\0P\0\377\0O\0\377\0G\0\377\0S\0\377\0W\0\377\0=\0\377\0@\0\377\202\0" + "<\0\377\13\0;\0\377\0<\0\377\0""7\0\377\0""9\0\377\0""5\0\377\0""6\0" + "\377\0:\0\377\0C\0\377\0I\0\377\0D\0\377\0""0\0\377\202\0(\0\377\177" + "\0\35\0\377\0\27\0\377\0#\0\377\0\27\0\377\0\"\0\377\0\37\0\377\0\24" + "\0\377\0\32\0\377\0\34\0\377\0\24\0\377\0\22\0\377\0\33\0\377\0\35\0" + "\377\0\30\0\377\0\24\0\377\0\32\0\377\0\4\0\377\0\10\0\377\0\4\0\377" + "\3\27\23\377\2%\15\377\10\"\35\377\6$\33\377\11-!\377\13@6\377\11""8" + "<\377\13C1\377\15:)\377\10""5,\377\10""4$\377\10/\40\377\6\77\24\377" + "\4H\21\377\7F\16\377\4A\11\377\3C\6\377\3@\5\377\3H\5\377\0L\30\377\0" + "G\27\377\0C\21\377\0E\26\377\0Q\0\377\0Z\26\377\0T\21\377\0S\25\377\0" + "_\26\377\0r\30\377\0m\34\377\0w\17\377\0x\35\377\0\177(\377\0^#\377\0" + "c\22\377\0U\17\377\0<\15\377\0B\34\377\0""3\30\377\0""8$\377\0""5\34" + "\377\0($\377\0.+\377\0()\377\0&3\377\0\37""6\377\0+.\377\0\40""5\377" + "\0""40\377\0@:\377\0>5\377\0A<\377\0FK\377\0fM\377\0mY\377\0s_\377\0" + "o_\377\0kW\377\0mS\377\0aZ\377\0ZX\377\0H_\377\0I_\377\0CT\377\0>c\377" + "\0""5[\377\0\77f\377\0""6n\377\0(q\377\0-j\377\0""5y\377\0C|\377\0:{" + "\377\0E{\377\0V\201\377\0_\203\377\0^g\377\0Ym\377\0Xi\377\0Zc\377\0" + "U`\377\0Gd\377\32R\227\377\0""9\202\377\0)g\377\0.s\377Q\15+\377.\0\31" + "\377\15\0\0\377\21\0\4\377\36.\16\377\31'\6\377\36\31\16\377\35\40\23" + "\377\35\22\25\377\20\15\27\377\34\25'\377\36\16\30\377\13\6\34\377\2" + "\40\"\377\17\16\13\377\0\16\27\377\0\16\36\377\15\36)\377\10\33(\377" + "\4\33""1\377\3\35""3\377\10\25""3\377\20\7#6\377\21\30-\377\0#\40\377" + "\6#%\377\0!$\377\0$*\377\0%1\377\0'1\377\0""3'\377\0""30\377\0*.\377" + "\0(\37\377\0""4!\377\2*\32\377\0""0&\377\0/\"\377\202\0""8,\377\36\0" + "B\35\377\0A'\377\4V\35\377\0V#\377\0R\"\377\2M\35\377\26f(\377\35g(\377" + "\10X'\377\5P&\377\26X\32\377\13b\"\377\15T!\377\22'(\377\21\4\24\377" + "\7\0\25\377\0\0\22\377\0\0\23\377\0\0\16\377\0\0\11\377\5\0\23\377\3" + "\0\27\377\0\0\24\377\0\0\27\377\0\0\24\377\0\0\26\377\0\0\32\377\0\0" + "\40\377\0\0#\377\0\0\37\377\202\0\0\27\377\27\0\0\25\377\0\0\7\377\0" + "\0\15\377\0\0\34\377\0\0\26\377\0\0\24\377\0\10\26\377\0G\20\377\0F\24" + "\377\0R\40\377\0M\31\377\0\\\27\377\0P!\377\0I\24\377\0\16\2\377\0\0" + "\0\377\0\0\11\377\0\0\7\377\0\0\0\377\0\0\12\377\0\0\3\377\0\0\7\377" + "\0\0\2\377\216\0\0\0\377\177\0\22\0\377\0K\0\377\0C\0\377\0=\0\377\0" + "I\0\377\0F\0\377\0H\0\377\0R\0\377\0J\0\377\0U\0\377\0K\0\377\0N\0\377" + "\0Q\0\377\0Z\0\377\0N\0\377\0@\0\377\0<\0\377\0""8\0\377\0""4\0\377\0" + ";\0\377\0""9\0\377\0""5\0\377\0>\0\377\0\16\0\377\0\0\0\377\0\4\0\377" + "\0\1\0\377\0\16\0\377\0\5\0\377\0\0\0\377\0\37\0\377\0G\0\377\0J\0\377" + "\0M\0\377\0O\0\377\0I\0\377\0L\0\377\0\77\0\377\0""8\0\377\0=\0\377\0" + "9\0\377\0\77\0\377\0""7\0\377\0N\0\377\0D\0\377\0""0\0\377\0<\0\377\0" + "\37\0\377\0\36\0\377\0$\0\377\0\40\0\377\0\27\0\377\0\"\0\377\0\23\0" + "\377\0\25\0\377\0%\0\377\0\27\0\377\0\36\0\377\0\27\0\377\0&\0\377\0" + "\36\0\377\0\32\0\377\0\31\0\377\0\30\0\377\0\26\0\377\0\22\0\377\0\27" + "\11\377\20;2\377\5(\27\377\10\"\33\377\4""8\40\377\7""4\40\377\7H4\377" + "\16I/\377\7E+\377\12""8\"\377\10""5#\377\5""9\37\377\6A\"\377\5\77\23" + "\377\5L\33\377\3H\25\377\3G\16\377\3@\20\377\2J\4\377\1L\7\377\1T\40" + "\377\0Y\21\377\0P\22\377\0X\16\377\0e\33\377\0Z!\377\0[\31\377\0b\23" + "\377\0W\31\377\0Y\16\377\0Z\33\377\0k\27\377\0_\27\377\0f%\377\0^#\377" + "\0f)\377\0N$\377\0""9\37\377\0""5(\377\0(\"\377\0(!\377\0)\35\377\0'" + "#\377\0\31'\377\0#6\377\0&4\377\0\40.\377\0%,\377\0""3#\377\0""71\377" + "\0""57\377\0<4\377\0V:\377\0PC\377\0^S\377\0sI\377\0vU\377\0\213T\377" + "\0oS\377\0gO\377\0aP\377\177\0UJ\377\0QW\377\0Cg\377\0;\\\377\0:i\377" + "\0""5j\377\0+n\377\0,x\377\0)o\377\0""6u\377\0At\377\0@t\377\0Nz\377" + "\0Qq\377\0Z{\377\0^\205\377\0[o\377\0_|\377\0^t\377\0[p\377\0Vl\377\0" + "Zi\377\22h\234\377\0C\216\377\0\40q\377\0$q\377Y\0,\3778\0\33\377\25" + "\0\6\377\26\0\0\377%*\16\377$'\4\377\23)\4\377\30\33\23\377!\16\33\377" + "\31\36\34\377\25\37\11\377\25\23\20\377\5\37\17\377\17\16\15\377\5\15" + "\25\377\0\10\25\377\7\20\37\377\4\16*\377\0\26&\377\0\26\"\377\11\27" + ")\377\6\23(\377\11\30&\377\5$!\377\14#\40\377\11\36\33\377\16\"(\377" + "\0#.\377\3\"\34\377\0-0\377\2/,\377\0\34*\377\0\33\34\377\0#'\377\0$" + "\24\377\0(\"\377\0.$\377\0""1)\377\0""4\"\377\0""4)\377\0C\30\377\0F" + "-\377\0J(\377\2K&\377\4Q\37\377\11K)\377\27V#\377\22[\17\377\16K'\377" + "\15P&\377\12S\26\377\0\"\35\377\0\0\16\3774:g\377Nl\271\377N\213\336" + "\377\\\214\327\377X\231\334\377Q\213\334\377T\220\327\377\\\216\345\377" + "D\215\342\377U\203\342\377S\216\347\377X\222\327\377N\200\346\377b\207" + "\357\377L\202\355\377O\220\347\377H\211\327\377T\212\324\377N\224\340" + "\377G\205\347\377;\211\331\377H\216\332\3779\212\337\3777\222\345\377" + ";\223\325\377\0\0\"\377\0C\36\377\0J\34\377\0M\23\377\0Z\16\377\0O\34" + "\377\0\33\22\377\0\11%\377\12a\204\377\33\237\300\377\32\244\313\377" + "\24\231\330\377\34\241\324\377\31\234\323\377\12\236\305\377\25\227\311" + "\377\23\245\306\377\25\232\313\377\21\241\322\377\21\247\304\377\5\246" + "\307\377\22\230\316\377\10\246\305\377\21\15\237\276\377\26\243\271\377" + "\14\243\267\377\16\245\275\377\20\234\263\377\27\235\262\377\16\235\254" + "\377\13\253\275\377\0\5\0\377\0J\0\377\0O\0\377\0Q\0\377\0J\0\377\0E" + "\0\377\0O\0\377\0K\0\377\0F\0\377\202\0O\0\377\7\0V\0\377\0O\0\377\0" + "Z\0\377\0J\0\377\0P\0\377\0D\0\377\0K\0\377\202\0:\0\377\26\0\77\0\377" + "\0""8\0\377\0A\0\377\0\26\0\377\0\260\250\377\2\262\252\377\0\303\260" + "\377\0\277\232\377\0\272\241\377\0\261\234\377\0L:\377\0-\0\377\0G\0" + "\377\0I\0\377\0H\0\377\0F\0\377\0C\0\377\0H\0\377\0:\0\377\0H\0\377\0" + "C\0\377\0D\0\377\202\0H\0\377\2\0F\0\377\0""7\0\377\202\0,\0\377\4\0" + "(\0\377\0%\0\377\0#\0\377\0%\0\377\202\0\33\0\377\3\0)\0\377\0/\0\377" + "\0\40\0\377\202\0)\0\377\202\0'\0\377\177\0\30\0\377\0\33\0\377\0$\0" + "\377\0\37\0\377\0\23\0\377\5(%\377\5.)\377\4""5\40\377\4,\33\377\6""8" + "\34\377\11L$\377\11M\36\377\5J\34\377\6A$\377\5G\25\377\5I\24\377\2L" + "\21\377\5J\23\377\3G\34\377\5X\33\377\4T\36\377\5A\31\377\4T\34\377\3" + "O\26\377\2R\31\377\1L\2\377\0X\7\377\0V\11\377\0Y\27\377\0^\31\377\0" + "W\31\377\0S\40\377\0V!\377\0Y\34\377\0V\20\377\0M\27\377\0V\31\377\0" + "O\32\377\0V!\377\0M\24\377\0X\25\377\0K$\377\0D\25\377\0B\32\377\0""1" + "\35\377\0$\13\377\0\31$\377\0!\26\377\0\33#\377\0""5*\377\0""2.\377\0" + "-8\377\0&%\377\0)4\377\0""74\377\0""9:\377\0L;\377\0OA\377\0VF\377\0" + "oQ\377\0}W\377\0\211^\377\0{[\377\0r^\377\0rb\377\0nE\377\0QU\377\0M" + "S\377\0Ma\377\0Ie\377\0Gd\377\0Cf\377\0" + "\31\377\4U\24\377\3S\22\377\22Y\33\377\2W\37\377\12O\30\377\12P\33\377" + "\2L\21\377\0\0\12\377En\261\377a\222\320\377E\215\340\377J\213\315\377" + "F\217\330\377S\204\314\377Q\202\333\377F\203\327\377A\201\344\377E\207" + "\337\377M\203\322\377D\210\323\377O\177\323\377Rw\345\377Q}\335\377K" + "\215\347\377V\212\317\377N\201\307\377F\220\326\377E\210\341\377C\222" + "\327\377;\220\323\377D\213\341\377F\223\323\377:\213\321\377A\213\320" + "\377\0\20\77\377\0""4\4\377\0N\26\377\0G\22\377\0R\22\377\0\31\21\377" + "\0\17O\377*\226\322\377.\220\332\377\27\226\326\377\14\232\330\377\32" + "\223\327\377\33\222\320\377\24\233\304\377\27\216\305\377\36\232\310" + "\377\30\244\305\377\17\234\310\377\40\264\311\377\40\235\306\377\16\233" + "\304\377!\230\275\377\"\233\271\377\26\253\273\377\31\244\301\377\31" + "\241\271\377\31\253\257\377\16\251\254\377\31\242\264\377\23\242\264" + "\377\5\253\261\377\0)\36\377\0""7\0\377\0M\0\377\0P\0\377\0T\0\377\0" + "P\0\377\0D\0\377\0B\0\377\0L\0\377\0I\0\377\0R\0\377\0H\0\377\0I\0\377" + "\0@\0\377\0H\0\377\0A\0\377\0""9\0\377\0B\0\377\0""7\0\377\0:\0\377\0" + ";\0\377\0A\0\377\0<\0\377\0\22\0\377\0\212\202\377\0\267\243\377\0\264" + "\246\377\0\255\241\377\0\270\245\377\0\245\241\377\0H>\377\0\35\0\377" + "\0;\0\377\0=\0\377\0M\0\377\0J\0\377\0U\0\377\0R\0\377\0S\0\377\0D\0" + "\377\0M\0\377\0@\0\377\0E\0\377\0A\0\377\0\77\0\377\0""9\0\377\0-\0\377" + "\0:\0\377\202\0,\0\377\177\0$\0\377\0\34\0\377\0\25\0\377\0)\0\377\0" + "\34\0\377\0$\0\377\0)\0\377\0""3\0\377\0""9\0\377\0""5\0\377\0!\0\377" + "\0\33\0\377\0\36\0\377\0\15\0\377\0\25\0\377\1\33\11\377\1\36\11\377" + "\2,\27\377\2""1\34\377\3=\36\377\5G$\377\5D\36\377\3P\33\377\2D\27\377" + "\3I\14\377\1J\12\377\3N\14\377\3P\21\377\2I\31\377\4Y)\377\5S\35\377" + "\5M\37\377\4P\36\377\4M\34\377\4U\30\377\2I\13\377\1K\11\377\0G\6\377" + "\0P\16\377\0^\6\377\0Z\27\377\0Y\25\377\0R\35\377\0X\25\377\0X\16\377" + "\0P\25\377\0J\25\377\0N\14\377\0C\35\377\0@\24\377\0E(\377\0J\31\377" + "\0F\35\377\0K\31\377\0B\32\377\0.\22\377\0.\17\377\0'\27\377\0\34\25" + "\377\0-+\377\0\77&\377\0D&\377\0""7,\377\0""35\377\0""1:\377\0>5\377" + "\0""9@\377\0L6\377\0[@\377\0gB\377\0fT\377\0~S\377\0\215Z\377\0\207`" + "\377\0{Y\377\0tS\377\0m;\377\0[N\377\0ZQ\377\0A_\377\0B_\377\0>^\377" + "\0Ll\377\0Ee\377\0Gs\377\0Ej\377\0Hp\377\0Gj\377\0K^\377\0Se\377\0Gd" + "\377\0Ou\377\0Oi\377\0Ta\377\0Iw\377\0Mj\377\0bl\377\0Tf\377\0Vl\377" + "\14d\245\377\0R\217\377\0>{\377\0>\177\377R\0B\3778\0\35\377\37\0\3\377" + "\17\0\0\377\33\33\2\377\32\"\12\377$\32\15\377)\25\10\377\37\12\14\377" + "\30\24\34\377\26\23\14\377\21\34\15\377\17\21\2\377\17\16\4\377\7\15" + "\13\377\15\26\17\377\0\15\12\377\2\16\13\377\14\33\0\377\11\7\13\377" + "\4\13\10\377\7\34\10\377\6\22\13\377\13\25\5\377~\16\27\6\377\22\35\20" + "\377\11\22\12\377\21\22\26\377\4\12\22\377\17\33\24\377\0\40\24\377\1" + "-\10\377\5\"\36\377\1""3\25\377\4/\30\377\0""6\25\377\0\37\25\377\0\"" + "\31\377\0""4\37\377\0""2\35\377\0=\17\377\0""3!\377\0B\22\377\0P\20\377" + "\0C\5\377\2D\14\377\4P\14\377\1>\6\377\0B\10\377\0J\0\377\0\30\3\377" + "\3\24O\377F\201\313\377\77}\312\377Dv\323\377Mu\306\3777\205\320\377" + "2x\320\377\"^\253\377\13-f\377\22\27j\377\21\16m\377\11\40o\377\6\34" + "o\377\21\26p\377\14\30n\377\14\34n\377\13\30`\377\0#B\377\13(]\377\14" + "!Z\377\6-[\377\0)e\377\0+Y\377\0>c\377\13""0a\377\5Li\377,n\236\377\13" + "3X\377\0\"\0\377\0M\0\377\0Q\14\377\0O\0\377\0\0\1\377\37r\260\377'\177" + "\301\377\31\206\307\377!\205\302\377#\214\312\377\34\222\304\377\21~" + "\265\377\0>S\377\0AH\377\0EQ\377\0CI\377\0JJ\377\0FY\377\0HI\377\0>@" + "\377\0EJ\377\0QV\377\0KI\377\0H;\377\0M7\377\0L+\377\0""99\377\0C<\377" + "\0WC\377\0{q\377\0SA\377\0)\0\377\0M\0\377\0D\0\377\0O\0\377\0>\0\377" + "\0""8\0\377\0""3\0\377\0""8\0\377\0C\0\377\0>\0\377\0""6\0\377\0>\0\377" + "\0\77\0\377\0""3\0\377\0&\0\377\0""7\0\377\0C\0\377\0K\0\377\0;\0\377" + "\0""9\0\377\0""6\0\377\0""7\0\377\0\0\0\377\0vv\377\0\240\250\377\0\230" + "\230\377\0\237\257\377\0\234\253\377\0\250\261\377\0OD\377\0&\0\377\0" + "L\0\377\0""8\0\377\0:\0\377\0L\0\377\0`\0\377\0T\0\377\0W\0\377\0J\0" + "\377\0H\0\377\202\0\77\0\377\3\0=\0\377\0*\0\377\0'\0\377\202\0*\0\377" + "\6\0,\0\377\0\35\0\377\0\24\0\377\0\31\0\377\0#\0\377\0\23\0\377\202" + "\0(\0\377\177\0#\0\377\0)\0\377\0""1\0\377\0&\0\377\0,\0\377\0\26\0\377" + "\0\30\0\377\0\24\0\377\0\15\0\377\3&\33\377\6)\"\377\3,\32\377\6/\35" + "\377\5<%\377\2H\"\377\4D#\377\3P\24\377\0S\7\377\1S\3\377\1O\4\377\1" + "W\5\377\1T\7\377\3]\21\377\3L\33\377\4K\26\377\4K\14\377\3E\22\377\4" + "D\25\377\2O\13\377\1=\7\377\1J\5\377\0H\16\377\0Q\6\377\0\\\13\377\0" + "[\14\377\0W\12\377\0W\11\377\0_\14\377\0X\15\377\0O\11\377\0L\23\377" + "\0""9\17\377\0=\27\377\0C\20\377\0:\27\377\0;(\377\0=\24\377\0H\30\377" + "\0""2\27\377\0""1\26\377\0""1\30\377\0(\20\377\0""9\32\377\0-\40\377" + "\0@&\377\0>\37\377\0H/\377\0=(\377\0@-\377\0V/\377\0P=\377\0_4\377\0" + "h9\377\0vA\377\0wD\377\0\213E\377\0\207L\377\0{O\377\0cE\377\0aJ\377" + "\0XD\377\0DT\377\0KM\377\0DQ\377\0\77\\\377\0NZ\377\0Ak\377\0Sf\377\0" + "Kj\377\0KZ\377\0Hf\377\0Sd\377\0G]\377\0E\\\377\0Jd\377\0L^\377\0BV\377" + "\0@S\377\0\77U\377\0R]\377\0_Z\377\0sh\377\0xd\377\23n\240\377\0U\215" + "\377\0Ds\377\0Ap\377W\0(\3770\0\11\377\31\0\0\377\13\0\0\377#\37\4\377" + ".#\0\377\"\22\4\377#\20\0\377\36\36\0\377\"\31\0\377\36\32\0\377\14\24" + "\0\377\14\35\0\377\15\30\2\377\6\21\6\377\1\25\0\377\15\30\1\377\16\33" + "\0\377\7\17\0\377\7\22\0\377\13\25\0\377\7\27\0\377\10\15\0\377\15\4" + "\3\377\20\13\0\377\13\31\0\377\7\32\2\377\2\25\4\377\7\31\1\377\3\35" + "\0\377#\7%\1\377\10\33\13\377\23-\4\377\13""6\23\377\7;\11\377\0""4\2" + "\377\0""1\13\377\0""4\7\377\0,\0\377\0<\0\377\0\77\4\377\0G\22\377\0" + "F\24\377\0E\6\377\0<\4\377\0@\15\377\0;\6\377\0;\1\377\0;\0\377\0C\0" + "\377\0\0\0\377,C\204\377I\200\303\377Mu\275\377D{\305\377=x\270\377F" + "u\306\377\77u\275\377\0\0\16\377\0\4\0\377\0\0\0\377\0\0\5\377\0\0\6" + "\377\0\0\22\377\0\0\20\377\204\0\0\0\377\177\0\2\0\377\0\5\0\377\0\4" + "\0\377\0\0\0\377\0\11\0\377\0\27\0\377\0\34\2\377\0\30\3\377\0\10\3\377" + "\0*\0\377\0@\15\377\0U\14\377\0I\3\377\0""7\0\377\0\0\17\377\"\212\257" + "\3771|\276\377+\200\270\377+\212\273\377\40\204\273\377\30\222\263\377" + "\0DU\377\0\21\0\377\0\35\0\377\0%\0\377\0\31\0\377\0\27\0\377\0\30\0" + "\377\0\23\0\377\0!\0\377\0\35\0\377\0%\0\377\0-\0\377\0\37\0\377\0\36" + "\0\377\0\33\0\377\0\27\0\377\0\32\0\377\0\20\0\377\0\12\0\377\0$\0\377" + "\0+\0\377\0@\0\377\0B\0\377\0A\0\377\0;\0\377\0""5\0\377\0""1\0\377\0" + "9\0\377\0)\0\377\0""1\0\377\0""0\0\377\0-\0\377\0+\0\377\0""0\0\377\0" + "*\0\377\0=\0\377\0""0\0\377\0B\0\377\0)\0\377\0""8\0\377\0""9\0\377\0" + "$\0\377\0\0\0\377\0io\377\0\214\251\377\0\226\270\377\0\232\253\377\0" + "\223\266\377\0\233\247\377\0HB\377\0$\0\377\0<\0\377\0R\0\377\0I\0\377" + "\0X\0\377\0`\0\377\0W\0\377\0I\0\377\0A\0\377\0E\0\377\0F\0\377\0E\0" + "\377\0""9\0\377\0.\0\377\0&\0\377\0,\0\377\0&\0\377\0\37\0\377\0\27\0" + "\377\0\32\0\377\0\40\0\377\0\36\0\377\0\40\0\377\0)\0\377\0\35\0\377" + "\0(\0\377\0;\0\377\0""6\0\377\0""5\0\377\0$\0\377\0\30\0\377\0\26\0\377" + "\0\17\0\377\0\31\0\377\5""2)\377\5""7)\377\15""4-\377\12""5-\377\11*" + "&\377\7/!\377\7@\31\377\5L\20\377\0\\\2\377\0Y\0\377\0U\1\377\0^\1\377" + "\1Y\3\377\1`\14\377\2R\7\377\3B\10\377\2D\16\377\1;\11\377\2:\16\377" + "\2=\13\377\2\77\11\377\1\\\5\377m\0i\16\377\0b\0\377\0X\0\377\0d\6\377" + "\0Z\17\377\0]\6\377\0]\2\377\0[\0\377\0I\7\377\0F\10\377\0;\7\377\0F" + "\25\377\0E\27\377\0F\25\377\0<\30\377\0G\12\377\0H\6\377\0;\17\377\0" + "6\30\377\0""6\20\377\0""1\21\377\0""8\33\377\0/\40\377\0F\21\377\0""9" + "\33\377\0@\34\377\0""9&\377\0F.\377\0O,\377\0\\/\377\0e5\377\0r;\377" + "\0x=\377\0y5\377\0\177C\377\0r2\377\0e4\377\0];\377\0_\77\377\0Y8\377" + "\0UC\377\0@A\377\0""9R\377\0IW\377\0:X\377\0\77a\377\0Dl\377\0Of\377" + "\0RZ\377\0PQ\377\0IM\377\0BC\377\0@M\377\0DL\377\0KF\377\0FL\377\0XP" + "\377\0PD\377\0SR\377\0dF\377\0wT\377\0oT\377\6p\240\377\0V|\377\0""4" + "g\377\0=^\377b\0""1\377@\0\10\377\24\0\0\377\27\0\0\377%%\0\377,\35\0" + "\377&%\0\3770\30\0\377\40\16\0\377/\20\0\377#\31\0\377\22\27\0\377\5" + "\20\0\377\30\24\0\377\26\17\0\377\16\17\0\377\14\21\0\377\12\31\0\377" + "\27\25\0\377\15\21\0\377\33\5\0\377\24\16\0\377\20\10\2\377\32\27\0\377" + "\16\23\0\377\30\22\1\377\32\20\4\377\21\36\0\377\20\24\3\377\24\30\3" + "\377\24\33\11\377\22\23\2\377\17%\0\377\7.\3\377\3/\12\377\0-\10\377" + "\0%\4\377\0""2\3\377\0""9\0\377\0""4\0\377\4G\13\377\0F\15\377\0L\0\377" + "\202\0<\0\377\202\0I\0\3770\0=\0\377\0A\0\377\0D\0\377\0\0\0\377,F\214" + "\377>q\274\377D\200\301\377Dz\257\377Hx\270\377>r\300\377;l\302\377\0" + "\0\0\377\0%\0\377\0\27\14\377\0\30\12\377\0\22\4\377\0\36\15\377\0\34" + "\0\377\0\36\0\377\0\26\0\377\0&\3\377\0#\0\377\0&\0\377\0#\0\377\0'\0" + "\377\0$\0\377\0/\0\377\0""2\0\377\0""8\0\377\0:\0\377\0H\0\377\0I\0\377" + "\0H\0\377\0L\4\377\0J\0\377\0""0\0\377\0\25\24\377&\205\256\377$\201" + "\266\377.\205\257\377\34\204\272\377\33\201\266\377\21\220\270\377\0" + "\0\377\202\0""2\0\377\2\0""9\0\377\0<\0\377\202\0.\0\377-\0""3\0\377" + "\0,\0\377\0""7\0\377\0-\0\377\0""4\0\377\0""0\0\377\0&\0\377\0""0\0\377" + "\0""6\0\377\0'\0\377\0""9\0\377\0.\0\377\0*\0\377\0\0\0\377\0Sd\377\0" + "\203\270\377\0\222\253\377\0\215\255\377\0\213\241\377\0\216\247\377" + "\0L;\377\0*\0\377\0<\0\377\0>\0\377\0I\0\377\0W\0\377\0V\0\377\0D\0\377" + "\0E\0\377\0D\0\377\0E\0\377\0>\0\377\0H\0\377\0""6\0\377\0""0\0\377\0" + ")\0\377\0#\0\377\0\26\0\377\0\40\0\377\0%\0\377\0-\0\377\0&\0\377\0'" + "\0\377\0\36\0\377\0$\0\377\202\0*\0\377\40\0I\0\377\0M\0\377\0""8\0\377" + "\0\40\0\377\0\33\0\377\0\25\0\377\0\20\0\377\0\24\0\377\14""5+\377\15" + ")'\377\14\"!\377\13\37\37\377\17\37\35\377\13-\33\377\7<\23\377\10S\15" + "\377G\216G\377\225\265\225\377\314\333\314\377\351\360\351\377\371\372" + "\371\377\340\351\340\377\234\274\235\377%`)\377\2E\7\377\3L\10\377\3" + "X\12\377\5P\13\377\4Z\12\377\1_\6\377\0h\1\377\0f\0\377\202\0b\0\377" + "@\0i\1\377\0o\10\377\0p\3\377\0X\16\377\2O\5\377:p>\377\216\260\221\377" + "\304\323\305\377\336\345\340\377\370\371\370\377\360\364\360\377\323" + "\336\324\377\201\240\204\377\11B\34\377\0""2\"\377\0""1\23\377\0""2\15" + "\377\0A%\377\0B'\377\0D*\377\0;#\377\0@!\377\0A\40\377\0G'\377\0Y\35" + "\377\0b)\377\1\207+\377T\263z\377\306\345\324\377\352\366\357\377\372" + "\374\373\377l\253\215\377\0b&\377\0P-\377\0S0\377\0R9\377\0IO\377\0>" + "G\377\0:T\377\0/M\377\0=O\377\0Ma\377\0GU\377\0NV\377\0SW\377\0HV\377" + "\0O[\377\0AF\377\0O;\377\0NM\377\0UK\377\0YB\377\0YM\377\0^I\377\0bJ" + "\377\0hH\377\0oX\377\0gW\377\7y\203\377\0gk\377\0CP\377\0BB\377^\0/\377" + ":\0\16\377\202\31\0\0\377\10#-\5\377)+\0\377*\40\0\377&\34\0\377#\30" + "\0\377\33\31\0\377#\32\0\377\35\26\0\377\202\34\20\0\377.\22\7\0\377" + "\22\11\0\377\22\17\0\377\25\23\0\377\31\12\0\377\34\26\0\377\21\11\0" + "\377!\17\0\377\16\34\2\377#\23\0\377$\25\0\377\40\20\0\377\31\33\10\377" + "\30\11\3\377\23\11\15\377\34\14\13\377\23\31\0\377\26\32\4\377\13\22" + "\4\377\13\"\14\377\6\36\14\377\10!\11\377\6(\10\377\21(\2\377\13(\0\377" + "\3""6\0\377\2K\0\377\0G\0\377\0E\0\377\0C\0\377\0H\0\377\0\77\0\377\0" + "B\0\377\0""5\0\377\0\77\0\377\0>\0\377\0\0\0\377-;\204\377Cj\275\377" + ";v\261\3778l\260\377>t\262\377:m\263\3775d\265\377\0\0\0\377\0\35\0\377" + "\202\0\30\0\377\5\0\32\0\377\0\22\4\377\0\24\0\377\0\32\0\377\0\26\0" + "\377\202\0\36\0\377e\0\25\0\377\0\"\0\377\0""1\0\377\0(\0\377\0,\0\377" + "\0""4\0\377\0""1\0\377\0""7\0\377\0""1\0\377\0C\0\377\0G\0\377\0;\0\377" + "\0G\0\377\0,\0\377\0\4\31\377\36\204\301\377\23\215\272\377!\222\276" + "\377\40\214\262\377\27\215\305\377\37\202\265\377\0""3V\377\0\36\0\377" + "\0L\0\377\0""1\0\377\0""0\0\377\0""2\0\377\0@\0\377\0""6\0\377\0;\0\377" + "\0>\0\377\0""7\0\377\0""6\0\377\0.\0\377\0=\0\377\0E\0\377\0+\0\377\0" + "1\0\377\0-\0\377\0""5\0\377\0""6\0\377\0""2\0\377\0<\0\377\0""7\0\377" + "\0:\0\377\0*\0\377\0+\0\377\0$\0\377\0#\0\377\0\23\0\377\0\32\0\377\0" + "\33\0\377\0\40\0\377\0\23\0\377\0\25\0\377\0\16\0\377\0\32\0\377\0\20" + "\0\377\0\16\0\377\0\15\0\377\0\25\0\377\0\32\0\377\0*\0\377\0\0\0\377" + "\0Bq\377\0\200\260\377\0\200\246\377\0y\247\377\0\214\240\377\0\231\241" + "\377\0""58\377\0\24\0\377\0""3\0\377\0:\0\377\0#\0\377\0(\0\377\0,\0" + "\377\0-\0\377\0""1\0\377\0-\0\377\0&\0\377\0+\0\377\0*\0\377\0""6\0\377" + "\0\77\0\377\0""2\0\377\0!\0\377\0\26\0\377\0\17\0\377\0\35\0\377\0\26" + "\0\377\0\40\0\377\0(\0\377\0\33\0\377\0'\0\377\0\"\0\377\0,\0\377\0/" + "\0\377\0""6\0\377\0;\0\377\0""9\0\377\202\0-\0\377\17\0\30\0\377\0\11" + "\0\377\3'\26\377\10!\34\377\12\40\35\377\14\31\26\377\11)\30\377\31""2" + "\37\377|\235\177\377\345\357\345\377\261\310\261\377\204\246\204\377" + "r\244r\377\227\275\227\377\353\361\353\377\202\377\377\377\377\25\367" + "\371\367\377H\211M\377\3L\12\377\5U\13\377\5X\14\377\5U\12\377\2Y\4\377" + "\0e\1\377\0d\0\377\0Z\0\377\0r\0\377\0n\0\377\0q\3\377\1`\1\377[\241" + "[\377\340\353\340\377\265\317\266\377x\243x\377X\210X\377z\243z\377\265" + "\310\265\377\203\377\377\377\377\16\300\317\303\377\2>\15\377\0<\5\377" + "\0A\0\377\0E\21\377\0H)\377\0""9\37\377\0;\"\377\0""8\34\377\0""8\33" + "\377\0E\17\377\0`\31\377\24\222B\377\300\351\312\377\203\377\377\377" + "\377\17\304\337\316\377\4e\"\377\0c%\377\0U,\377\0]0\377\0W9\377\0[5" + "\377\0QM\377\0\77M\377\0F\77\377\0E9\377\0""6C\377\0>P\377\0FP\377\0" + "EW\377\202\0NL\377O\0[H\377\0VE\377\0SA\377\0H=\377\0S<\377\0\\=\377" + "\0RK\377\0cI\377\0jG\377\0]T\377\0iS\377\13v\205\377\0Mq\377\0""8Y\377" + "\0-Y\377^\0-\377D\0\23\377\26\0\0\377\24\0\0\377\"3\0\3774+\0\377*&\0" + "\3772\30\0\377\"\34\0\377#\22\0\377\32\3\0\377\40\13\0\377\40\5\0\377" + "(\12\0\377\36\14\0\377\30\6\0\377$\17\0\377\31\5\0\377\25\7\0\377\34" + "\15\0\377\22\23\0\377\"\22\2\377\37\25\0\377\"\31\3\377%\24\10\377'\15" + "\0\377\36\12\0\377\40\0\4\377\30\2\0\377'\3\2\377\22\11\4\377\24\12\0" + "\377\22\11\0\377\15\22\17\377\16\6\7\377\25\14\15\377\25\35\0\377\23" + "-\3\377\0""7\0\377\0""4\0\377\0:\0\377\0""5\0\377\0@\0\377\0G\6\377\0" + "F\0\377\0;\0\377\0D\0\377\0H\0\377\0C\0\377\0""7\0\377\0\0\0\377/C\205" + "\3778q\260\377:g\274\3771Z\257\377'_\272\377*_\261\3777^\271\377\0\0" + "\0\377\0\37\0\377\0\17\0\377\0\31\0\377\0\23\0\377\0\35\0\377\202\0\34" + "\0\377\33\0\20\0\377\0#\0\377\0%\0\377\0\36\0\377\0\40\0\377\0\32\0\377" + "\0!\0\377\0\"\0\377\0'\0\377\0""5\0\377\0""1\0\377\0%\6\377\0""3\0\377" + "\0\35\2\377\0\33\0\377\0/\0\377\0\33\0\377\0\27""6\377(\211\321\377*" + "\217\312\377!\220\311\377\22\205\307\377\17\203\305\377\14}\275\377\0" + "1V\377\0\13\0\377\0""7\1\377\202\0""7\0\377\25\0<\0\377\0@\0\377\0""7" + "\0\377\0E\0\377\0A\0\377\0""7\0\377\0B\0\377\0J\0\377\0""9\0\377\0=\0" + "\377\0<\0\377\0)\0\377\0""1\0\377\0""9\0\377\0""2\0\377\0-\0\377\0=\0" + "\377\0""7\0\377\0!\0\377\0\37\0\377\0\25\0\377\202\0\0\0\377\15\0\33" + "\24\377\0\25\11\377\0\35\12\377\0\6\1\377\0\17\16\377\0\27\5\377\0\12" + "\10\377\0\7\12\377\0\16\10\377\0\26\15\377\0\23\23\377\0\10\15\377\0" + "\0\16\377\202\0\0\0\377\30\0Jq\377\0k\262\377\0z\252\377\0t\250\377\0" + "y\234\377\0y\242\377\0\":\377\0\0\0\377\0\34\0\377\0\35\0\377\0\31\0" + "\377\0\32\0\377\0\34\11\377\0\21\0\377\0\35\7\377\0!\0\377\0\33\1\377" + "\0\32\2\377\0\15\0\377\0\23\0\377\0""0\0\377\0""5\0\377\0%\0\377\0\12" + "\0\377\202\0\20\0\377\35\0\31\0\377\0\16\0\377\0\23\0\377\0\11\0\377" + "\0\0\0\377\0\35\0\377\0\32\0\377\0\35\0\377\0\36\4\377\0\3\0\377\0<\0" + "\377\0$\0\377\0\35\0\377\0\33\0\377\0\23\0\377\6\35\17\377\14#\25\377" + "\11\36\22\377\11\21\23\377Uj_\377\353\357\354\377\243\272\246\377\31" + "Z\35\377\0T\1\377\0T\0\377\0R\0\377\0c\1\377\21e\21\377\266\316\266\377" + "\202\377\377\377\377\25\355\363\355\377\33r\40\377\11d\14\377\10g\16" + "\377\10c\13\377\3b\5\377\1Q\1\377\0Z\0\377\0e\0\377\0c\0\377\0j\0\377" + "%\203&\377\306\337\306\377\276\327\276\377'\205'\377\0Z\0\377\0W\0\377" + "\0S\0\377\0V\0\377\0L\0\377U\177U\377\203\377\377\377\377]\25\377\0C\14\377\0B\37\377\0@\23\377\0@\13\377\0-" + "\26\377\0F\14\377'z=\377\334\354\340\377\244\340\257\377,\303N\377\16" + "\277U\377\14\242<\377\5\215&\377\0q\40\377\0h/\377\0P-\377\0R&\377\0" + "R'\377\0Q7\377\0B\77\377\0S8\377\0J=\377\0D=\377\0AN\377\0DR\377\0SU" + "\377\0J@\377\0IG\377\0PB\377\0`7\377\0O6\377\0\\C\377\0X;\377\0N@\377" + "\0JH\377\0PB\377\0SG\377\0hW\377\0OX\377\0Yb\377\15f\236\377\0C\214\377" + "\0\"m\377\0\40f\377Z\0\37\377<\0\4\377$\0\0\377/\0\0\37754\0\377\40," + "\0\3772*\5\377-0\0\3774\13\0\377,\22\0\3773\15\0\3772\14\0\377\33\12" + "\0\377\30\3\0\377\33\13\0\377\35\2\0\377#\0\0\377\26\11\0\377\25\4\0" + "\377\33\13\0\377(\12\0\377\37\32\0\3770\17\5\377-\31\0\377(\10\0\377" + "\"\20\0\377(\1\0\377\40\0\0\377\36\4\0\377&\3\0\377\22\13\0\377\31\0" + "\0\377\21\7\2\377#\16\0\377\"\3\0\377#\5\4\377\35\26\0\377\35\34\0\377" + "\16,\0\377\0;\0\377\11:\0\377\0B\0\377\0""5\0\377\0<\0\377\0""7\0\377" + "\202\0""8\0\377\21\0B\0\377\0>\0\377\0""8\0\377\0\0\0\377/4~\377=a\272" + "\377;U\267\377,W\262\377\36I\251\3776W\305\377;U\264\377\0\0\0\377\0" + "\17\0\377\0\31\0\377\0\33\0\377\0\17\0\377\0\30\0\377\202\0\21\0\377" + "\4\0\17\0\377\0'\0\377\0,\0\377\0\31\0\377\202\0\25\0\377\25\0\23\1\377" + "\0\"\0\377\0(\0\377\0%\0\377\0\37\0\377\0$\0\377\0\40\6\377\0,\0\377" + "\0/\20\377\0\"\0\377\0\32\2\377\0\0*\377!v\274\377\37v\313\377\33\203" + "\314\377+{\307\377\37~\313\377\26\177\304\377\0%[\377\0\15\0\377\0""8" + "\0\377\202\0@\0\377^\0E\0\377\0B\0\377\0A\0\377\0B\0\377\0@\0\377\0>" + "\0\377\0B\0\377\0=\0\377\0>\0\377\0""3\0\377\0""8\0\377\0""1\0\377\0" + "=\0\377\0""2\0\377\0/\0\377\0,\0\377\0+\0\377\0""4\0\377\0\"\0\377\0" + "\0\0\377\0\36\32\377\0m\215\377\0\207\233\377\0\222\257\377\0\212\253" + "\377\0\206\244\377\0|\231\377\0\207\227\377\0\206\226\377\0}\224\377" + "\0\200\227\377\0\200\232\377\0r\250\377\0\202\241\377\0\201\235\377\0" + "v\243\377\0n\254\377\0Hn\377\0Qy\377\0o\257\377\0k\256\377\0r\257\377" + "\0m\270\377\0a\224\377\0\34/\377\0\6\0\377\0#\0\377\0\30\0\377\0\0\0" + "\377\0qi\377\0\203\227\377\0\205\236\377\0{\250\377\0\210\241\377\0\220" + "\232\377\0\215\235\377\0\177\224\377\0\11\0\377\0\17\0\377\0\40\0\377" + "\0'\0\377\0\27\0\377\0\20\0\377\0\31\0\377\0\15\0\377\0\30\0\377\0\7" + "\0\377\0\0\0\377\0XO\377\0\205\227\377\0\213\222\377\0\201\211\377\0" + "$\14\377\0""8\0\377\0L\0\377\0""3\0\377\0%\0\377\0\33\0\377\0\36\0\377" + "\4\35\14\377\7\33\22\377\7\16\20\377\230\234\234\377\377\377\377\377" + "j\200s\377\2=\15\377\0P\2\377\0c\0\377\0Z\0\377\0a\0\377\0Y\0\377\0[" + "\0\377\13`\13\377\353\361\353\377\202\377\377\377\377S|\257\177\377\10" + "h\16\377\13]\16\377\10_\14\377\3[\4\377\1Y\1\377\0[\0\377\0Z\0\377\0" + "d\0\3778\2238\377\353\363\353\377\211\274\211\377\3q\3\377\0z\0\377\0" + "n\0\377\0m\0\377\0b\0\377\0T\0\377\0G\0\377\0=\0\377\257\303\257\377" + "\377\377\377\377\214\251\214\377\1T\1\377\0E\0\377\0@\1\377\0>\7\377" + "\0G\5\377\0<\7\377\0""0\0\377\0D\22\377\0A\15\377\37_3\377\352\362\354" + "\377\302\343\310\377\2\264*\377\0\3041\377\0\303B\377\0\2511\377\0\230" + "1\377\0{(\377\0f-\377\0X.\377\0R+\377\0Z2\377\0I8\377\0C,\377\0G+\377" + "\0N2\377\0TJ\377\0FL\377\0NE\377\0RO\377\0GK\377\0N[\377\0OD\377\0L9" + "\377\0NE\377\0KI\377\0HG\377\0QB\377\0KA\377\0SF\377\0aP\377\0dL\377" + "\0ad\377\0b_\377\5e\222\377\0>s\377\0(f\377\0!h\377W\0#\377\77\0\0\377" + "&\0\0\377#\0\0\3776\37\0\3779%\0\3775&\0\377;\"\2\3771\25\0\377*\20\0" + "\3774\25\0\377,\0\0\377\202\37\0\0\3772+\0\0\3770\2\0\377&\6\0\377-\15" + "\0\377!\21\3\3771\10\0\377.\2\0\377,\11\0\3777\5\0\3771\16\3\3772\14" + "\0\377-\15\7\3773\13\5\377%\3\7\377!\3\0\377#\3\2\377$\0\3\377\36\12" + "\0\377!\26\0\377+\17\0\377\40\32\0\377\"\34\0\377\21\24\0\377\26\35\0" + "\377\21,\0\377\11""4\0\377\0/\0\377\0""1\1\377\0=\0\377\0""4\0\377\7" + "1\0\377\6B\0\377\0B\0\377\0H\0\377\0F\0\377\0-\0\377\0\0\0\377+8q\377" + "@`\246\3775[\253\377,G\256\377!K\277\377,S\267\3776D\276\377\0\0\0\377" + "\0\10\0\377\0\13\0\377\0\0\0\377\0\1\0\377\0\6\0\377\203\0\0\0\377\1" + "\0\3\0\377\207\0\0\0\377P\0\7\0\377\0\3\0\377\0\12\4\377\0\22\6\377\0" + "\"\0\377\0*\0\377\0,\0\377\0\27\0\377\0\0'\377&w\305\377#v\301\377!w" + "\303\377\34o\313\377*k\315\377\35t\311\377\0(v\377\0\6\5\377\0\6\0\377" + "\0\26\0\377\0\23\0\377\0\32\0\377\0\26\0\377\0\24\0\377\0\3\0\377\0\20" + "\0\377\0\27\0\377\0\13\0\377\0\31\0\377\0\20\0\377\0\22\0\377\0\31\0" + "\377\0\40\0\377\0/\0\377\0""9\0\377\0""5\0\377\0&\0\377\0\40\0\377\0" + "(\0\377\0\24\0\377\0\23\22\377\0~\241\377\0\200\237\377\0\207\242\377" + "\0\216\250\377\0~\230\377\0p\236\377\0r\237\377\0}\240\377\0w\216\377" + "\0o\222\377\0v\237\377\0v\232\377\0t\234\377\0s\226\377\0x\237\377\0" + "n\260\377\0j\255\377\1o\245\377\0b\261\377\0c\264\377\0g\251\377\0_\257" + "\377\0_\253\377\0l\256\377\0\32""7\377\0\0\0\377\0\14\0\377\0\27\0\377" + "\0\11\0\377\0\0\0\377\0bq\377\0p\235\377\0v\227\377\0z\230\377\0~\243" + "\377\0}\232\377\0~\237\377\0^w\377\0\0\0\377\0\12\0\377\202\0\34\0\377" + "\1\0%\0\377\202\0\17\0\377\35\0\5\0\377\0\0\0\377\0ha\377\0\220\216\377" + "\3\206\210\377\0sa\377\0\2\0\377\0:\0\377\0o\0\377\0_\0\377\0B\0\377" + "\0$\0\377\0\34\0\377\0\35\0\377\2$\6\377\3#\7\377\216\221\222\377\377" + "\377\377\377\200\207\206\377\6'\24\377\5N\13\377\0U\1\377\0Y\1\377\0" + "X\1\377\0T\0\377\0N\0\377\0Z\0\377\1a\3\377~\256\202\377\202\377\377" + "\377\377V\266\323\267\377\7e\16\377\13^\16\377\12O\17\377\5S\13\377\1" + "M\3\377\0`\1\377\0]\0\377C\215C\377\366\371\366\377\223\276\223\377\1" + "e\1\377\0\210\0\377\0\204\0\377\0t\0\377\0s\0\377\0f\0\377\0Q\0\377\0" + "E\0\377\0K\0\377`\220`\377\214\252\214\377\1A\1\377\0L\0\377\0X\0\377" + "\0G\0\377\0L\0\377\0F\0\377\0B\2\377\0""6\11\377\0\77\4\377\2W\23\377" + "\313\336\320\377\377\377\377\377V\275o\377\0\304\37\377\0\3033\377\0" + "\310I\377\0\276D\377\0\2479\377\0\213/\377\0p%\377\0]2\377\0U/\377\0" + "P)\377\0N\35\377\0\77""1\377\0C\"\377\0D'\377\0ZD\377\0`<\377\0Z@\377" + "\0hC\377\0OG\377\0JE\377\0LB\377\0B\77\377\0BL\377\0JM\377\0O9\377\0" + "RK\377\0]N\377\0\\L\377\0RS\377\0YL\377\0cZ\377\0\\^\377\0l\215\377\0" + "Ng\377\0/Q\377\0+X\377c\0\40\377:\0\15\377#\0\0\377*\0\0\377:)\0\377" + "/\33\0\377C\33\0\377B\14\0\377<\16\0\3776\7\0\3774\5\0\3776\1\0\377-" + "\0\0\377/\0\0\3773\0\0\377\202:\7\0\377\37+\26\0\377%\3\0\3773\1\0\377" + ",\2\0\3771\14\0\3778\10\0\377,\22\0\3775\5\0\3778\11\0\3779\23\0\377" + "+\12\0\377,\10\0\377'\15\2\377\36\13\0\377\31\22\0\377%\22\0\377&\27" + "\0\377$\22\0\377\34!\0\377\22\33\0\377\16/\0\377\11""1\0\377\11""8\0" + "\377\7""7\0\377\6""9\0\377\1""9\0\377\10""8\0\377\0\77\0\377\0""9\0\377" + "\0=\0\377\0C\0\377\202\0;\0\377\10\0\0\0\377@7z\377EN\246\377GQ\267\377" + "6R\256\3775K\270\377>=\266\3778G\256\377\203\0\0\0\377S\0\0\32\377\0" + "\0\26\377\3\0\20\377\4\0\34\377\0\0\31\377\1\0#\377\0\0\33\377\0\0\34" + "\377\0\0%\377\0\0)\377\0\0(\377\0\0\40\377\0\0""1\377\0\0.\377\0\0\40" + "\377\0\0#\377\0\0,\377\0\0%\377\0\0'\377\0\0\0\377\0\40\0\377\0\26\0" + "\377\0\7+\377\33}\305\377\"g\273\377!h\303\377\32g\300\377*g\302\377" + "\36x\276\377\35e\263\377\0\"b\377\0\35V\377\0%T\377\0$I\377\0\"B\377" + "\0""0H\377\0*E\377\0&>\377\0\36H\377\0,I\377\0+@\377\0.>\377\0""38\377" + "\0""06\377\0""4+\377\0+7\377\0\10\0\377\0\2\0\377\0\23\0\377\0!\0\377" + "\0(\0\377\0$\0\377\0\0\0\377\0C\\\377\0\203\230\377\0|\241\377\0w\235" + "\377\0u\240\377\0r\241\377\0S\202\377\0\23\15\377\0\7\0\377\0\6\1\377" + "\0\0\0\377\0\6\4\377\0\0\0\377\0\0\2\377\0\0\6\377\0\0\15\377\0\0\14" + "\377\0\0\11\377\0\23""1\377\0c\246\377\0b\253\377\0`\236\377\0Z\241\377" + "\0Z\252\377\0d\242\377\0\25.\377\0\0\0\377\0\12\0\377\0\6\0\377\0\16" + "\0\377\202\0\0\0\377\15\0Im\377\0x\230\377\0z\231\377\0{\233\377\0\203" + "\241\377\0\202\230\377\0r\244\377\0iv\377\0\0\0\377\0\14\0\377\0\40\0" + "\377\0\30\0\377\0\22\0\377\202\0\0\0\377\34\0nw\377\0\227\217\377\0\215" + "\234\377\0p_\377\0\0\0\377\0""3\0\377\0i\0\377\0t\0\377\0h\0\377\0W\0" + "\377\0""7\0\377\0$\0\377\0\31\0\377\0\22\0\377jtk\377\377\377\377\377" + "\315\321\317\377\10%\30\377\6""9\26\377\5K\17\377\1d\11\377\1^\11\377" + "\2c\6\377\1`\2\377\1[\3\377\4O\10\377\6G\15\377*`4\377\202\377\377\377" + "\377\40\326\346\327\377\6k\16\377\6Z\20\377\11`\23\377\11]\17\377\4d" + "\7\377\0l\0\377\40v\40\377\355\363\355\377\302\330\302\377\3q\3\377\0" + "j\0\377\0x\0\377\0\201\0\377\0\207\0\377\0\202\0\377\0\203\0\377\0f\0" + "\377\0Z\0\377\0P\0\377\12Q\12\377\1Q\1\377\0K\0\377\0V\0\377\0W\0\377" + "\0H\1\377\0F\5\377\0I\0\377\0>\11\377\0L\6\377\0P\1\377P\210T\377\202" + "\377\377\377\377_\25\2425\377\0\273,\377\0\312=\377\0\300N\377\0\261" + "7\377\0\213\"\377\0\205\32\377\0x\40\377\0g$\377\0U)\377\0J(\377\0I!" + "\377\0L(\377\0E,\377\0P2\377\0W<\377\0^;\377\0jF\377\0]G\377\0WP\377" + "\0_M\377\0PO\377\0KP\377\0[P\377\0PK\377\0_O\377\0ZV\377\0UP\377\0VP" + "\377\0VQ\377\0YL\377\0_X\377\0^P\377\0g\214\377\0Jt\377\0-P\377\0""6" + "Z\377`\0+\377;\0\2\377$\0\0\3772\0\0\3777\30\0\3778\25\0\377@\26\0\377" + "4\17\0\377;\4\0\3775\11\0\3779\0\0\377-\3\0\3772\10\0\3777\10\0\377+" + "\4\0\3771\16\0\377/\25\0\3776\27\0\3774\11\0\377<\16\0\377<\12\0\377" + "0\7\0\377:\4\0\377A\10\0\377B\16\0\377@\13\0\3777\10\0\377\35\23\0\377" + "%\31\0\377$\24\0\377%\"\6\377&\31\2\377'\20\0\377)\23\0\377\31\33\0\377" + "\36\36\0\377\14&\0\377\16\"\0\377\16.\0\377\30>\0\377\6""8\0\377\1""7" + "\0\377\6""5\3\377\0:\0\377\0,\0\377\6=\0\377\4=\0\377\4""4\0\377\7=\0" + "\377\1>\0\377\0\0\0\377\77,y\377OI\260\377IK\257\377BI\253\377>@\265" + "\3778A\254\3774D\253\377\202\0\0\0\377=\5\0\20\377EB\246\377HQ\267\377" + "DB\264\377WB\262\377SE\265\377BE\261\3777J\261\3779G\261\377\33\377\4_\31\377\5X\23\377\6T\17\377\4_\14\377\5E\14\377\10E" + "\21\377\12\77\26\377\17E\30\377\376\376\376\377\377\377\377\377\365\371" + "\365\377\5c\15\377\10k\15\377\5l\16\377\5q\15\377\7p\13\377\12j\15\377" + "\315\341\315\377\371\373\371\377(\210(\377\0f\0\377\0l\0\377\202\0\204" + "\0\377s\0\233\0\377\0\224\0\377\0\211\0\377\0~\1\377\0d\5\377\0Y\11\377" + "\0Z\0\377\0L\0\377\0S\0\377\0c\0\377\0Y\0\377\0P\3\377\0W\12\377\0M\5" + "\377\0Q\2\377\0N\15\377\0a\14\377\277\327\300\377\377\377\377\377\322" + "\346\330\377\0\232/\377\0\2558\377\0\315=\377\0\304C\377\0\2501\377\0" + "\226\35\377\0\225\27\377\0\177\36\377\0i#\377\0c*\377\0c$\377\0\\.\377" + "\0P,\377\0Z0\377\0P7\377\0W:\377\0[B\377\0ZE\377\0[H\377\0XH\377\0aK" + "\377\0]L\377\0SR\377\0ZV\377\0`^\377\0[P\377\0[M\377\0`[\377\0[`\377" + "\0XZ\377\0\\U\377\0QW\377\0Zd\377\10[\233\377\0""9n\377\0\36^\377\0\37" + "b\377c\0(\3775\0\10\377\"\0\0\377$\0\0\377<\36\0\3773\7\0\377,\26\0\377" + "/\23\0\3772\10\0\3775\21\0\377+\6\0\377+\21\0\3776\27\0\3775\12\0\377" + "3\15\0\377%\15\0\3776\27\0\3772\30\0\377<\36\0\377B!\0\3773\27\0\377" + "8\2\0\3776\0\0\377;\1\0\377@\26\0\3777\4\0\3776\26\0\377#\31\0\377,\31" + "\0\377:\31\0\377,\34\0\377/\24\0\377#\35\0\377\35%\0\377\25\32\0\377" + "\35\40\3\377\36/\0\377\27F\0\377\0A\0\377\7>\0\377\10""8\0\377\10""7" + "\0\377\0""4\0\377\0""7\0\377\2""2\0\377\3.\0\377\1#\0\377\22(\0\377\24" + "\40\0\377\13\40\0\377\1\0\0\377G\40}\377G8\260\377N@\266\377G1\263\377" + "M5\261\377:\77\236\377;;\246\377\202\0\0\0\377`\0\0\11\377HD\255\377" + "BG\252\377KC\236\377Z:\254\377QQ\271\377>I\267\3771K\253\377*Q\266\377" + "-V\256\3770b\257\3779Q\245\3772T\256\377.d\245\3770R\263\377&Z\305\377" + "+^\303\377)b\266\377(^\260\377\0\15-\377\0\12\0\377\0(\0\377\0\20\0\377" + "\0\0\20\377)n\267\377%m\271\377$x\271\377\34l\255\377\34c\271\377\31" + "]\265\377\27b\253\377#t\233\377\26h\226\377\40i\235\377\16d\235\377\0" + "r\243\377\13x\245\377\11s\237\377\24s\241\377\6m\237\377\6q\230\377\0" + "n\232\377\0r\220\377\0k\227\377\2^\237\377\7m\237\377\5r\236\377\0m\232" + "\377\0k\252\377\0\23=\377\0\0\0\377\0\11\0\377\0\10\0\377\0\0\0\377\0" + "_\251\377\0c\257\377\0d\262\377\0m\250\377\0i\244\377\0g\231\377\0\0" + "\17\377\0\0\0\377\0\37\0\377\0\30\0\377\0\17\0\377\0\12\0\377\0\6\0\377" + "\0\25\0\377\0\21\0\377\0\36\0\377\0\33\0\377\0\24\0\377\0\0\0\377\0""8" + "\\\377\0b\235\377\0X\227\377\0W\236\377\0g\237\377\0v\235\377\0\40""2" + "\377\0\0\0\377\0\24\0\377\0\33\0\377\0\22\0\377\0\11\0\377\0\14\0\377" + "\0\0\0\377\0\10\0\377\0n\204\377\0|\224\377\5}\213\377\7~\212\377\0\212" + "\224\377\0\205\222\377\0z\214\377\0av\377\202\0\0\0\377\177\0\33\10\377" + "\0|\212\377\0\214\231\377\0\222\225\377\0""8#\377\0\12\0\377\0\24\0\377" + "\0""9\0\377\0""4\0\377\0L\0\377\0R\0\377\0b\0\377\0^\0\377\0P\0\377\0" + "9\0\377\0+\0\377\214\243\214\377\377\377\377\377\323\330\323\377\1+\5" + "\377\2%\12\377\6\77\24\377\5K\31\377\13W\36\377\15J\32\377\12;\25\377" + "\12@\23\377\10""8\26\377\11=\26\377\10""7\22\377\6K\21\377\345\355\346" + "\377\377\377\377\377\360\365\361\377\6o\21\377\4o\11\377\3o\7\377\6|" + "\7\377\5i\10\377z\267|\377\377\377\377\377\222\306\222\377\0n\0\377\0" + "r\0\377\0s\0\377\0w\0\377\0\211\0\377\0\237\13\377\0\241\2\377\0\234" + "\3\377\0\202\13\377\0}\20\377\0p\6\377\0g\0\377\0b\0\377\0O\0\377\0P" + "\1\377\0H\7\377\0V\0\377\0M\15\377\0S\24\377\0^\5\377\0a\4\377\22l\37" + "\377\374\375\374\377\377\377\377\377\221\306\241\377\0\235,\377\0\276" + "7\377\0\311>\377\0\275\77\377\0\245.\377\0\230\34\377\0\220!\377\0{)" + "\377\0\200$\377\0j#\377\0W5\377\0Z,\377\0c-\377\0P9\377\0V0\377\0MA\377" + "\0I\77\377\0XN\377\0ZK\377\0cG\377\0UA\377\0ZK\377\0TU\377\0Z[\377\0" + "cP\377\0[K\377\0f\\\377\0ed\377\0h]\377\0^X\377\0MU\377\0d`\377\0_m\377" + "\6O\226\377\0""4\201\377\0#a\377\0\36g\377T\0%\377C\0\6\377/\0\0\377" + "8\0\0\3774\33\0\377-\25\0\377/\10\0\377\"\7\0\377*\34\0\3770\16\0\377" + "/\11\0\377/\15\0\377)\14\0\3771\16\0\377;\26\0\3773\35\0\3778\40\0\377" + "\77\30\0\377>'\0\377:!\0\3777#\0\377<\24\0\377A\34\0\377#C\24\0\377B" + "\36\0\377=\40\0\3773\31\0\3774\36\0\3771'\0\3777!\0\377#\"\0\377)$\0" + "\377\32\37\0\37720\0\377)\"\0\377%$\0\377\11""6\0\377\15""9\0\377\23" + "(\0\377\24""4\0\377\0-\0\377\2'\0\377\12*\0\377\12+\0\377\13""5\0\377" + "\0""1\0\377\16)\0\377\20\32\0\377\20\"\0\377\21\35\0\377\0\0\0\377G\17" + "u\377PG\264\377FH\274\377O/\267\377C;\274\377G6\247\377ML\263\377\203" + "\0\0\0\377;\34\0""9\377\2\0\30\377\22\0\23\377\23\0\40\377\7\0\34\377" + "\4\0\32\377\7\0\21\377\0\0\26\377\0\0\17\377\0\0\21\377\0\0\24\377\0" + "\32R\3776_\251\3772j\271\377!g\271\377.[\301\377#e\262\377(Y\241\377" + "\0\0\27\377\0\24\0\377\0\34\0\377\0\37\0\377\0\0\0\377#]\241\377&s\236" + "\377,n\235\3771Y\265\377'^\256\377\37[\244\377\31_\242\377\27f\225\377" + "\23V\225\377\23d\216\377\11`\224\377\21i\224\377\20e\225\377\1r\225\377" + "\5o\225\377\3a\220\377\6l\215\377\0h\232\377\1b\230\377\0V\222\377\0" + "W\225\377\0_\221\377\15b\217\377\7Y\240\377\10M\237\377\0B\200\377\0" + "\0\0\377\0\7\0\377\0\5\0\377\0\0\0\377\0U\240\377\0]\264\377\0Z\267\377" + "\0[\243\377\0W\246\377\0[\247\377\202\0\0\0\377\31\0\14\0\377\0\7\0\377" + "\0\15\0\377\0\0\0\377\0\25\0\377\0\26\0\377\0\23\0\377\0\21\0\377\0\23" + "\0\377\0\20\0\377\0\0\0\377\0=^\377\0c\230\377\0P\244\377\0d\250\377" + "\0g\240\377\1V\233\377\0\16-\377\0\0\0\377\0\37\0\377\0\12\0\377\0\20" + "\0\377\0\24\0\377\0\7\0\377\0\3\0\377\202\0\0\0\377I\0sr\377\0}\210\377" + "\0\202\212\377\0\210\216\377\7\213\212\377\11\213\211\377\14\212\222" + "\377\0qk\377\0""1\17\377\10\220\226\377\6\234\216\377\11\227\212\377" + "\0\77\12\377\0\14\0\377\0#\0\377\0""8\0\377\0\77\0\377\0B\0\377\0J\0" + "\377\0i\0\377\0k\0\377\0c\0\377\0f\0\377\0U\0\377\10F\10\377\365\367" + "\365\377\377\377\377\377w\214w\377\1.\3\377\6,\21\377\6\77\31\377\7A" + "\25\377\6""8\24\377\11A\25\377\6F\26\377\10O\33\377\2F\27\377\4A\17\377" + "\3B\12\377\10B\22\377\344\355\346\377\377\377\377\377\333\352\335\377" + "\7h\23\377\5l\13\377\3|\7\377\2{\5\377\21\220\23\377\357\367\357\377" + "\377\377\377\377%\210%\377\0u\0\377\0x\0\377\0v\0\377\0\207\0\377\0\223" + "\0\377\0\233\0\377\0\247\0\377\0\240\25\377\0\234\22\377\0\205\5\377" + "\0w\0\377\0i\14\377\0R\12\377\0O\14\377\0I\1\377\0N\11\377\0N\2\377\0" + "G\22\377\0Z\20\377\0^\12\377\0Q\0\377Y\233c\377\202\377\377\377\377b" + "Q\244i\377\0\224%\377\0\2648\377\0\307E\377\0\302@\377\0\2635\377\0\213" + "/\377\0\201\37\377\0l\36\377\0b\32\377\0]\34\377\0T-\377\0[(\377\0\\" + ")\377\0`7\377\0L>\377\0I=\377\0B9\377\0HC\377\0NM\377\0KF\377\0RJ\377" + "\0UW\377\0NO\377\0QV\377\0bK\377\0^`\377\0ke\377\0_i\377\0ii\377\0Xm" + "\377\0Xl\377\0Zp\377\0dp\377\0\\\234\377\0=p\377\0&T\377\0/V\377f\0'" + "\377U\0\16\377>\0\0\377B\0\0\377;\37\0\377)\17\0\377/\40\0\377!\23\0" + "\377.\6\0\3777\22\0\377*\16\0\3770\33\0\377+\23\0\377:\15\0\3777\22\0" + "\3772\33\0\377@(\0\377B/\0\377*+\0\377.2\0\3772\36\0\3774%\0\377\77)" + "\0\377B(\0\377E#\0\377F\34\0\377/\34\0\3772\32\0\377+\37\0\377.\31\0" + "\377,$\0\377&-\0\377\30&\0\377\25%\0\377\36.\0\377\33*\0\377\24)\0\377" + "\21""2\0\377\13\37\0\377\14""1\0\377\1+\0\377\13-\0\377\27+\0\377\12" + "-\0\377\14/\0\377\16.\0\377\24\36\0\377\35\37\0\377\21\32\0\377\35\40" + "\0\377\0\0\0\377A\36\203\377SL\267\377P=\273\377R:\256\377K0\271\377" + "H>\261\377O:\272\377\0\0\0\377\0\23\0\377\214\0\0\0\3777\0\3""3\3776" + "X\255\377)[\250\3772X\253\3777Z\237\377)Y\242\377$V\255\377\0\0\10\377" + "\0\0\0\377\0%\0\377\0!\0\377\0\0\0\377\0\17-\3770O\232\3771V\237\377" + "\40L\224\377\40G\225\377.K\215\377\"S\216\377-]\234\377+W\225\377*h\214" + "\377\31n\225\377\20h\237\377\23l\215\377$a\216\377\33c\221\377\21d\232" + "\377\15b\223\377\13j\230\377\3f\205\377\7Y\220\377\5]\211\377\7L\210" + "\377\13V\233\377\0Z\251\377\0U\245\377\3S\232\377\0\0\0\377\0\4\0\377" + "\0\7\0\377\0\0\0\377\7P\242\377\12Y\235\377\0W\225\377\6Y\263\377\11" + "^\243\377\3b\240\377\0\0\11\377\0\0\0\377\0\11\0\377\0\10\0\377\0\0\0" + "\377\0\13\0\377\0\30\0\377\202\0\25\0\377\23\0\24\0\377\0\33\0\377\0" + "\"\0\377\0\0\0\377\0A\\\377\3[\235\377\0^\240\377\0b\227\377\2b\233\377" + "\0n\236\377\0\24""1\377\0\0\0\377\0\23\0\377\0\17\0\377\0\27\0\377\0" + "\4\0\377\0\2\0\377\0\0\0\377\0\25\0\377\202\0\0\0\377\17\0si\377\7z\202" + "\377\16\205\205\377\22\201\207\377\21|\202\377\7\206x\377\17\220t\377" + "\12\214s\377\3\220u\377\13\203j\377\0\17\0\377\0\7\0\377\0""1\0\377\0" + "/\0\377\0""7\0\377\202\0""9\0\377\7\0O\0\377\0c\0\377\0f\0\377\0s\0\377" + "\0x\0\377\0V\0\377HxH\377\202\377\377\377\377\177;^;\377\0,\0\377\4O" + "(\377\7:\26\377\7A\25\377\12T\30\377\5F\32\377\4N\34\377\4G\33\377\0" + "@\17\377\2G\13\377\2H\11\377\10Q\30\377\366\370\367\377\377\377\377\377" + "\303\332\311\377\7}\30\377\6\201\23\377\3\206\12\377\2\211\7\377\205" + "\320\206\377\377\377\377\377\301\346\301\377\0\233\0\377\0\215\0\377" + "\0z\0\377\0w\0\377\0\224\0\377\0\230\4\377\0\263\13\377\0\254\17\377" + "\0\263\20\377\0\261\23\377\0\255\21\377\0\227\22\377\0~\27\377\0^\32" + "\377\0Z\4\377\0U\0\377\0I\0\377\0K\13\377\0S\10\377\0Z\23\377\0Z\11\377" + "\0V\15\377\234\303\242\377\377\377\377\377\376\376\376\377\22oA\377\0" + "\203,\377\0\241<\377\0\274\77\377\0\277J\377\0\247S\377\0\230C\377\0" + "\207*\377\0g&\377\0U,\377\0X#\377\0T-\377\0g7\377\0V6\377\0X6\377\0\\" + "\77\377\0X6\377\0GK\377\0PI\377\0GR\377\0G@\377\0MJ\377\0XW\377\0UY\377" + "\0hS\377\0cR\377\0oe\377\0qj\377\0nc\377\0ir\377\0ck\377\0lq\377\0rj" + "\377\0df\377\2g\217\377\0Kt\377\0$Z\377\0\35T\377r\0+\377W\0\22\377;" + "\0\0\3772\0\0\377,\27\0\377*\32\0\377'\15\0\377!\25\0\377:\"\0\377@'" + "\0\3773\32\0\3774\23\0\3772\33\0\3773\24\0\377\77-\0\377:3\0\377\77-" + "\0\377=(\0\377D-\0\377;-\0\3775/\0\377>9\0\377G,\0\377=-\0\3778&\0\377" + "@%\0\3770!\0\3771$\0\377$\33\0\377\27$\0\377)'\0\377*.\0\377#+\0\377" + "\24""4\0\377\40&\0\377!+\0\377\31*\0\377!\36\0\377\26&\0\377\15+\0\377" + "\30\"\0\377\25\17,\0\377\15*\0\377\30%\0\377\16\34\0\377\31$\0\377\15" + "\30\0\377\20\35\0\377\7\21\0\377\3\34\0\377\0\0\0\377Q\32p\377ZE\255" + "\377VJ\245\377_7\251\377]/\262\377U3\271\377PB\257\377\0\0\0\377\0\6" + "\0\377\0\21\0\377\0\22\0\377\202\0\23\0\377\17\0\15\0\377\0\4\0\377\0" + "\24\0\377\0\14\0\377\0\7\0\377\0\0\0\377\0\5\0\377\0\0\0\377\15\26=\377" + "Hh\250\377\77\\\250\377=X\222\3777J\233\377EH\232\377DU\236\377\202\0" + "\0\0\377*\0\20\0\377\0\32\0\377\0\12\0\377\0\0\0\377\7\0\10\377!2[\377" + "0[\221\3779]\212\3775V\200\377.X\205\3772_\230\3779b\216\377\0\377]\377\30k\230\377\23j\231\377\37h\226\377\32t\203\377\34j" + "\211\377\0\7\30\377\204\0\0\0\377\6\0\6\0\377\0\10\0\377\0\31\0\377\0" + "\25\0\377\0\27\0\377\0(\0\377\202\0\0\0\377\36&\200h\377/\221y\377\"" + "\214j\377\32\230s\377\33\231o\377%\222r\377\24\220o\377\0X\"\377\0\14" + "\0\377\0\31\0\377\0,\0\377\0/\0\377\0""0\0\377\0D\0\377\0>\0\377\0X\0" + "\377\0d\0\377\0n\0\377\0\207\0\377\0\205\0\377\0o\0\377\303\326\303\377" + "\377\377\377\377\357\361\357\377\0""7\0\377\0""5\0\377\0""0\0\377\0H" + "\0\377\0T\0\377\0R\0\377\202\0W\0\377\5\0`\0\377\0c\0\377\0\\\0\377\0" + "b\0\377P\266s\377\202\377\377\377\377\5I\244U\377\4\230\16\377\4\230" + "\21\377\6\232\35\377>\256Q\377\202\377\377\377\377\7@\332@\377\0\325" + "\0\377\0\264\0\377\0\257\0\377\0\240\0\377\40\262\40\377\323\361\323" + "\377\207\377\377\377\377\11}\302\177\377\0~\10\377\0j\7\377\0o\23\377" + "\0^\15\377\0h\40\377\0h\17\377\0v\26\377\21v.\377\202\377\377\377\377" + "w\216\271\234\377\0v(\377\0v%\377\0\2331\377\0\270J\377\0\305U\377\0" + "\320W\377\0\310G\377\0\257;\377\0\232-\377\0\2000\377\0a\40\377\0e$\377" + "\0l0\377\0l'\377\0b5\377\0h1\377\0e3\377\0_4\377\0R6\377\0P7\377\0Y9" + "\377\0^>\377\0[J\377\0ZE\377\0kV\377\0nX\377\0j\\\377\0mT\377\0nL\377" + "\0q^\377\0yg\377\0\202U\377\0\177]\377\0vl\377\0j\240\377\0>x\377\0""3" + "f\377\0@k\377x\1<\377Y\0\27\3773\0\2\3775\0\0\377D\33\7\377D,\0\377:" + "'\0\377D7\0\377@.\0\377D,\0\37793\0\377<-\0\377J5\0\377C-\0\377B0\0\377" + "C)\0\377G2\0\377<2\0\377<-\0\377y\206\3776n\206\377;e\201\3777o\205\377.r\200\377\0\0\0\377\0\14\0\377" + "\0\22\0\377\0\0\0\377\"w\216\377&u\223\3776w\204\377)\204\204\377\36" + "yw\377%\202\216\377\0\0\4\377\0\0\0\377\0\30\0\377\0\12\0\377\0\5\0\377" + "\0\14\0\377\0\6\0\377\0\16\0\377\0\22\0\377\0\14\0\377\0\15\0\377\0\4" + "\0\377\0\0\0\377\0RF\377/r|\377%x\214\3771\177z\377(}{\377\40|r\377\0" + "\"\14\377\0\0\0\377\0\16\0\377\0\10\0\377\0\0\0\377\0\12\0\377\0\6\0" + "\377\0\15\0\377\0$\0\377\0\27\0\377\0\37\0\377\0\10\0\377\0\27\0\377" + "3\226m\377:\251}\3773\237p\377,\246e\3776\253m\377-\251v\377,\257\204" + "\377+\270|\377+\252\204\377\0n*\377\0\5\0\377\0!\0\377\0'\0\377\0@\0" + "\377\0=\0\377\0U\0\377\0k\0\377\0\221\0\377\0\251\0\377\0\243\0\377\0" + "\223\0\377\361\367\361\377\377\377\377\377\355\361\355\377\0""6\0\377" + "\0,\0\377\202\0""7\0\377\20\0B\0\377\0W\0\377\0c\0\377\0e\3\377\1_\3" + "\377\1d\7\377\1p\23\377\4\200\31\377\333\356\335\377\377\377\377\377" + "\203\274\207\377\1p\6\377\0v\7\377\0|\6\377\4~\25\377\245\323\245\377" + "\202\377\377\377\377\177\14\314\14\377\0\314\0\377\0\321\0\377\0\306" + "\0\377\0\267\0\377\0\275\0\377\0\300\0\377\0\322\16\377\0\323\25\377" + "\0\321\37\377\250\360\261\377\377\377\377\377\360\373\360\377\2\262\4" + "\377\0\223\5\377\0\206\3\377\0}\14\377\0r\13\377\0n\15\377\0u\3\377\0" + "u\2\377\0p\0\377\205\273\205\377\377\377\377\377\365\371\365\377\10p" + "\30\377\0f\10\377\0~\23\377\0\210\36\377\0\227-\377\0\274F\377\0\326" + "Q\377\0\361U\377\0\345A\377\0\266:\377\0\241\37\377\3\222-\377\0\205" + "5\377\0\177'\377\0g\34\377\0t\37\377\0d)\377\0j2\377\0j*\377\0j9\377" + "\0_9\377\0iB\377\0_8\377\0kI\377\0p>\377\0i>\377\0fJ\377\0gM\377\0{J" + "\377\0vO\377\0uN\377\0~X\377\0{P\377\0{U\377\0\206X\377\0t\220\377\0" + "Np\377\0:W\377\0""0S\377}\12A\377c\0\27\377=\0\12\3775\0\6\377E\"\13" + "\377M/\11\377Q3\4\377U6\34\377S3\13\377L=\23\377F<\6\377P>\4\377Q9\11" + "\377N=\0\377B5\12\377\77\77\0\377B9\4\377BA\0\37795\0\377@7\0\3776,\0" + "\377;#\0\377-(\0\377-2\0\377(%\0\377-&\0\377/1\0\377$,\0\377*0\0\377" + "\"B\0\377+7\0\377!>\0\377(0\0\377\"+\0\377\37(\0\377\"\40\0\377\"(\0" + "\377*!\0\377(&\0\377'\33\0\377-\14\0\377\33\0\0\377\40\15\0\377\22\20" + "\0\377\34\23\0\377&\16\0\377!\21\0\377!\7\0\377&\12\0\377\"\7\0\377\13" + "\0\0\377w&c\377\235V\241\377\241Y\256\377\236V\240\377\220Q\235\377\221" + "Z\231\377\207S\237\377\7\0\0\377\0\3\0\377\0\11\0\377\0\20\0\377\0\3" + "\0\377\4\0\6\0\377\2\14\0\377\5\22\0\377\10\22\0\377\203\0\0\0\377\12" + "\0\3\0\377\0\0\0\3779\0#\377}S\221\377uU\214\377rW\220\377g^\222\377" + "i`\224\377we\214\377\12\0\0\377\205\0\0\0\377\1\0\3\0\377\205\0\0\0\377" + "\7\0\14\0\377\0\25\0\377\0\6\0\377\0\21\0\377\0\17\0\377\0\40\0\377\0" + "\27\0\377\202\0\16\0\377\24\0\24\0\377\0\31\0\377\0\0\0\377\35eJ\377" + "E\207\203\377K{\201\377Nx\211\377Rs\205\3778z\177\3777p\206\377\0\0\0" + "\377\0\24\0\377\0\13\0\377\0\0\0\377As\221\377;z\212\377D\206\230\377" + "0\210\217\3772\214\200\3770\203\202\377\202\0\0\0\3774\0\7\0\377\0\25" + "\0\377\0\31\0\377\0\32\0\377\0\14\0\377\0\20\0\377\0\5\0\377\0\15\0\377" + "\0\24\0\377\0\14\0\377\0\0\0\377\0TP\3775\202\203\377-\177\204\377=\204" + "\177\377-v{\377=\210v\377\0\37\3\377\0\0\0\377\0\1\0\377\0\12\0\377\0" + "\2\0\377\0\11\0\377\0\24\0\377\0\31\0\377\0\30\0\377\0\36\0\377\0\0\0" + "\377\0""8\4\377:\241s\377F\251n\377H\254k\377\0g\27\377<\265i\377;\252" + "o\377>\264{\377;\272r\377=\274\200\3776\264|\3773\251z\377\0\77\0\377" + "\0\10\0\377\0.\0\377\0E\0\377\0A\0\377\0f\0\377\0g\0\377\0\231\0\377" + "\0\247\0\377\0\272\0\377\0\246\0\377\365\372\365\377\202\377\377\377" + "\377\24\14D\14\377\0,\0\377\0""4\0\377\0""9\0\377\0J\0\377\0Y\0\377\1" + "e\5\377\1e\12\377\4]\14\377\12l\31\377\13~\"\377J\261\\\377\377\377\377" + "\377\361\367\361\377\23{\31\377\1f\3\377\0h\2\377\0}\2\377\0\200\0\377" + "\303\337\303\377\202\377\377\377\377\177\17\256\17\377\0\272\0\377\0" + "\307\1\377\0\277\12\377\0\300\1\377\0\305\0\377\0\310\0\377\0\314\1\377" + "\0\341\20\377\1\340\37\377\347\375\351\377\377\377\377\377\265\366\270" + "\377\0\301\14\377\0\225\16\377\0\223\6\377\0|\2\377\0\205\5\377\0\204" + "\0\377\0z\7\377\0s\0\377\0r\0\377\307\337\307\377\377\377\377\377\257" + "\320\262\377\0p\5\377\0z\15\377\0\201\36\377\0\216\40\377\0\2151\377" + "\0\245;\377\0\313;\377\0\367J\377\0\351C\377\0\312<\377\0\2554\377s\305" + "\210\377#\221H\377\0\202-\377\0{)\377\0m%\377\0f$\377\0`4\377\0`<\377" + "\0h\77\377\0j9\377\0m;\377\0v>\377\0m7\377\0gE\377\0cO\377\0cG\377\0" + "sH\377\0tM\377\0\202N\377\0\205O\377\0\216K\377\0\222W\377\0\217c\377" + "\0\203V\377\0p\215\377\0Xl\377\0=W\377\0B`\377\204\26;\377e\0&\377E\0" + "\3\377<\0\7\377B$\5\377T+\6\377d/\10\377_-\23\377\\1\24\377W:\24\377" + "KD\14\377L>\33\377NE\17\377I;\0\377F<\14\377JG\15\377E=\27\377>8\12\377" + "A:\0\37768\0\377-3\0\37783\0\377:2\0\3772+\0\377\211\217\377\77\220\213\3774\212\215\3774\203\211\377\202\0\0\0" + "\377\2\0\17\0\377\0\30\0\377\202\0\36\0\377\24\0!\0\377\0\26\0\377\0" + "\27\0\377\0\"\0\377\0\23\0\377\0\15\0\377\0\0\0\377\7VH\3776\211|\377" + "<\223\210\377A\205\203\377<\206\206\377<\212n\377\0\37\17\377\0\0\0\377" + "\0\13\0\377\0\15\0\377\0\12\0\377\0\11\0\377\0\12\0\377\202\0\25\0\377" + "\32\0\0\0\377\0>\0\377R\257h\377S\246r\377\77\231^\377\0<\0\377\0\30" + "\0\377\0B\0\377F\262y\377J\272{\377=\305\202\377B\272\205\377G\274\202" + "\377@\266\202\377F\275\200\377\0T\3\377\0!\0\377\0\77\0\377\0K\0\377" + "\0Y\0\377\0o\0\377\0\232\0\377\0\275\0\377\0\307\0\377\0\257\0\377\326" + "\355\326\377\202\377\377\377\377\24/g/\377\0""8\0\377\0E\0\377\0J\0\377" + "\0Q\0\377\0^\0\377\5^\21\377\3k\25\377\4W\22\377\13j\34\377\13l\35\377" + "\305\343\311\377\377\377\377\377X\236^\377\1r\4\377\0z\2\377\0r\1\377" + "\0n\0\377\0m\0\377\316\343\316\377\202\377\377\377\377\12/\251/\377\0" + "\253\0\377\0\274\0\377\0\307\0\377\0\314\0\377\0\323\0\377\0\330\0\377" + "\0\316\0\377\0\332\0\377&\342+\377\202\377\377\377\377\6y\362\202\377" + "\0\315\1\377\0\254\0\377\0\213\0\377\0|\4\377\0w\0\377\202\0y\0\377\177" + "\0q\4\377\15z\15\377\374\375\374\377\377\377\377\377a\243d\377\0s\10" + "\377\0t\22\377\0v\25\377\0p\35\377\0q\36\377\0\216)\377\0\267'\377\0" + "\335:\377\0\347N\377\0\331M\377\15\317D\377\305\353\320\377\2\2150\377" + "\0\200.\377\0\203.\377\0x1\377\0m9\377\0mF\377\0`J\377\0e\77\377\0v8" + "\377\0x7\377\0m3\377\0xH\377\0pN\377\0mO\377\0iH\377\0qK\377\0jQ\377" + "\0|N\377\0\203N\377\0\215U\377\0\206X\377\0\222a\377\0\217V\377\0o\205" + "\377\0Sk\377\0GP\377\0\77R\377\207\7@\377\\\0\"\377E\0\0\377E\0\4\377" + "R\37\14\377Y)\17\377[(\22\377S/\15\377V0\20\377J*\12\377L5\20\377LA\27" + "\377C6\23\377D;\20\377N4\0\377P5\7\377D6\21\377O,\6\377>)\0\377<1\0\377" + "J7\0\377K-\0\377:.\0\377E3\0\377A0\0\377:1\0\377-6\0\3772:\0\3773-\0" + "\377(4\0\377&:\0\37719\0\377(7\0\377/1\0\377%6\0\377+>\0\377\40'\0\377" + "(\33\0\377%\40\0\377\"\27\0\377+\23\0\3771\32\0\377+\35\0\377\34\20\0" + "\377\37\17\0\377\"\31\0\377\30\21\0\377)\17\0\377\37\6\0\377/\14\0\377" + "'\0\0\377\2457y\377\271n\234\377\256n\240\377\251h\240\377\256m\250\377" + "\271k\234\377\257`\231\377\23\0\0\377\30\16\0\377\14\22\0\377\0\10\0" + "\377\17\13\0\377\21\22\0\377\0\17\0\377\1\17\0\377\0\12\0\377\10\0\0" + "\377\7\23\0\377\0\31\0\377\0\11\0\377\0\0\0\377A\6*\377\231r\231\377" + "\212y\222\377\223v\221\377\223q\230\377\215p\223\377zr\222\377\22\0\0" + "\377\0\0\0\377\0\15\0\377\0\7\0\377\202\0\5\0\377\207\0\0\0\377R\0\2" + "\0\377\0\0\0\377\0\3\0\377\0\13\0\377\0\5\0\377\0\12\0\377\0\16\0\377" + "\0\5\0\377\0\15\0\377\0\10\0\377\0\0\0\3776XD\377b\215\220\377T\202\205" + "\377`\211\206\377T\177\204\377M\205\177\377K\200\206\377\0\0\0\377\0" + "\11\0\377\0\3\0\377\0\0\0\377I}\207\377>\215\214\377J\227\223\377L\213" + "\223\377>}\220\3777\205\234\377\0\1\0\377\0\0\0\377\0\22\0\377\0\31\0" + "\377\0\40\0\377\0\30\0\377\0&\0\377\0(\0\377\0!\0\377\0*\0\377\0\25\0" + "\377\0\31\0\377\0\0\0\377\15fS\377U\226\202\377P\226w\377K\217v\377K" + "\236z\377F\221\200\377\0/\24\377\0\0\0\377\0\10\0\377\0\14\0\377\0!\0" + "\377\0\32\0\377\0$\0\377\0\7\0\377\0\0\0\377\16j\20\377L\302o\377G\265" + "g\377F\254f\377\0\36\0\377\0\27\0\377\0*\0\377\0\22\0\377\0Q\0\377S\302" + "u\377V\271x\377X\276~\377F\275\211\377D\302\213\377E\316\225\377J\304" + "\215\377\0W\10\377\0\37\0\377\0\77\0\377\0`\0\377\0~\0\377\0\244\0\377" + "\0\300\0\377\0\303\0\377\0\262\0\377\265\335\265\377\202\377\377\377" + "\377\24\224\267\224\377\0B\0\377\0H\0\377\0N\0\377\0Y\0\377\4P\10\377" + "\12v\30\377\5`\23\377\6`\13\377\6T\14\377|\263\200\377\377\377\377\377" + "\207\272\212\377\1k\6\377\0n\2\377\0m\0\377\0o\0\377\0q\0\377\0k\0\377" + "\256\320\256\377\202\377\377\377\377\4e\272e\377\0\220\0\377\0\245\0" + "\377\0\261\0\377\202\0\276\0\377\4\0\315\0\377\0\323\0\377\0\335\0\377" + "e\367n\377\202\377\377\377\377v=\353A\377\0\313\0\377\0\244\0\377\0\211" + "\2\377\0{\0\377\0z\10\377\0v\0\377\0j\6\377\0d\5\377g\243j\377\377\377" + "\377\377\320\342\323\377\6u\34\377\0x\37\377\0s\24\377\0n\15\377\0s\33" + "\377\0t\"\377\0\215\33\377\0\242\31\377\0\303&\377\0\345E\377\0\340H" + "\377}\362\236\377l\334\224\377\0\2437\377\0\2150\377\0\206A\377\0yB\377" + "\0yF\377\0m>\377\0w=\377\0h1\377\0jA\377\0sB\377\0pH\377\0mM\377\0h8" + "\377\0y\77\377\0nE\377\0i=\377\0fG\377\0hJ\377\0jD\377\0wN\377\0s\\\377" + "\0|a\377\0\215U\377\0x\214\377\0Oz\377\0""5W\377\0\77X\377\223\3<\377" + "k\0\33\377\77\0\0\377J\0\0\377R\"\12\377Z\"\0\377Z!\3\377Y%\30\377H&" + "\25\377^*\24\377W,\26\377S1\32\377W+\21\377W%\32\377\\/\31\377R-\21\377" + "L&\7\377G,\6\377H1\0\377J#\0\377C/\0\377G(\0\3775<\0\377D8\0\377@'\0" + "\377+<\0\37713\0\3774*\0\377/,\0\377(0\0\3771:\0\37729\0\377=9\0\377" + "&7\0\3771/\0\37774\0\377#!\0\377(\35\0\377\30\31\0\377*\"\0\377%\37\0" + "\3772\"\0\377\"%\0\377'\23\0\377)\31\0\377*\23\0\377>\16\0\3770\3\0\377" + "0\0\0\3776\15\0\377!\0\0\377\230\77r\377\305\201\233\377\273|\244\377" + "\314t\245\377\304l\262\377\270s\232\377\304z\243\377+\0\0\377\11\0\0" + "\377\13\0\0\377\2\0\0\377\13\0\0\377\6\0\0\377\0\0\0\377\7\0\0\377\204" + "\0\0\0\377\14\4\0\0\377\0\0\0\377h7D\377\252\207\234\377\246\210\235" + "\377\245s\240\377\244}\226\377\230v\242\377\224u\234\377\31\0\2\377\0" + "\0\0\377\0\13\0\377\225\0\0\0\377\7BcT\377^\212\211\377d\202\202\377" + "l\206\212\377d{\207\377ew\221\377[{\204\377\204\0\0\0\377\7Pv\213\377" + "Z\216\220\377Z\207\232\377L\212\260\377>\216\256\377H\214\231\377\0\23" + "$\377\206\0\0\0\377\1\0\3\0\377\205\0\0\0\377).{n\377S\240\211\377G\236" + "\177\377O\247\203\377H\255\215\377Q\236\210\377\0""5\37\377\0\0\0\377" + "\0\25\0\377\0\36\0\377\0!\0\377\0\34\0\377\0\3\0\377\0\0\0\377\33\227" + "Q\377F\274n\377J\270k\3774\233I\377\0\4\0\377\0$\0\377\0:\0\377\0>\0" + "\377\0C\0\377\0)\0\377\7`\22\377]\312z\377]\326\203\377O\326\225\377" + "N\332\235\377H\325\201\377C\323x\377M\337|\377\0]\17\377\0/\0\377\0M" + "\0\377\0b\0\377\0\206\0\377\0\265\0\377\0\304\0\377\0\262\0\377z\304" + "z\377\202\377\377\377\377\24\367\371\367\377\31`\32\377\6e\26\377\24" + "|'\377\35\2177\377\13x\25\377\13r\24\377\7g\15\377\4a\6\377^\217_\377" + "\371\373\371\377\202\254\204\377\3\\\12\377\1c\3\377\0f\0\377\0n\0\377" + "\0o\0\377\0p\0\377\0o\0\377\207\263\207\377\202\377\377\377\377\177\332" + "\354\332\377\4\217\4\377\0\221\0\377\0\252\0\377\0\271\0\377\0\275\0" + "\377\0\317\0\377\0\337\0\377'\356,\377\332\372\335\377\377\377\377\377" + "\371\376\371\377\10\341\24\377\0\256\2\377\0\231\10\377\0\204\0\377\0" + "y\5\377\0z\22\377\0}\5\377\0d\4\377\7r\15\377\333\352\334\377\336\355" + "\340\377\35\211$\377\0w\14\377\0u\11\377\0x\16\377\0}\24\377\0y\31\377" + "\0s\17\377\0\204\31\377\0\210\36\377\0\245'\377\0\314=\377<\344v\377" + "\352\375\357\377\15\345H\377\0\254<\377\0\226*\377\0|7\377\0pD\377\0" + "\200I\377\0q;\377\0j<\377\0m6\377\0iE\377\0qP\377\0zO\377\0{D\377\0x" + ">\377\0hD\377\0e;\377\0f\77\377\0gD\377\0_L\377\0fT\377\0ZD\377\0^\77" + "\377\0rL\377\0uQ\377\0_\215\377\0KZ\377\0""3G\377\0:R\377\200\11""2\377" + "p\0\34\377Z\0\1\377T\0\4\377T$\27\377R-\7\377O+\21\377\\-\21\377Z&\10" + "\377U5\13\377e@\32\377]2\22\377Z0\34\377V*\36\377V$\23\377]&\23\377T" + "'\17\377T#\0\377K(\0\377G\37\4\377F)\2\3778*\0\377B$\11\377A&\0\3772" + "%\0\377\77+\0\377;*\0\377(:\0\377/:\0\3778/\0\37711\0\37737\0\377\16\0\3777\7\0\3770\6\0\377%\12\0\377)" + "\0\0\377\247c\212\377\316\214\243\377\314\213\237\377\323{\255\377\311" + "\201\242\377\313x\251\377\302w\242\377\273w\251\377\301w\233\377\305" + "s\226\377\273f\201\377\303g{\377\302cy\377\275e\212\377\310x\212\377" + "\315n\202\377\304s\217\377\267x\212\377\274~\211\377\304\213\232\377" + "\314\204\240\377\301s\257\377f\275u\237\377\302\177\231\377\267w\227" + "\377\300q\244\377\302}\240\377N\0%\377\12\0\0\377\17\2\0\377\3\0\0\377" + "\37\0\6\377\247\220\243\377\245\212\216\377\245\207\211\377\250\225\230" + "\377\263\232\235\377\246\232\241\377\250\227\226\377\232\223\217\377" + "\227\231\225\377\202\220\227\377~\233\232\377y\232\220\377\177\225\214" + "\377}\224\221\377\200\232\213\377\202\241\215\377}\236\224\377\177\237" + "\211\377t\236\223\377r\227\226\377m\230\231\377g\234\237\377q\237\217" + "\377g\240\244\377^\217\221\377\0\0\0\377\0\11\0\377\0\13\0\377\0\25\0" + "\377\0\7\0\377\0\0\0\377W\234\232\377k\255\251\377l\253\254\377`\257" + "\246\377d\251\243\377`\254\253\377T\244\260\377J\243\264\377R\252\260" + "\377I\246\242\377C\261\244\377C\251\245\377\\\275\246\377N\273\246\377" + "H\274\243\377P\257\231\377G\271\232\377P\256\234\377O\266\216\377V\266" + "\221\377H\257\227\377Q\275\232\377M\254\222\377H\260\226\377\4fH\377" + "\0\0\0\377\0!\0\377\0\22\0\377\0\0\0\377\0\23\0\3779\261\203\377F\267" + "\216\377M\277\223\377\36iC\377\0\6\0\377\0\40\0\377\0)\0\377\0;\0\377" + "\0""1\0\377\0:\0\377\0<\0\377\0A\0\377\0:\0\377\0\33\0\377\3\2163\377" + "Y\350\203\377T\347}\377K\363\204\377N\345\220\377N\362\206\377U\370\203" + "\377V\375\226\377\0s\35\377\0)\0\377\0t\0\377\0\254\0\377\0\253\0\377" + "\0\254\0\377\0\237\0\377D\243D\377\365\372\365\377\202\377\377\377\377" + "\23\366\371\366\377\251\321\253\377\177\262\202\377\213\274\217\377\262" + "\317\263\377\353\362\353\377\202\254\203\377\15c\15\377\1d\1\377\2e\4" + "\377\0_\0\377\0Z\0\377\0]\0\377\0m\0\377\0q\0\377\0o\0\377\0k\0\377\0" + "d\0\377y\250y\377\203\377\377\377\377\7\341\361\341\377\241\324\241\377" + "\204\310\204\377\252\332\252\377\324\356\324\377v\330v\3776\3077\377" + "\202\377\377\377\377\7q\331\207\377\0\300)\377\0\255\26\377\0\232\15" + "\377\0\226\12\377\16\207\26\377\341\356\342\377\203\377\377\377\377\1" + "\376\376\376\377\206\374\375\374\377\206\377\377\377\377\177\365\374" + "\367\377\35\333Y\377\0\340M\377\0\315D\377\0\265O\377\0\236F\377\0\211" + "J\377\0nE\377\0n\77\377\0k[\377\0zO\377\0}H\377\0\177S\377\0zK\377\0" + "vC\377\0s8\377\0gG\377\0pO\377\0mT\377\0pP\377\0rS\377\0_Z\377\0PR\377" + "\0MI\377\0SR\377\0aL\377\0Y\220\377\0""2t\377\0%L\377\0*B\377\227\0E" + "\377j\0#\377Y\0\12\377W\0\12\377f\34\21\377a%\26\377d-\17\377i)\37\377" + "r3\20\377w*\34\377k6\33\377k0\37\377o2\26\377^-\20\377f\31\30\377l!\30" + "\377O\27\21\377L\33\17\377S\40\6\377X\31\15\377A\25\10\377K\20\14\377" + "M\40\2\377I\30\3\377H\37\0\377L*\0\3772'\0\37794\3\377<<\4\377E:\22\377" + "C7\12\377W8\10\377C3\1\377N*\0\377M3\10\377B-\0\377<&\0\377K$\0\377O" + "!\0\377Q(\0\377K\35\0\377=\22\0\377D\31\0\377C\31\0\377@\24\0\3775\17" + "\0\377<\7\0\3773\0\0\377+\10\0\377.\6\0\377%\0\0\377\40\0\0\377\32\0" + "\0\377k\16""4\377\253Zn\377\315\200\242\377\331{\250\377\323x\235\377" + "\312v\245\377\314p\234\377\315p\226\377\316y\217\377\306l\206\377\320" + "e\210\377\332^\211\377\324i|\377\327q\204\377\326n\207\377\320my\377" + "\320}\201\377\310\202\223\377\301^\205\377d\11$\377K\0\21\377\317z\246" + "\377\312s\230\377\312g\240\377\301s\232\377\274\201\237\377\230Bc\377" + "\11\0\0\377\13\13\0\377\12\1\0\377\5\0\0\377\277\205\225\377\271\207" + "\230\377\270\206\233\377\263\223\231\377\264\231\235\377\255\214\230" + "\377\242\232\235\377\222\222\224\377\227\223\227\377\225\206\242\377" + "\235\212\224\377\237\234\205\377\235\220\212\377\13\215\242\227\377}" + "\232\231\377\200\240\226\377\205\232\241\377\204\244\231\377z\253\236" + "\377u\252\223\377u\265\227\377y\256\242\377]\230\200\377*RS\377\202\0" + "\0\0\377\1\0(\0\377\202\0%\0\377^\0!\0\377\0\0\0\377\0\0\12\377$_d\377" + "U\232\220\377x\272\256\377p\244\251\377t\253\261\377d\267\261\377^\263" + "\251\377V\266\264\377T\277\245\377W\265\254\377`\273\246\377O\270\246" + "\377M\305\242\377Y\267\246\377V\266\235\377\\\274\244\377\0RA\377\0\6" + "\0\377X\305\234\377O\301\236\377R\266\235\377M\270\243\377W\256\226\377" + "B\254\204\377\0\0\0\377\0\22\0\377\0\2\0\377\0P<\377Z\304\235\377Z\300" + "\246\377a\303\227\377\40\202N\377\0\7\0\377\0\33\0\377\0""6\0\377\0." + "\0\377\0C\0\377\0@\0\377\0""0\0\377\0<\0\377\0""9\0\377\0""7\0\377\0" + "3\0\377\0\24\0\377(\256Y\377N\353\233\377Y\344\217\377k\361\177\377`" + "\366\214\377b\370\213\377X\366\200\377X\370\207\377\0\203\32\377\0R\0" + "\377\0\234\0\377\0\261\0\377\0\251\0\377\0\236\0\377\0\222\0\377#\224" + "#\377\230\307\232\377\335\353\336\377\370\373\370\377\357\366\357\377" + "\323\347\325\377\252\315\255\377[\241`\377\17p\21\377\1j\1\377\0`\0\377" + "\0f\0\377\0[\0\377\0^\0\377\0a\0\377\0j\0\377\0i\0\377\0c\0\377\0d\0" + "\377\0i\0\377\0b\0\377\0n\0\377Q\232Q\377\274\332\274\377\357\366\357" + "\377\364\371\364\377\334\355\334\377\245\321\246\377S\266V\377\4\232" + "\7\377\0\246\24\377d\311m\377\202\377\377\377\377e'\276C\377\0\254\40" + "\377\0\243\21\377\0\242\26\377\0\222\21\377w\274\202\377\330\351\335" + "\377\330\352\334\377\330\350\332\377\330\350\333\377\330\350\334\377" + "\330\350\332\377\330\352\333\377\330\354\334\377\333\353\337\377\341" + "\355\343\377\347\361\352\377\355\364\357\377\362\366\364\377\370\372" + "\371\377\376\376\376\377\370\373\371\377\320\352\331\377F\265r\377\0" + "\307A\377\0\313U\377\0\276V\377\0\267a\377\0\245M\377\0\211U\377\0uF" + "\377\0}K\377\0t[\377\0dF\377\0vW\377\0x:\377\0mP\377\0m;\377\0sG\377" + "\0dN\377\0iH\377\0hX\377\0gU\377\0`f\377\0hZ\377\0^_\377\0P]\377\0TQ" + "\377\0_C\377\0N\202\377\0*n\377\0\32P\377\0\36M\377\204\5R\377m\0""1" + "\377Y\0\7\377O\0\13\377R\30\37\377g)\36\377n#!\377x\32""4\377v\36\25" + "\377t$\26\377n1\"\377z\40\33\377g'$\377o\34\32\377u\23\30\377m\30\22" + "\377[\24\32\377b\34\"\377W\24\25\377_\26\14\377X\25\22\377X\30\10\377" + "T\20\7\377J\22\0\377D\30\2\377L#\0\377U*\4\377P<\6\377S1\15\377O5\3\377" + "W/\13\377V/\4\377Q&\12\377[+\20\377I,\24\377E)\4\377W$\1\377S(\0\377" + "X\"\5\377\\\27\0\377E\14\0\377>\12\0\377M\6\0\377B\5\0\377H\4\0\377H" + "\3\0\377=\0\0\3778\2\0\377\202.\0\0\377#1\0\0\3775\0\0\377%\0\0\377\34" + "\0\0\377\33\0\0\377#\0\0\377(\0\0\377\36\0\0\377\27\0\0\377\37\0\0\377" + "\24\0\0\377\21\0\0\377'\0\0\377$\0\0\377\40\0\0\377\37\0\0\377!\0\0\377" + "\15\0\0\377\26\0\0\377\35\0\0\377\20\0\0\377\30\0\0\377\40\0\0\377\"" + "\0\0\377\30\0\0\377\35\0\0\377\11\0\0\377\12\0\0\377\17\0\0\377\30\0" + "\0\377\35\0\0\377\4\0\0\377\15\0\0\377\11\0\0\377\1\0\0\377\227\0\0\0" + "\377\10\0\17\0\377\0'\0\377\0\37\0\377\0""5\0\377\0+\0\377\0""1\0\377" + "\0/\0\377\0\26\0\377\221\0\0\0\377\1\0\10\0\377\207\0\0\0\377\3\0\32" + "\0\377\0(\0\377\0\13\0\377\203\0\0\0\377\11\0\7\0\377\0\30\0\377\0""5" + "\0\377\0""2\0\377\0+\0\377\0G\0\377\0>\0\377\0@\0\377\0<\0\377\202\0" + "9\0\377\15\0%\0\377\0#\0\377\0\27\0\377\0\34\0\377\0\30\0\377\0\26\0" + "\377\0\20\0\377\0\27\0\377\0\17\0\377\0\31\0\377\0@\0\377\0x\0\377\0" + "\241\0\377\202\0\266\0\377D\0\233\0\377\0\200\0\377\0\204\0\377\0q\1" + "\377\1y\6\377\5u\16\377\5w\24\377\7p\22\377\4m\12\377\1r\4\377\0e\1\377" + "\0n\0\377\0i\0\377\0h\0\377\0f\0\377\0b\0\377\0j\0\377\0k\0\377\0e\0" + "\377\0f\0\377\0e\7\377\0g\6\377\0n\0\377\0o\0\377\0n\6\377\0a\7\377\0" + "t\7\377\0x\5\377\0}\27\377\0x\27\377\0|\21\377\0\202\12\377\0\216\"\377" + "\242\331\256\377\377\377\377\377\335\363\342\377\1\2402\377\0\2323\377" + "\0\221\23\377\0\2261\377\0\207-\377\0}0\377\0\2000\377\0\200\31\377\0" + "\177\16\377\0\203\33\377\0m&\377\0l&\377\0~1\377\0|%\377\0o(\377\0m3" + "\377\0q6\377\0c+\377\0i2\377\0i(\377\0p9\377\0`*\377\0f1\377\0\202>\377" + "\0\245K\377\0\277\\\377\1\274^\377\0\263]\377\0\230Q\377\0\227Z\377\0" + "\210P\377\0\203Q\377\202\0mO\377L\0uN\377\0jQ\377\0zK\377\0mG\377\0e" + "Q\377\0dR\377\0cZ\377\0lV\377\0VW\377\0SX\377\0X\\\377\0[g\377\0b^\377" + "\0[S\377\0UM\377\26G\207\377\0/n\377\0\16O\377\0\23X\377\214\0N\377n" + "\0.\377B\0\34\377\77\0\26\377`\32\37\377T\31\24\377k\40\36\377}\27""1" + "\377{\36'\377s+\33\377l%\36\377o\37+\377o\35#\377e\36%\377o&\33\377i" + "\22!\377e\23\17\377g\6\40\377a\21\32\377a\15\35\377_\16\16\377X\31\27" + "\377Q\22\7\377S\27\0\377U\34\0\377O$\2\377M'\6\377R4\4\377P6\1\377R6" + "\15\377V%\5\377e.\21\377\\4\0\377X7\4\377U'\4\377X\30\0\377S\23\0\377" + "Y\31\0\377W\25\0\377M\12\0\377L\12\0\377Q\0\0\377G\0\0\377L\0\0\377C" + "\0\0\377>\3\0\377D\2\0\377=\0\0\3775\0\0\3774\0\0\3776\0\0\3775\0\0\377" + "%\0\0\377\35\0\0\377/\0\0\3775\0\0\377*\0\0\377\202$\0\0\377\7\37\0\0" + "\377\31\0\0\377!\0\0\377-\0\0\377*\0\0\377&\0\0\377(\0\0\377\202$\0\0" + "\377\25!\0\0\377\33\4\0\377\31\0\0\377&\0\0\377+\14\0\377)\0\0\377$\7" + "\0\377\35\14\0\377\17\0\0\377\14\0\0\377\33\1\0\377\37\0\0\377\15\0\0" + "\377\24\1\0\377\17\12\0\377\0\0\0\377\0\2\0\377\0\0\0\377\7\6\0\377\1" + "\21\0\377\0\15\0\377\210\0\0\0\377\25\0\4\0\377\0\6\0\377\0\24\0\377" + "\0\7\0\377\0\22\0\377\0\21\0\377\0\32\0\377\0\30\0\377\0\34\0\377\0)" + "\0\377\0$\0\377\0\31\1\377\0""3\0\377\0.\0\377\0""8\0\377\0;\0\377\0" + "8\0\377\0.\0\377\0""2\0\377\0@\0\377\0""0\0\377\202\0""6\0\377\22\0""1" + "\1\377\0)\0\377\0-\0\377\0""8\0\377\0""6\0\377\0""5\0\377\0""8\0\377" + "\0""9\0\377\0""1\0\377\0""7\0\377\0,\0\377\0\40\0\377\0,\0\377\0""3\0" + "\377\0""2\0\377\0""8\0\377\0""0\0\377\0,\0\377\204\0""5\0\377\177\0:" + "\0\377\0-\0\377\0""5\0\377\0'\0\377\0%\0\377\0)\0\377\0""5\0\377\0:\0" + "\377\0E\0\377\0""7\0\377\0E\0\377\0""9\0\377\0<\0\377\0;\0\377\0<\0\377" + "\0*\0\377\0""7\0\377\0;\0\377\0D\0\377\0T\0\377\0D\0\377\0L\0\377\0O" + "\0\377\0M\0\377\0C\0\377\0Q\0\377\0n\0\377\0\213\0\377\0\264\0\377\0" + "\273\0\377\0\255\0\377\0\206\0\377\0~\0\377\0z\0\377\0{\0\377\0\202\0" + "\377\0z\0\377\0r\0\377\0e\0\377\0j\0\377\0_\0\377\0a\0\377\0n\0\377\0" + "e\0\377\0T\0\377\0\\\0\377\0a\0\377\0\\\0\377\0K\0\377\0`\0\377\0\\\0" + "\377\0]\0\377\0\\\0\377\0k\1\377\0h\3\377\0{\20\377\0x\20\377\0{\14\377" + "\0x\26\377\0w\12\377\0{\10\377\0z\37\377\0\204%\377\0\177%\377\341\362" + "\345\377\377\377\377\377\220\327\240\377\0\234+\377\0\216\34\377\0\207" + "$\377\0\206*\377\0\2027\377\0\207,\377\0~/\377\0\207!\377\0\2046\377" + "\0t1\377\0i%\377\0r.\377\0s:\377\0r=\377\0p$\377\0i+\377\0t)\377\0r'" + "\377\0h\40\377\0h%\377\0o1\377\0e+\377\0c$\377\0~;\377\0\215J\377\0\250" + "N\377\5\232f\377\0\244N\377\0\221N\377\0\230Z\377\0\223Q\377\0\203G\377" + "\0rV\377\0kT\377\0rR\377\0eQ\377\0rU\377\0oW\377\0lM\377\0c[\377\0^[" + "\377\0Yc\377\0G]\377\0CY\377\0JS\377\0aI\377\0bR\377\0UP\377\0NN\377" + "\26\77~\377\0""1f\377\0\23M\377\0\24Y\377\207\0E\377f\0'\377V\0\17\377" + "T\0\26\377e\25\36\377S\35\37\377q\24\35\377&y\31%\377e'\30\377e+\35\377" + "_!\31\377b$\37\377_&\23\377_-\30\377Y\31\16\377\\\36\21\377_\30\25\377" + "`\7&\377\\\26#\377g\35\15\377m\25\21\377^\20\0\377W\23\0\377L\30\0\377" + "D+\1\377E0\0\377Z2\0\377^,\0\377T.\0\377S0\0\377^.\0\377e.\0\377\\(\0" + "\377[\25\0\377`\36\0\377P!\0\377K\23\0\377Z\31\0\377T\25\0\377D\25\0" + "\377Q\20\0\377I\1\0\377M\4\0\377B\5\0\377G\10\0\377\202;\0\0\377\10""6" + "\3\0\3772\0\0\3775\0\0\377/\0\0\377'\0\0\3773\0\0\377+\0\0\3772\0\0\377" + "\202/\0\0\377\32,\0\0\377#\0\0\377$\0\0\377!\0\0\377.\0\0\377-\0\0\377" + "+\0\0\377-\0\0\377(\0\0\377)\0\0\377\40\1\0\377#\10\0\377\31\0\0\377" + "0\5\0\377!\1\0\377\36\0\0\377\21\0\0\377\35\11\0\377\26\4\0\377\6\17" + "\0\377\22\0\0\377\1\0\0\377\26\0\0\377\17\7\0\377\12\12\0\377\15\0\0" + "\377\202\0\0\0\377\2\10\0\0\377\3\7\0\377\204\0\0\0\377\1\0\12\0\377" + "\204\0\0\0\377\202\0\3\0\377\15\0\24\0\377\0\12\0\377\0\13\0\377\0\6" + "\0\377\0\11\0\377\0\20\0\377\0\10\0\377\0\14\0\377\0\26\0\377\0\35\0" + "\377\0+\6\377\0$\0\377\0""2\0\377\202\0/\0\377\5\0:\0\377\0>\0\377\0" + "<\0\377\0=\2\377\0@\0\377\202\0<\0\377\13\0""6\0\377\0""1\0\377\0""2" + "\0\377\0""1\0\377\0>\0\377\0:\0\377\0D\0\377\0<\0\377\0K\0\377\0G\7\377" + "\0<\0\377\202\0:\0\377\177\0""9\0\377\0;\0\377\0-\0\377\0<\0\377\0;\0" + "\377\0""1\0\377\0,\0\377\0""3\0\377\0E\0\377\0C\0\377\0>\0\377\0""6\0" + "\377\0;\0\377\0""7\0\377\0""4\0\377\0""8\0\377\0<\0\377\0G\0\377\0E\0" + "\377\0A\0\377\0O\0\377\0C\0\377\0;\0\377\0""6\0\377\0""2\0\377\0;\0\377" + "\0@\0\377\0E\0\377\0G\0\377\0N\0\377\0G\0\377\0H\0\377\0>\0\377\0I\0" + "\377\0Y\0\377\0n\0\377\0\204\0\377\0\252\0\377\0\263\0\377\0\244\0\377" + "\0\220\0\377\0\201\0\377\0\200\0\377\0y\0\377\0i\0\377\0X\0\377\0M\0" + "\377\0L\0\377\0V\0\377\0c\0\377\0b\0\377\0U\0\377\0B\0\377\0V\0\377\0" + "_\0\377\0c\0\377\0L\0\377\0a\0\377\0Q\0\377\0T\0\377\0W\0\377\0U\0\377" + "\0^\0\377\0^\4\377\0g\2\377\0l\0\377\0j\13\377\0k\13\377\0v\11\377\0" + "t\26\377\0p!\377\0q%\377\40\214A\377\377\377\377\377\373\374\373\377" + "\"\2157\377\0s\27\377\0t\32\377\0}\33\377\0t\26\377\0x.\377\0r-\377\0" + "z/\377\0pA\377\0l7\377\0lF\377\0\200:\377\0\2008\377\0h(\377\0g)\377" + "\0y,\377\0n(\377\0j\"\377\0r%\377\0f&\377\0u+\377\0l/\377\0s/\377\0i" + "\77\377\0sG\377\0}I\377\0\204N\377\0\177L\377\0\233J\377\0\226H\377\0" + "\216C\377\0~D\377\0xR\377\0pR\377\0u\\\377\0gY\377\0jR\377\0jV\377\0" + "nQ\377\0YM\377\0hM\377\0TT\377\0YL\377\0RS\377\0GI\377\0RQ\377\0cE\377" + "\0XW\377\0VK\377\0WQ\377\27Fy\377\0@`\377[\0\31L\377\0\22I\377|\0>\377" + "j\0\32\377L\0\14\377W\0\13\377T%\16\377Y\40\16\377`\40\14\377]#\24\377" + "\\/\23\377W4\31\377W>\33\377X8\35\377U3\20\377[-\21\377X1\32\377X\40" + "\24\377Y'\34\377O#\31\377Z\33\21\377_$\0\377`\27\2\377Z'\0\377O$\11\377" + "N(\16\377_)\0\377Y(\0\377Y)\0\377S)\0\377V,\0\377T!\0\377]2\0\377a.\0" + "\377]\"\0\377e0\0\377T\22\0\377J\16\0\377R$\0\377K\40\0\377J\26\0\377" + "J\21\0\377K\26\0\377E\13\0\377G\16\0\377O\30\0\377B\16\0\377G\14\0\377" + "5\0\0\3777\2\0\3770\0\0\377.\0\0\377)\0\0\3771\0\0\377/\0\0\3777\0\0" + "\377,\0\0\377#\0\0\377\40\0\0\377-\0\0\3772\0\0\3776\0\0\377(\0\0\377" + "4\0\0\377,\0\0\377-\0\0\377*\0\0\3773\0\0\377/\0\0\377\40\0\0\377\37" + "\0\0\377\34\0\0\377\40\0\0\377\23\0\0\377\26\0\0\377\23\0\0\377\10\14" + "\0\377\12\3\0\377\5\7\0\377\21\16\0\377\3\3\0\377\16\30\0\377\25\31\0" + "\377\6\21\0\377\15\11\0\377\5\0\0\377\0\13\0\377\0\6\0\377\2\0\0\377" + "\1\0\0\377\7\0\0\377\211\0\0\0\377\1\0\11\0\377\202\0\0\0\377\7\0\5\0" + "\377\0\16\0\377\0\10\0\377\0\0\0\377\0\11\0\377\0\22\0\377\0\36\0\377" + "\202\0\37\0\377\12\0#\0\377\0\33\0\377\0+\0\377\0.\0\377\0""5\12\377" + "\0\77\6\377\0\77\2\377\0=\0\377\0""8\0\377\0H\1\377\202\0H\0\377\10\0" + "0\4\377\0""8\0\377\0""5\0\377\0\77\0\377\0N\3\377\0\\\0\377\0W\0\377" + "\0Q\0\377\202\0M\0\377\10\0O\0\377\0""8\0\377\0)\0\377\0&\0\377\0'\0" + "\377\0/\0\377\0*\0\377\0(\0\377\202\0""9\0\377\5\0:\0\377\0A\0\377\0" + "D\0\377\0@\0\377\0""5\0\377\202\0""9\0\377\35\0""6\0\377\0""5\0\377\0" + "B\0\377\0A\0\377\0L\0\377\0J\0\377\0D\0\377\0X\0\377\0N\0\377\0O\0\377" + "\0A\0\377\0J\0\377\0L\0\377\0O\0\377\0H\0\377\0""5\0\377\0""7\0\377\0" + ">\0\377\0R\0\377\0n\0\377\0\214\0\377\0\256\0\377\0\300\3\377\0\303\11" + "\377\0\240\1\377\0\204\0\377\0s\3\377\0Z\0\377\0\\\0\377\202\0J\0\377" + "\177\0C\0\377\0I\0\377\0@\0\377\0M\0\377\0@\0\377\0""8\0\377\0I\0\377" + "\0B\0\377\0G\0\377\0I\0\377\0>\0\377\0\77\0\377\0=\0\377\0J\0\377\0N" + "\0\377\0U\0\377\0S\2\377\0U\5\377\0]\17\377\0c\7\377\0\\\2\377\0i\30" + "\377\0f\27\377\0d'\377\0]\37\377_\240q\377\377\377\377\377\214\271\226" + "\377\0h\25\377\0p\23\377\0m\24\377\0l%\377\0q%\377\0d%\377\0e+\377\0" + "g\33\377\0e5\377\0j8\377\0{4\377\0z\"\377\0{-\377\0u)\377\0r\37\377\0" + "s#\377\0l1\377\0h/\377\0f\40\377\0f.\377\0h&\377\0r0\377\0b4\377\0]C" + "\377\0d;\377\0nB\377\0rE\377\0r>\377\0w4\377\0s>\377\0q4\377\0^5\377" + "\0nN\377\0hM\377\0fM\377\0cN\377\0lU\377\0eM\377\0\\P\377\0[H\377\0W" + "G\377\0XS\377\0PP\377\0NI\377\0UF\377\0X@\377\0\\<\377\0TJ\377\0FN\377" + "\0VJ\377\21G|\377\0-f\377\0\27K\377\0\14Q\377\210\2D\377a\0*\377N\0\10" + "\377N\0\4\377^$\35\377N)\26\377V3\26\377W>\24\377Z2\24\377[>\30\377e" + "9\23\377`D\20\377c8\26\377a\77\23\377S9\20\377N-\34\377U0\2\377Q/\14" + "\377\\.\11\377T\"\12\377U)\0\377]\37\11\377],\10\377g-\10\377c4\0\377" + "i8\0\377],\0\377`-\0\377T*\0\377[*\0\377f9\0\377g$\0\377\\$\0\377Y*\0" + "\377a$\0\377V\24\0\377O\27\0\377R%\0\377\\\31\0\377Q&\0\377P\26\0\377" + "N!\0\377Q\27\0\377O\24\0\377I\16\0\377\6D\13\0\377J\14\0\377@\4\0\377" + "2\0\0\3774\0\0\377.\0\0\377\202%\0\0\377\15$\0\0\377*\0\0\377'\0\0\377" + "5\0\0\3771\0\0\377)\0\0\377,\0\0\377*\0\0\377.\0\0\3772\0\0\3775\0\0" + "\377:\0\0\377*\0\0\377\202(\0\0\377\23*\0\0\377+\0\0\377*\0\0\377\34" + "\0\0\377\21\4\0\377\32\2\0\377\11\6\0\377\34\0\0\377\26\17\0\377\36\13" + "\0\377\30\15\0\377\25\25\0\377\16\31\0\377\16\36\0\377\13\24\0\377\2" + "\7\0\377\17\16\0\377\21\11\0\377\10\15\0\377\202\11\0\0\377\211\0\0\0" + "\377\3\0\2\0\377\0\0\0\377\0\1\0\377\202\0\0\0\377%\0\6\0\377\0\2\0\377" + "\0\14\0\377\0\12\1\377\0\16\3\377\0\7\0\377\0\14\0\377\0\36\0\377\0\27" + "\0\377\0!\1\377\0$\2\377\0""0\21\377\0+\17\377\0""3\33\377\0""3\20\377" + "\0=\12\377\0@\15\377\0B\10\377\0>\6\377\0,\12\377\0E\0\377\0""3\7\377" + "\0\77\0\377\0I\0\377\0Q\0\377\0P\0\377\0]\0\377\0R\0\377\0T\0\377\0A" + "\2\377\0""5\2\377\0\37\0\377\0(\0\377\0+\0\377\0(\0\377\0&\0\377\0'\0" + "\377\202\0""7\0\377\177\0""1\0\377\0-\0\377\0,\0\377\0-\0\377\0*\1\377" + "\0(\0\377\0""4\0\377\0""8\0\377\0""5\0\377\0D\0\377\0I\0\377\0G\0\377" + "\0U\0\377\0P\0\377\0Q\0\377\0K\0\377\0D\1\377\0N\0\377\0M\0\377\0S\0" + "\377\0""6\0\377\0""7\0\377\0;\0\377\0.\0\377\0\77\0\377\0e\0\377\0\211" + "\0\377\0\260\0\377\0\277\23\377\0\304\26\377\0\254\20\377\0\222\4\377" + "\0|\3\377\0j\0\377\0U\3\377\0I\0\377\0=\3\377\0=\0\377\0<\0\377\0D\0" + "\377\0""8\0\377\0\77\0\377\0\77\2\377\0A\0\377\0<\6\377\0K\0\377\0E\0" + "\377\0;\0\377\0""9\2\377\0""6\0\377\0M\0\377\0I\0\377\0D\0\377\0M\23" + "\377\0]\21\377\0^\7\377\0d\24\377\0V\15\377\0`\25\377\0c'\377\0Z\32\377" + "\0]\37\377\0`\31\377\271\322\300\377\312\332\314\377\7V\25\377\0]\35" + "\377\0g\37\377\0V\27\377\0H\30\377\0P\34\377\0i\35\377\0\\!\377\0`-\377" + "\0r1\377\0t\37\377\0y(\377\0d\40\377\0o\40\377\0q.\377\0t!\377\0p*\377" + "\0h*\377\0k!\377\0b\25\377\0Y#\377\0]*\377\0X*\377\0Y,\377\0N5\377\0" + "SB\377\0[:\377\0TF\377\0W3\377\0f>\377\0o@\377\0j@\377\0Y9\377\0[/\377" + "\0\\.\377\0c;\377\0]>\377\0X<\377\0f\77\377\0P=\377\0OC\377\0aE\377\0" + "X=\377\0QG\377\0MC\377\0SA\377\0LN\377\0IO\377\0GP\377\0IH\377\0BC\377" + "\15""1z\377\0\14U\377\0\0\77\377\0\16E\377\200\5F\377h\0\24\377I\0\0" + "\377E\0\0\377T5\7\377R9\33\377R5\26\377>U=\30\377`7\7\377`<\22\377`T" + "\30\377aO\17\377bG\26\377W6\34\377U.\20\377S/\0\377Y*\11\377Z)\3\377" + "X\"\0\377V2\0\377X\40\0\377[.\2\377`9\0\377l4\0\377g+\0\377_4\0\377n" + "4\0\377o8\0\377c;\0\377f*\0\377_\37\0\377a*\0\377V'\0\377^\40\0\377b" + "\"\0\377V\26\0\377V\34\0\377g\21\0\377M\27\0\377S\"\0\377S\24\0\377H" + "\35\0\377L\22\0\377L\27\0\377N\15\0\377G\24\0\377<\20\0\3778\17\0\377" + ".\4\0\377/\0\0\3772\5\0\3777\5\0\377;\0\0\3774\0\0\3778\0\0\3777\0\0" + "\3774\0\0\377.\0\0\377'\0\0\3776\0\0\377/\10\0\377/\0\0\3775\0\0\377" + "D\0\0\377;\2\0\377/\0\0\3778\0\0\377,\0\0\377-\0\0\377\202*\0\0\377\23" + ")\12\0\377&\7\0\377$\3\0\377+\10\0\377\27\10\0\377\35\14\0\377\24\23" + "\0\377\27\37\0\377\13\40\0\377\7$\0\377\12\32\0\377\6\14\0\377\23\2\0" + "\377\14\3\0\377\0\25\0\377\0\11\0\377\0\3\0\377\0\0\0\377\3\3\0\377\210" + "\0\0\0\377\1\0\4\0\377\205\0\0\0\377\40\0\4\0\377\0\10\2\377\0\0\15\377" + "\0\5\0\377\0\0\0\377\0\16\21\377\0\4\0\377\0\16\0\377\0\23\11\377\0\22" + "\20\377\0\"\11\377\0$\24\377\0'\20\377\0""1\5\377\0""4\20\377\0:\21\377" + "\0A\23\377\0D\17\377\0H\0\377\0=\0\377\0""9\3\377\0\77\3\377\0S\0\377" + "\0M\0\377\0R\0\377\0P\0\377\0Q\0\377\0I\0\377\0""5\0\377\0/\0\377\0""6" + "\0\377\0""4\0\377\202\0/\0\377\177\0)\0\377\0\27\0\377\0#\0\377\0""3" + "\0\377\0)\0\377\0-\1\377\0+\6\377\0(\10\377\0&\10\377\0(\1\377\0B\4\377" + "\0;\5\377\0""9\0\377\0E\0\377\0\77\0\377\0K\3\377\0D\3\377\0J\4\377\0" + "E\12\377\0K\16\377\0M\24\377\0E\0\377\0F\0\377\0I\0\377\0B\0\377\0E\0" + "\377\0S\10\377\0M\14\377\0]\0\377\0h\6\377\0\200\13\377\0\257\10\377" + "\0\313\20\377\0\320\30\377\0\264\16\377\0\231\30\377\0\200\16\377\0_" + "\11\377\0[\3\377\0U\23\377\0K\2\377\0@\0\377\0""8\0\377\0\77\0\377\0" + "8\0\377\0""5\2\377\0""8\0\377\0;\0\377\0B\0\377\0G\7\377\0=\6\377\0E" + "\0\377\0F\0\377\0J\0\377\0E\0\377\0S\0\377\0L\4\377\0_\3\377\0[\12\377" + "\0U\17\377\0S\14\377\0M\34\377\0Y\27\377\0b\23\377\0M\34\377\0[\34\377" + "<|Y\377\325\343\327\377\25O)\377\0I\31\377\0R\37\377\0A%\377\0""8\40" + "\377\0C*\377\0N)\377\0S(\377\0S*\377\0R\27\377\0Y\21\377\0Q\22\377\0" + "S\23\377\0b\36\377\0a\27\377\0b#\377\0c\"\377\0d\24\377\0g\30\377\0a" + "\37\377\0`\27\377\0b+\377\0W%\377\0]\"\377\0Y\37\377\0O'\377\0I5\377" + "\0Y*\377\0X0\377\0W)\377\0[@\377\0`@\377\0Z=\377\0V-\377\0\\/\377\0U" + "9\377\0R2\377\0K3\377\0B.\377\0C4\377\0XA\377\0UD\377\0E9\377\0EB\377" + "\0MD\377\0CB\377\0\2\0\377=\4\0\377A\0\0" + "\377;\5\0\3778\0\0\3773\7\0\377'\0\0\3779\0\0\3776\0\0\3772\0\0\3778" + "\1\0\3771\0\0\377)\21\0\377\35\15\0\377\32\13\0\377%\12\0\377\33\21\0" + "\377\22\31\0\377\23.\0\377\15!\0\377\24\31\0\377\7\20\0\377\0\27\0\377" + "\0\23\0\377\0\32\0\377\3\23\0\377\20\10\0\377\11\2\0\377\213\0\0\0\377" + "\3\0\6\0\377\0\0\0\377\0\12\0\377\202\0\0\0\377\177\0\1\6\377\0\10\7" + "\377\0\13\2\377\0\12\10\377\0\21\0\377\0\23\6\377\0\35\10\377\0\35\14" + "\377\0\20\1\377\0\30\15\377\0\24\7\377\0\26\16\377\0""2\12\377\0,\5\377" + "\0""5\16\377\0G\22\377\0V\21\377\0E\0\377\0C\15\377\0""6\11\377\0Q\5" + "\377\0J\0\377\0N\0\377\0P\5\377\0X\6\377\0S\0\377\0B\5\377\0#\4\377\0" + "-\0\377\0""4\12\377\0-\0\377\0)\0\377\0""3\0\377\0-\0\377\0""3\0\377" + "\0)\0\377\0&\0\377\0""0\0\377\0,\0\377\0,\21\377\0\37\14\377\0\40\11" + "\377\0""1\20\377\0""0\6\377\0""4\6\377\0<\2\377\0B\20\377\0""2\7\377" + "\0\77\7\377\0<\15\377\0=\17\377\0""5\12\377\0D\30\377\0H\16\377\0K\16" + "\377\0;\0\377\0:\16\377\0D\14\377\0F\21\377\0H\12\377\0N\16\377\0N\13" + "\377\0^\3\377\0}\2\377\0\247\30\377\0\273\21\377\0\267\37\377\0\247\40" + "\377\0\224\25\377\0h\7\377\0`\7\377\0\\\21\377\0M\20\377\0H\30\377\0" + "<\25\377\0""1\4\377\0>\1\377\0>\0\377\0+\7\377\0""1\0\377\0>\7\377\0" + "E\14\377\0I\14\377\0O\3\377\0P\23\377\0Q\21\377\0[\12\377\0h\0\377\0" + "d\0\377\0c\21\377\0`\33\377\0f\27\377\0X\20\377\0X\25\377\0N\24\377\0" + "Q\21\377\1U\34\377\23]0\377Z\221p\377\273\316\303\377\34Z3\377\0""9\25" + "\377\0Q\30\377\0J\31\377\0*#\377\0-\37\377\0/&\377\0.#\377\0@,\377\0" + "\77*\377\0B\25\377\0;\31\377\0K\25\377\0B\27\377\0Q\25\377\0Y\36\377" + "\0h\27\377\0]\33\377\0Z\35\377\0^\31\377\0b\37\377\0Q)\377\0U#\377\0" + "I\31\377\0N#\377\0V\40\377\0Q)\377W\0G)\377\0T6\377\0N1\377\0A4\377\0" + "VA\377\0R5\377\0M9\377\0Q4\377\0RB\377\0EH\377\0;C\377\0@C\377\0>9\377" + "\0KB\377\0@A\377\0\77""9\377\0Q=\377\0C9\377\0GB\377\0G@\377\0@A\377" + "\0\77E\377\0LD\377\0""3N\377\0""6@\377\0\30\0\377U\2\0\377" + "W\0\0\377S\0\0\377N\0\0\377I\14\0\377J\0\0\377S\0\0\377\202B\0\0\377" + "\37P\0\0\377U\0\0\377E\0\0\377\77\10\0\377D\4\0\377M\10\0\3776\4\0\377" + "7\0\0\377E\0\0\377@\1\0\3779\4\0\377E\14\0\3770\5\0\3774\0\0\377#\4\0" + "\377(\1\0\377\31\16\0\377\34\22\0\377\20\36\0\377\27-\0\377#\34\0\377" + "\35!\0\377\0\27\0\377\0%\0\377\0\24\0\377\0\23\0\377\0\17\0\377\21\13" + "\0\377\0\26\0\377\0\20\0\377\3\14\0\377\204\0\0\0\377\1\5\0\0\377\206" + "\0\0\0\377+\0\13\0\377\0\27\0\377\0\17\0\377\0\4\20\377\0\34\14\377\0" + "\30\20\377\0\21\14\377\0\31\14\377\0\24\0\377\0\30\0\377\0\21\2\377\0" + "\24\12\377\0\26\4\377\0#\12\377\0\36\13\377\0\33\11\377\0'\20\377\0<" + "\14\377\0:\16\377\0G\7\377\0H\20\377\0J\22\377\0G\21\377\0D\25\377\0" + "S\0\377\0b\16\377\0i\10\377\0Z\2\377\0Z\14\377\0""9\13\377\0\77\15\377" + "\0""7\21\377\0""3\25\377\0""5\1\377\0""1\0\377\0@\0\377\0""9\0\377\0" + "1\0\377\0\40\5\377\0""4\5\377\0(\3\377\0%\12\377\0#\11\377\202\0#\4\377" + "5\0.\21\377\0+\3\377\0""2\13\377\0/\22\377\0,\37\377\0.\25\377\0C\31" + "\377\0""9\22\377\0""4\11\377\0""5\11\377\0""7\12\377\0>\6\377\0@\10\377" + "\0""7\24\377\0A\25\377\0""3\7\377\0""7\0\377\0@\5\377\0E\5\377\0:\4\377" + "\0C\7\377\0U\0\377\0k\25\377\0\206\31\377\0\247!\377\0\251$\377\0\240" + "\7\377\0\204\16\377\0f\13\377\0V\14\377\0M\34\377\0C\37\377\0>\12\377" + "\0B\16\377\0@\12\377\0""8\14\377\0:\11\377\0""3\5\377\0""9\20\377\0@" + "\22\377\0L\22\377\0O\15\377\0M\16\377\0P\23\377\0a\25\377\0n\16\377\0" + "m\20\377\0o'\377\0l'\377\0m\34\377\0h\35\377F\211V\377\375\375\375\377" + "\203\377\377\377\377\177\374\375\374\377\221\263\235\377\10Z\33\377\0" + "K\26\377\0N\37\377\0B\40\377\0\77\37\377\0""8\"\377\0$\36\377\0\"-\377" + "\0\40\35\377\0&\33\377\0-)\377\0*.\377\0""1#\377\0F.\377\0<4\377\0U(" + "\377\0P!\377\0\\\37\377\0M\30\377\0V&\377\0S#\377\0\\)\377\0M!\377\0" + ">5\377\0D,\377\0I.\377\0>3\377\0;3\377\0""82\377\0@-\377\0EA\377\0PQ" + "\377\0O0\377\0H1\377\0<>\377\0""0\77\377\0ID\377\0FJ\377\0""5Q\377\0" + ">J\377\0""1E\377\0\77D\377\0""83\377\0""7.\377\0""6A\377\0D:\377\0C=" + "\377\0EA\377\0:L\377\0""6E\377\0""2>\377\0""9C\377\0""6G\377\0""2F\377" + "\0+k\377\0\1M\377\0\0""7\377\0\0@\377\230\0L\377\203\0\"\377Z\0\0\377" + "U\0\0\377lH\16\377wW\40\377xS\22\377rc\21\377`_\10\377nh\10\377ga\0\377" + "tY\0\377hI\0\377l5\0\377n6\0\377j'\0\377\200'\0\377x#\0\377s$\0\377m" + "\33\0\377c\24\0\377p\26\0\377l\34\0\377j!\0\377d\36\0\377_'\0\377g'\0" + "\377l1\0\377_'\0\377`2\0\377d4\0\377Z+\0\377h\31\0\377l\37\0\377X\32" + "\0\377b\35\0\377f!\0\377i#\0\377P&\0\377K\33\0\377J\25\0\377W\14\0\377" + "S\6\0\377O\16\0\377E\11\0\377Y\1\0\377k\24\0\377d\31\0\377S\12\0\377" + "]\22\0\377Q\0\0\377U\0\0\377Q\4\0\377N\21\0\377^\0\0\377g\0\0\377_\0" + "\0\377W\0\0\377^\0\0\377X\0\0\377I\0\0\377N\6\0\377W\5\0\377U\5\0\377" + "E\12\0\377=\1\0\377=\15\0\377\32E\4\0\377A\16\0\377<\7\0\377.\0\0\377" + "0\13\0\377/\2\0\377\36\2\0\377\30\2\0\377\5\20\0\377\6\30\0\377\30\25" + "\0\377\17\35\0\377\12\31\0\377\7\33\0\377\11\21\0\377\0\24\0\377\0\35" + "\0\377\0\36\0\377\3\24\0\377\0\15\0\377\0\27\0\377\0\10\0\377\0\2\0\377" + "\0\0\0\377\5\0\0\377\0\7\0\377\206\0\0\0\377\177\0\6\3\377\0\15\7\377" + "\0\27\5\377\0\11\11\377\0\30\16\377\0\26\31\377\0\17\13\377\0\20\6\377" + "\0\24\7\377\0\31\4\377\0\30\6\377\0\34\21\377\0\12\0\377\0\12\3\377\0" + "\"\0\377\0\"\2\377\0\"\14\377\0.\23\377\0:\5\377\0>\7\377\0""6\16\377" + "\0;\34\377\0H\17\377\0Q\20\377\0O\25\377\0Y\11\377\0\\\16\377\0d\21\377" + "\0c\26\377\0U\32\377\0U\37\377\0G\31\377\0""8\21\377\0""1\11\377\0;\20" + "\377\0=\17\377\0""2\1\377\0""6\4\377\0(\0\377\0#\3\377\0$\10\377\0*\24" + "\377\0'\27\377\0\"\17\377\0\40\34\377\0*\32\377\0*\20\377\0-\21\377\0" + "2\2\377\0""1\20\377\0&\20\377\0""4\13\377\0""1\26\377\0""4\36\377\0'" + "\33\377\0)\21\377\0""0\32\377\0B\21\377\0\77\16\377\0/\21\377\0""2\22" + "\377\0""2\5\377\0""3\1\377\0""0\2\377\0""5\7\377\0""8\24\377\0-\12\377" + "\0""7\7\377\0M\10\377\0N\21\377\0v\16\377\0\212\17\377\0\214\36\377\0" + "q\4\377\0i\20\377\0K\32\377\0L\33\377\0C!\377\0C\17\377\0""2\23\377\0" + "4\3\377\0=\14\377\0=\24\377\0:\21\377\0>\0\377\0B\16\377\0M\37\377\0" + "<\20\377\0G\17\377\0Z\12\377\0f\30\377\0}\34\377\0\221!\377\0\221.\377" + "\0\2216\377\0\2170\377\0s1\377\12l,\377\260\317\271\377\320\340\325\377" + "\316\336\321\377\257\305\265\377{\250\221\377\32fA\377\0M,\377\0R2\377" + "\0H,\377\0<3\377\0@\37\377\0<)\377\0""1,\377\0\32""5\377\0\37""0\377" + "\0!8\377\0!%\377\0%+\377\0$'\377\0""52\377\0""40\377\0:3\377\0H(\377" + "\0\\9\377\0K\40\377\0M%\377\0K(\377\0Z!\377\0J$\377d\0G\40\377\0H,\377" + "\0""5)\377\0:0\377\0+/\377\0""14\377\0:G\377\0AL\377\0KE\377\0E@\377" + "\0AA\377\0""7G\377\0""86\377\0@B\377\0:J\377\0""4N\377\0+I\377\0""2B" + "\377\0""1:\377\0""30\377\0/8\377\0""0>\377\0""48\377\0""4:\377\0""3I" + "\377\0;@\377\0""5A\377\0@7\377\0.L\377\0""6D\377\0""1J\377\0""89\377" + "\1/j\377\0\34X\377\0\0\77\377\0\0A\377\216\1E\377t\0+\377^\0\0\377c\0" + "\2\377kP\14\377mG\32\377jY\22\377fP\15\377uW\0\377h\\\10\377|^\0\377" + "{L\0\377nD\0\377{3\0\377t:\0\377u\77\0\377y'\0\377w#\0\377i\25\0\377" + "m\32\0\377o\6\0\377_\5\0\377g\37\0\377m)\0\377a\34\0\377s!\0\377b&\0" + "\377e&\0\377Z+\0\377j\40\0\377c$\0\377g\32\0\377j\27\0\377[\37\0\377" + "`\33\0\377i\32\0\377^$\0\377[\31\0\377T%\0\377]\36\0\377X\23\0\377F\30" + "\0\377I\3\0\377R\1\0\377`\3\0\377\\\10\0\377^\20\0\377W\15\0\377`\12" + "\0\377Y\7\0\377e\1\0\377`\0\0\377~\24\5\377\246(W\377\2569R\377\2470" + "F\377\2302>\377l\20\0\377R\0\0\377\215\34(\377\230@Q\377\242AV\377\231" + "@K\377\2256\377\0""1+\377\0@\"\377\0B\40\377\0=,\377\0""8" + "-\377\0D.\377\0<\37\377\0D'\377\0@\37\377y\0""9/\377\0>0\377\0""5+\377" + "\0""72\377\0""04\377\0.6\377\0""6>\377\0<@\377\0DF\377\0OG\377\0=>\377" + "\0B4\377\0""4H\377\0+<\377\0-F\377\0'G\377\0+3\377\0)7\377\0""7:\377" + "\0""1\77\377\0""33\377\0/7\377\0'C\377\0.=\377\0*6\377\0B5\377\0F6\377" + "\0""2F\377\0""7K\377\0""8^\377\0FL\377\0AF\377\17""4k\377\0\21L\377\0" + "\0""2\377\0\0D\377\232\16:\377\202\0!\377e\0\2\377X\0\0\377s\77\15\377" + "rM\16\377pH\10\377lN\1\377p^\0\377yR\0\377~^\2\377vQ\0\377\177N\0\377" + "\177D\0\377~E\0\377\207\77\0\377w*\0\377s'\0\377i!\0\377t\34\0\377b\22" + "\0\377j\21\0\377p\34\0\377j\35\0\377l\32\0\377t\35\0\377[\33\0\377j(" + "\0\377m\"\0\377_%\0\377j\"\0\377m%\0\377^*\0\377f$\0\377j)\0\377c%\0" + "\377c*\0\377_\"\0\377`%\0\377V\21\0\377S\23\0\377e\6\0\377S\15\0\377" + "`\26\0\377j\34\0\377`\7\0\377_\6\0\377c\5\0\377_\3\0\377\\\10\0\377^" + "\7\0\377\200\36\3\377\260Ng\377\217/\32\377\216+\26\377\214-\40\377\205" + "0$\377e\3\0\377x%\3\377\253E}\377z3(\377y0,\377t7)\377{3B\377=\17\0\377" + "8\0\0\377\77\0\0\377F\0\0\377\\\21\0\377\232P\225\377z6>\377d0\"\377" + "c@,\377{Ys\377.#\0\377\32\32\0\3777;%\377zi\237\377\14\31\0\377\6\24" + "\0\377\10\13\0\377\3\12\0\377\3\2\0\377\4\13\0\377\1\0\0\377\203\0\0" + "\0\377\1\0\15\0\377\202\0\0\0\377\3\0\11\0\377\30.D\377\0\0\20\377\204" + "\0\0\0\377\177\0\16\0\377\0\7\10\377\0\21\13\377\0\10\20\377\0\33\12" + "\377\0\15\10\377\0*9\377\27Z\262\377\10N\203\377\0Fu\377\0!\33\377\0" + "\15\0\377\0\31\26\377\0\"\27\377\0\33\36\377\0\32\35\377\0\25\20\377" + "\0$\27\377\0&\33\377\0""1\"\377\0""7,\377\0B\35\377\0='\377\0.\30\377" + "\0""3\35\377\0QW\377\10\211\362\377\0u\244\377\0c\206\377\0d\222\377" + "\0\216\327\377\0gY\377\0RU\377\10y\333\377\0i\242\377\0X\234\377\0c\214" + "\377\0g\204\377\0""8(\377\0He\377\34m\317\377\0Jg\377\0Rv\377\0U\225" + "\377\7q\271\377\0)\13\377\0)\32\377\0(\27\377\0\26\32\377\0:W\3770x\361" + "\377\0X\214\377\0Tv\377\0Qt\377\0*G\377\0\"\26\377\0'\25\377\0)\24\377" + "\0'\30\377\0-\20\377\0$\14\377\0*\25\377\0/\22\377\0,\11\377\0#\25\377" + "\0\37\5\377\0#\7\377\0""3\22\377\0B\2\377\0K\15\377\0V\27\377\0^\33\377" + "D\213\334\377\27|x\377\0L\30\377\0I\"\377\0""6\15\377\0:\15\377\0""4" + "\20\377\0'\25\377\0""5\23\377\32hz\377V\235\342\377\0:\23\377\0;\21\377" + "\0""9\36\377\0""8\"\377\0>%\377\0J\35\377\0R\34\377\0d\27\377\0r!\377" + "\0\207#\377\0\237%\377\11\2709\377\3\253<\377\0\230;\377\0\2129\377\0" + "l2\377\0_\25\377\0Q\24\377\0T\32\377\0F\34\377\0T\34\377\0D(\377\0E1" + "\377\0=)\377\0>%\377\0:(\377\0*+\377\0-.\377\0(*\377\0\37\"\377\0&%\377" + "\0,\34\377\0\36&\377\0*%\377\0&/\377\0&!\377\0-#\377\0\77!\377\0""1)" + "\377\0""5#\377\0""70\377\0""8*\377\0))\377\0""5!\377\177\0""7)\377\0" + ",%\377\0""11\377\0""5#\377\0\40'\377\0/2\377\0;9\377\0\377\0" + "%H\377\0)8\377\0\37""9\377\0(=\377\0*A\377\0/\77\377\0)7\377\0)A\377" + "\0.9\377\0""5>\377\0""8B\377\0\77E\377\0MI\377\0]L\377\0SH\377\0IM\377" + "\16)o\377\0\13O\377\0\0/\377\0\0""3\377\223\11""7\377v\0\34\377\\\0\0" + "\377J\0\0\377h1\1\377j4\5\377\\:\0\377ZF\0\377\\M\0\377[W\1\377pN\0\377" + "qU\0\377|V\0\377\203Q\0\377\204Q\0\377{A\0\377\2027\0\377\1771\0\377" + "v#\0\377t\32\0\377e\21\0\377k\21\0\377w\30\0\377s\36\0\377x\32\0\377" + "k&\0\377j\27\0\377c%\0\377d-\0\377e'\0\377a0\0\377u6\0\377j;\0\377i<" + "\0\377i,\0\377`-\0\377e.\0\377Y\26\0\377a\36\0\377\\\33\0\377c\22\0\377" + "b\31\0\377Y\14\0\377a\33\0\377V\24\0\377S\12\0\377_\1\0\377W\5\0\377" + "K\13\0\377T\3\0\377\\\30\0\377\243Ib\377\23389\377Y\15\0\377S\27\0\377" + "V\26\0\377E\15\0\377N\2\0\377{'$\377\277O\213\377I\0\0\377C\2\0\377A" + "\10\0\377F\0\0\3777\0\0\377:\0\0\377\77\10\0\377;\10\0\377J'\17\377\231" + "k\233\377\77&\0\3770\24\0\3770\31\0\377\223o\232\377F,\0\377\23\27\0" + "\377K\77\77\377\212f\246\377\20\7\0\377A6.\377\"\30\0\377\0\0\0\377(" + ")\17\377\37\27\"\377\3\0\0\377\6\15\0\377(0<\377+,G\377\26""4A\377\20" + "&)\377\177\0\3\0\377\0\20\0\377\21/G\377\0\13\7\377\0\22\34\377\16/T" + "\377\33""7O\377\26:N\377\0\36#\377\0\11\0\377\0\21\0\377\0\11\22\377" + "\0\15\7\377\0\11\0\377\14N\213\3778q\345\377\6K\217\377\0\37\7\377\0" + "&\0\377\1Mr\377\7O\201\377\0D\200\377\5L\202\377\0\35\37\377\0#\30\377" + "\0Ii\377\0Mn\377\0c\226\377\0e\221\377\0@H\377\0""93\377\0""44\377\0" + "./\377\0JP\377\32\216\364\377\0Md\377\0""9,\377\0""1%\377$\215\372\377" + "\0^i\377\0[p\377\33\225\376\377\0Tg\377\0=6\377\0""5\37\377\0""4\27\377" + "\0""1\23\377\0;R\377\0It\377\0\33\13\377\0\35\36\377\0""7M\377%|\364" + "\377\0\37#\377\0\34\23\377\0\26#\377\0\36\22\377\0:X\377Cw\347\377\0" + "%H\377\0\24\7\377\0\27\20\377\0\32\12\377\0\40""1\377\17Gx\377\14Y\201" + "\377\1S\202\377\0I[\377\5Uw\377\12f\204\377\2o\177\377\0""0\0\377\0)" + "\11\377\0HU\377\0OP\377\0(\22\377\0<<\377\31d\202\377\0""2\11\377\0:" + "\26\377I\224\336\377\11t\203\377\0Q\25\377\0TR\377\15\\x\377\20k\202" + "\377\0Pl\377\0,\23\377\0<,\3779\213\261\377J\233\340\377\1Sc\377\0""4" + "\36\377\0H6\377(o\215\377\37m\217\377!k\221\377\30sy\377\0m!\377\3\202" + "B\377,\233\204\377\37\245p\3776\265\231\377(\246\206\377\0\235F\377\5" + "\203A\377\26\211\213\377\26\210w\377\35w\211\377\0G)\377\0P(\377\0G+" + "\377\0C\36\377\0""5#\377\0;\36\377\0D+\377\0-\36\377\0\40\35\377\0\37" + "(\377\0\32\35\377\0&\21\377\0,\25\377\0\40\35\377\0\33$\377\0$0\377\0" + "%'\377\0/%\377m\0""2\35\377\0""3\34\377\0""0)\377\0""02\377\0""10\377" + "\0,%\377\0""5'\377\0$)\377\0)1\377\0'!\377\0$)\377\0-,\377\0(.\377\0" + "&5\377\0""8\77\377\0""8F\377\0>D\377\0""4G\377\0>\77\377\0.:\377\0!=" + "\377\0,J\377\0#:\377\0%@\377\0#>\377\0\31;\377\0\"R\377\0+\77\377\0&" + "8\377\0&;\377\0""3@\377\0&D\377\0!9\377\0/7\377\0""3E\377\0""0F\377\0" + "BL\377\0[L\377\0[O\377\0[X\377\0SK\377\0+u\377\0\4Q\377\0\0.\377\0\0" + "8\377\215\2""7\377[\0\25\377M\0\1\377I\0\0\377V-\6\377T%\0\377K/\0\377" + "]:\0\377S\77\0\377aC\0\377gO\0\377eX\0\377tQ\0\377|f\0\377\200T\0\377" + "~W\0\377\205V\0\377\206M\0\377u/\0\377l\40\0\377n\36\0\377v\22\0\377" + "\203\23\0\377\200\22\0\377m\17\0\377j\36\0\377g\40\0\377g\36\0\377h)" + "\0\377Z,\0\377c3\0\377fL\0\377m7\0\377gD\0\377f>\0\377Z/\0\377^;\0\377" + "X)\0\377\\$\0\377H\37\0\377S(\0\377L\25\0\377Q\31\0\377N\20\0\377P\11" + "\0\377A\14\0\377@\26\0\377.\12\0\377:\21\0\377<\34\0\377b+\0\377\301" + "b\224\377l&\0\377\77\23\0\377K\37\0\377=\35\0\377\77\15\0\377A\1\0\377" + "x31\377\270Z\234\3777\25\0\377G\11\0\3770\15\0\3772\11\0\377\2020\0\0" + "\377\177.\11\0\377(\24\0\377H&\17\377\251c\267\377P\37\22\377$\13\0\377" + ")\7\0\377\227p\270\3772\40\2\377\37\0\0\377S0J\377\215j\247\377\12\6" + "\0\377\177c\235\377G.-\377\2\2\0\377bF{\377WXz\377\2\0\0\377G@`\377X" + "b\221\377GZ~\377Qq\251\377;N\205\377\0\0\0\377\0%\26\377Wo\251\377\0" + "\32\11\377%.H\377\\y\267\377%Ps\377(W\224\377\36Z\200\377\0\30\12\377" + "\0\32\0\377\0\17\0\377\0\25\3\377\0\13\15\377,p\304\377C\177\336\377" + ",k\270\377\0\30\10\377\2""2M\377=}\324\377\35^\264\377\36`\265\377/v" + "\321\377\0>n\377\0\37\37\377\24c\314\377\37|\336\377\33\203\353\377\12" + "\177\310\377\0DT\377\0<0\377\0""9$\377\0""2(\377\0Bc\377+\226\376\377" + "\0\77g\377\0""3=\377\0""1$\3776\226\377\377\0Ee\377\0Wo\3777\225\377" + "\377\0Mf\377\0)&\377\0-\36\377\0*\26\377\0(\22\377\0.\34\377\0!\36\377" + "\0\35\"\377\0!=\377\22[\233\377(q\326\377\0\36!\377\0\12!\377\0\4!\377" + "\0\24\25\377\0""6S\377D\177\343\377\0.B\377\0\26\32\377\0\21\21\377\0" + "\20\11\377\0(M\377J\204\346\3775u\274\377,j\245\377=\177\303\3778\216" + "\312\3772\206\245\3779\212\274\377\24Zs\377\0\30\16\3777{\255\377G|\265" + "\377\0\23\31\377\35Tj\377\\\224\334\377\0\37\31\377\0""1\35\377V\222" + "\366\377!c\207\377\0""9\22\377\3OU\377-}\260\377.}\266\377N\211\320\377" + "\0CO\377\0""7@\377H\210\311\377R\227\375\377\35v\233\377\0,#\377,y\256" + "\377Y\214\342\377H\205\274\377B\207\260\377K\222\313\377\27i_\377!}_" + "\377c\246\346\377h\247\326\377U\260\306\377\177>\256\240\377\0\222#\377" + "E\223\243\377Z\236\323\377N\227\272\377Q\215\267\377\0J(\377\0<%\377" + "\0;(\377\0""0$\377\0-\31\377\0+\21\377\0!\32\377\0+%\377\0*\32\377\0" + "%\36\377\0\32.\377\0\40$\377\0\25*\377\0\6&\377\0\11,\377\0#!\377\0\31" + "4\377\0$+\377\0""2#\377\0-'\377\0/-\377\0--\377\0/,\377\0\"'\377\0,\30" + "\377\0\33$\377\0\40\36\377\0\36+\377\0#-\377\0\35""1\377\0.#\377\0\40" + "<\377\0,@\377\0,=\377\0;>\377\0>=\377\0:1\377\0)'\377\0+D\377\0+6\377" + "\0\26""6\377\0\26@\377\0\"\77\377\0""2:\377\0""3\77\377\0&\77\377\0""7" + "2\377\0""3\77\377\0""1G\377\0\40@\377\0""9M\377\0+<\377\0-;\377\0""8" + "\77\377\0FC\377\0ZK\377\0VN\377\0kJ\377\0kN\377\17'q\377\0\0X\377\0\0" + "@\377\0\0""7\377\177\4""2\377g\0\20\377E\0\0\377\77\0\0\377Q(\0\377K" + "9\0\377Q6\0\377L7\0\377U9\0\377YF\0\377L\77\0\377TT\0\377kL\0\377pV\0" + "\377od\0\377\200]\0\377il\0\377sX\0\377sJ\0\377h4\0\377p7\0\377n\"\0" + "\377k\16\0\377c\33\0\377m!\0\377e&\0\377`+\0\377\\7\0\377_/\0\377Y(\0" + "\377]<\0\377mA\0\377_4\0\377SB\0\377]I\0\377X\77\0\377_6\0\377Q8\0\377" + "C)\0\377E3\0\377A%\0\377D\34\0\377F\33\0\377=\30\0\377,\24\0\377)%\0" + "\377-\25\0\377,\32\0\377-\37\0\3774\37\0\377T%\0\377\261t\231\377P%\0" + "\377I,\0\377U\"\5\377a/\2\377U\77\0\377@\25\0\377\177n7\25\377\253y\232" + "\377V0\0\377T*\0\377U\40\26\3777\22\0\377A\5\0\3776\11\0\3772\14\0\377" + "4\11\0\377T%\21\377\250p\272\377_0=\377E\33\23\3776\16\30\377\211`\231" + "\377(\22\15\377\32\6\0\377U\77M\377\226x\250\377\23\23\0\377\232u\252" + "\377A@B\377\1\3\0\377hV|\377lV|\377\0\0\0\377\201w\260\377:\77C\377\0" + "\7\0\377Bm\212\377Ecv\377\0\4\0\377\0\"\7\377oz\254\377\0,\12\377\22" + "D@\377Xv\275\377\0\21\0\377\0,\15\377T}\260\377\0""3\"\377\0\40\0\377" + "\0\25\10\377\0\24\5\377\0\26\0\377\0F/\377V\213\333\377\0""0\77\377\0" + "\15\13\377(p\251\377,a\252\377\0\15\3\377\0\20\17\377\16F\200\377)k\316" + "\377\0\27\36\377\32s\306\3774\206\367\377\0L\201\377\0+/\377\0-<\377" + "\0""16\377\0')\377\0&,\377\0Dn\3779\231\377\377\0f\222\377\0Mr\377\0" + "Rs\3777\215\377\377\0\77g\377\0GZ\377+\231\362\377\4_\205\377\0\77k\377" + "\0L\\\377\0""9\77\377\0\34\27\377\0*\22\377\0!!\377\0\33\37\377\5Q\205" + "\3776t\334\377\0\26G\377\0\7\37\377\0\16)\377\0\14\36\377\0\11\40\377" + "\0""2C\377Z\204\350\377\6Kl\377\0-M\377\0\"8\377\0\21\30\377\0\"B\377" + "Y\211\371\377\0*F\377\0\23\17\377V\212\351\377\20Hy\377\0\22\27\377\24" + "Qx\377f\233\352\377\0\30\30\377Tu\261\377Sy\265\377\0\23\7\377'_~\377" + "o\227\350\377\0!\27\377\0)\35\377q\213\350\3772f\211\377\0,\37\377\0" + "\"\22\377\0\40\37\377\0\32\35\377[\244\370\377\25`\222\377\0$\"\377\35" + "i\222\377m\243\370\377\0$\40\377\0,(\377r\254\376\377\37k\205\377m\0" + "-\25\377\0""2&\377U\203\301\3777b\212\377\22be\377z\227\356\377c\243" + "\310\377\3\244(\377\0\230/\377\2}'\377\201\246\364\377;\177\223\377\0" + "A\33\377\2D#\377\0""3\36\377\0'\27\377\0*\6\377\0(\11\377\0&\35\377\0" + "\35\36\377\0.\37\377\0*&\377\0&<\377\0)<\377\0'/\377\0\25""1\377\0\22" + ")\377\0\21,\377\0\22,\377\0!.\377\0%7\377\0)0\377\0""17\377\0%7\377\0" + "32\377\0+#\377\0-*\377\0""1)\377\0%:\377\0(%\377\0#'\377\0.\"\377\0(" + "$\377\0\34.\377\0#%\377\0""1*\377\0(5\377\1-7\377\0""3K\377\0""0C\377" + "\0""10\377\0\40:\377\0!/\377\0'=\377\0\30:\377\0'4\377\0+3\377\0""1:" + "\377\0""3B\377\0\"@\377\0""0=\377\0+N\377\0-G\377\0-C\377\0""7C\377\0" + "(G\377\0$G\377\0(O\377\0;J\377\0=H\377\0PT\377\0]\\\377\0k[\377\23\14" + "}\377\0\0f\377\0\0=\377\0\0H\377\203\2""0\377e\0\26\377>\0\0\3777\0\0" + "\377A!\0\377@.\0\377B4\0\377M7\0\377L8\0\377PB\0\377RB\0\377];\0\377" + "OS\0\377ZW\0\377Zl\0\377es\0\377ag\0\377`g\0\377dT\0\377^J\0\377eG\0" + "\377YA\0\377T5\0\377Z$\0\377d5\0\377d+\0\377e6\0\377\\/\0\377Q,\0\377" + "Y)\0\377]1\0\377\\/\0\377\202O:\0\377\177S-\0\377V8\0\377S,\0\377J<\0" + "\377K4\0\377M8\0\377F+\0\377E(\0\3773\"\0\3770#\0\377,%\0\377.*\0\377" + "\35\40\0\377\40/\0\377'\33\0\3772\27\0\377R7\0\377\262m\240\377U+\2\377" + "\245\\i\377\302\204\255\377\306\200\250\377\305u\236\377_+\0\377;\25" + "\0\377\232ou\377\273y\260\377\320\177\266\377\305s\251\377{O>\3775\30" + "\0\3775\16\0\377,\10\0\377%\26\0\377E/\22\377\265j\273\377\254n\265\377" + "\251v\270\377\246p\254\377RFS\377\30\15\0\377&\27\0\377XQ6\377\233{\246" + "\377\3\4\0\377\220\205\261\377=F;\377\0\7\0\377wgt\377r]q\377\0\21\0" + "\377\214\177\266\377@DE\377\0\10\0\377eq\201\377]f\177\377\0\1\0\377" + "\12""0\21\377j\221\262\377\0;\10\377\33N<\377s\215\264\377\0\30\0\377" + "\0G\27\377p\226\301\377\0>\34\377\0$\0\377\0\35\0\377\0\33\0\377\0!\0" + "\377\0B0\377o\206\332\377\0""7=\377\0\"\0\377/u\267\3777`\275\377\0\1" + "\20\377\0\7\24\377\7G\213\377\37[\303\377\0\20(\377)p\317\377+f\313\377" + "\0\36/\377\0),\377\0)4\377\0-6\377\0%,\377\0""12\377\0Pz\377^\244\377" + "\377M\253\377\377O\253\377\377N\264\377\377\13p\264\377\0""6=\377\0""0" + "(\377\22q\221\377[\260\377\377^\240\377\377X\231\377\377\"s\270\377\0" + "+2\377\0!\34\377\0\36\36\377\0""2J\377i\215\377\377\32P\220\377\0\13" + "\34\377\0\17\32\377\0\16\27\377\0\12\20\377\0\12\31\377\0""3W\377j\201" + "\371\377m\214\363\377o\177\371\377Hk\277\377\0\12\7\377\0\35G\377h\215" + "\356\377\0#H\377\0\6\15\377n\212\342\377\25Q\210\377\0\16\15\377*J\206" + "\377g~\232\354\377\0\15\13\377Or\266\377Gt\267\377\0\22\34\3772c\215" + "\377w\243\370\377\0$\40\377\0**\377x\235\366\377.v\210\377\0%\26\377" + "\0""8*\377\6HY\377\5@I\377}\264\374\377(p\212\377\0-\37\377\"l\211\377" + "\177\264\377\377\0'\37\377\0&\40\377s\250\347\3776b\201\377\0\37'\377" + "\0\34.\377Y\211\307\377\377\0""62\377\0" + ":8\377\0""8/\377\0C9\377\0A)\377\0""68\377\0""5<\377\0""36\377\0/@\377" + "\0.8\377\0#1\377\0""1@\377\0\34>\377\3\32G\377\0&R\377\0$I\377\0/8\377" + "\12\40A\377\0,I\377\0\40A\377\0\37C\377\0\"F\377\0\22<\377\0\34B\377" + "\0\36S\377\0\27N\377\0\27F\377\0#F\377\0/H\377\0+S\377\0%R\377\0\36M" + "\377\0+G\377\0\36;\377\0\30K\377\0&W\377\0""4W\377\0J\\\377\0`d\377\0" + "hZ\377\36\13~\377\10\0^\377\0\0L\377\0\0K\377\207\0\35\377j\0\0\377\202" + "<\0\0\377\177T1\0\377O+\0\377F<\0\377D@\0\377G8\0\377E>\0\377G\77\0\377" + "\77@\0\377RJ\0\377VG\0\377Qg\0\377Wl\0\377ep\0\377`x\0\377Vn\0\377]j" + "\0\377^Z\0\377`J\0\377dC\0\377X8\0\377Z:\0\377V>\0\377P\77\0\377Y;\0" + "\377U>\0\377T@\0\377D6\0\377A/\0\377IA\0\377[;\0\377J8\0\377I-\0\377" + "H\77\0\377\77A\0\377SA\0\377WA\0\377>8\0\377:/\0\3773.\0\377E-\0\377" + "@$\0\3777\34\0\377\32&\0\377\36.\0\377*\40\0\377*#\0\377L6\0\377\304" + "\200\225\377E>\1\377\37!\0\377/\30\0\377J3\3\377\306y\257\377P-\11\377" + "#!\0\377\26\"\0\377'#\0\377+$\0\377T2\13\377\331\206\270\3777\27\0\377" + "\"\26\0\377*\31\0\377'\11\0\377Y1\1\377\277\203\265\377B/\15\377\20\14" + "\0\377\13\16\0\377\13\33\0\377\20\24\0\377\14\30\0\377LV5\377\230}\243" + "\377\0\22\0\377\252\215\256\377DX9\377\0\33\0\377uqw\377zkl\377\0\7\0" + "\377\252\203\246\377MH:\377\0\7\0\377ckt\377`p\177\377\0\1\0\377\5+\1" + "\377}\234\276\377\0""6\34\377#YF\377\212\237\271\377\0\32\0\377\1""6" + "*\377z\226\312\377\3N\"\377\0""7\0\377\0'\0\377\0/\0\377\0&\3\377\26" + "90\377x\241\347\377\0@A\377\0\20\12\377>h\275\377Ie\273\377\0\3%\377" + "\0\2\32\377\5A\210\377*n\300\377\0\5&\377:h\326\377;f\315\377\0\20\40" + "\377\0#+\377\0\34""0\377\0*.\377\0+,\377\0$<\377\0Hr\377t\241\377\377" + "\0Ug\377\0""00\377\0()\377\0/3\377\0*2\377\0.-\377\177\0.'\377\0!,\377" + "\0\30(\377\0\26""3\377u\227\377\377\0""6b\377\0\22)\377\0\7&\377^|\311" + "\377-M\225\377\0\24\35\377\0\5\32\377\0\7\"\377\0\0\40\377\0\3$\377\0" + "\17(\377\11*X\377\203\220\375\377\14$Q\377\0\3\35\377\0\14\21\377\0\15" + "\31\377\24-K\377y\207\355\377\2(G\377\0\0\31\377v\214\377\3777D\206\377" + "\0\5\24\377/O\211\377|\223\367\377\0\0\26\377So\310\377Qz\276\377\0\14" + "\"\3770h\214\377\200\244\377\377\0-\33\377\0.*\377y\252\377\377'j\224" + "\377\0""8\32\3770g\226\377\205\266\377\377\227\261\377\377\222\260\376" + "\3773i\205\377\0\36\34\377>k\217\377}\256\366\377\0+\27\377\0.\27\377" + "\210\256\352\377D\\\211\377\0!\30\377\0\13\"\377bn\274\377JK\221\377" + "$Te\377\225\244\373\377%xu\377\13\2137\377\13\2000\377\34l-\377<`X\377" + "\244\237\377\377\247\256\374\377\1""9#\377\0""4\26\377\0+\17\377\2\36" + "\23\377\7(\34\377\0\35\35\377\0\32\"\377\0""6\34\377\0C)\377\0""34\377" + "\0C\377\0D=\377\0+0\377\0*7\377\0\36\35\377\0#(\377\0).\377" + "\0""3*\377\0+:\377\0A<\377\0I6\377\0M\77\377\0Q:\377\0QA\377\0AG\377" + "\0""9B\377\0'7\377\0\37;\377\0\26""6\377\0\17\77\377\0\37""9\377\0\33" + "=\377\0\36P\377\0!A\377\1""1<\377\6%E\377\22#K\377\22/J\377\10\27M\377" + "\4\30I\377\0\24G\377\0\37\77\377\0\25B\377\0\32P\377\0\17G\377\0\27N" + "\377\0!J\377\4\36W\377\0(J\377\0\35M\377\0\31M\377\0\27>\377\0\24D\377" + "\0\16U\377\0\24W\377\0""1P\377\0""9R\377\0P_\377\0fV\377\177\23\0\207" + "\377\0\0a\377\0\0C\377\0\0""9\377\205\0#\377j\0\2\377P\0\0\377G\0\0\377" + "M.\0\377K*\0\377Q>\0\377O6\0\377S<\0\377H:\0\377\\I\0\377X\77\0\377O" + "H\0\377JJ\0\377TR\0\377`[\0\377RR\0\377`W\0\377`Y\0\377Mi\0\377Yd\0\377" + "SR\0\377QD\0\377\\O\0\377GH\0\377JO\0\377FB\0\377G;\0\377>3\0\377C:\0" + "\3777A\0\3775=\0\377F>\0\377L7\0\377='\0\3779/\0\377F/\0\377H/\0\377" + "I4\0\377;-\0\377>+\0\377B.\0\377;.\0\377;0\0\3772!\0\3770\32\0\377'%" + "\0\377!2\0\377!\31\0\377&\37\0\377S=\0\377\313\240\241\377\77@\0\377" + "!%\0\377\"&\0\377K4\0\377\313\234\246\377DI\0\377!)\0\377\40&\0\377\33" + "\27\0\377\36\24\0\377Z6\14\377\340\222\264\377+&\0\377.\23\0\377!!\0" + "\377\36%\0\3778/\0\377\275\221\260\37720\14\377\6\36\0\377\0\37\0\377" + "\5\32\0\377\21\40\0\377\0\27\0\377]N-\377\245\224\246\377\1\21\0\377" + "\241\224\260\377P[<\377\1\23\0\377\205ss\377tyi\377\0\32\0\377\236\225" + "\261\377T^2\377\0\33\0\377us|\377hx~\377\0\25\0\377\31""0\27\377\226" + "\245\312\377\15,\32\3775[O\377\202\242\311\377\0\30\0\377\13B\25\377" + "\220\253\315\377\22H&\377\0(\0\377\0'\0\377\0\32\0\377\0&\0\377\12@+" + "\377z\222\334\377\0+.\377\0\23\4\377Kl\260\3775j\262\377\0\6\12\377\0" + "\5\14\377\16@\212\3779c\320\377\0\0\37\377;m\300\377\77f\272\377\0\21" + "\35\377\0\35\"\377\0\36\40\377\0\36\25\377\0!\40\377\0\31!\377\177\0" + "7e\377~\250\377\377\3Gl\377\0%,\377\0""0+\377\0'#\377\0\34/\377\0'\34" + "\377\0'+\377\0).\377\0\36""6\377\0\13'\377\200\226\377\377\0""0b\377" + "\0\7\35\377Xt\310\377]w\304\377\0\14$\377\0\7\34\377\0\10\27\377\0\0" + "\27\377\0\0/\377\0\10$\377\0\0\34\377\16,\\\377\226\235\361\377\16$I" + "\377\0\23\22\377\0\11\20\377\0\25\16\377\16""9E\377\220\213\352\377\21" + "-L\377\0\12\3\377\215\224\362\377@F\203\377\0\0\1\377AH\220\377\221\222" + "\375\377\0\5\27\377g\204\314\377l{\313\377\0\12\37\377/\\\224\377\217" + "\245\377\377\0'1\377\0""06\377\221\270\377\377:o\223\377\36U[\377\230" + "\257\375\377\33Be\377\0-.\377\243\277\377\377Hi\217\377\0(\40\377:^\236" + "\377\237\267\377\377\0""5!\377\0(\30\377\231\266\377\377FZ\231\377\0" + "\33,\377\0\17%\377rz\323\377A_\234\377+S[\377\260\260\374\377:yc\377" + "\35u0\377\27\1770\377\33m0\377\14W&\377\1P\20\377Lj\221\377\241\257\377" + "\377\0,\36\377\0*\"\377\0\33\37\377\0\34#\377\0-*\377\0#1\377\0""50\377" + "\0)4\377\0\77""0\377\0D:\377\0A;\377\0<>\377\0<=\377\0/8\377\0""46\377" + "\0&2\377\0\27""5\377\0\37""3\377\0#9\377\0)8\377\0-D\377\0<=\377\0""9" + "D\377\0""9H\377\0>Q\377\0-G\377\2+:\377\2\30@\377\0\36@\377\0\30""2\377" + "\0\32/\377\0\37D\377\0\37B\377\10%8\377\1#\77\377\11':\377\4.A\377\11" + "\36F\377\20(=\377\7\34D\377\21\35=\377\12\27""9\377\5\22@\377\6\17Q\377" + "\0\23W\377\0\15O\377\6\37C\377\5\36O\377\0\13C\377\0\27C\377\0\15B\377" + "r\0\26P\377\0\17V\377\0\31K\377\0\26H\377\0-F\377\0""7H\377\0""8\\\377" + "\0KT\377\26\10\201\377\0\0c\377\0\0I\377\0\0E\377\217\11'\377i\0\6\377" + "P\0\0\377L\0\0\377^\77\0\377[4\0\377KD\0\377]J\0\377PD\0\377IL\0\377" + "IB\0\377HK\0\377JH\0\377RJ\0\377PW\0\377VW\0\377eN\0\377ZT\0\377SR\0" + "\377Lo\0\377Rc\0\377P\\\0\377HZ\0\377SX\0\377U\\\0\377KQ\0\377L@\0\377" + "\77""9\0\377=6\0\377;,\0\377D8\0\377@D\0\377B6\0\3776!\0\377:*\0\377" + "88\0\377L/\0\3775+\0\3773%\0\3774-\0\377=2\0\377I=\0\377I>\0\377<7\0" + "\377B0\0\3779)\0\3773&\0\37776\0\377&%\0\377(\35\0\3775=\0\377\236}X" + "\377\330\254\255\377\24\"\0\377\33)\0\377JO\0\377\327\250\260\377MP\0" + "\377\30\27\0\377%\34\0\377\"\21\0\377\37\24\0\377bD\10\377\346\227\303" + "\377+\26\0\377\17!\0\377%\40\0\377\16*\0\377@=\2\377\315\242\266\377" + "1V\20\377\0'\0\377\0\36\0\377\1!\0\377\0\"\0\377\0\27\0\377Xj(\377\303" + "\246\250\377\0\36\0\377\260\256\234\377Xc@\377\1!\0\377\211\217m\377" + "\207{\177\377\0\32\0\377\250\263\300\377T_K\377\0\37\0\377~\200\206\377" + "\200\231\205\377\0\36\0\377(<\21\377\243\254\300\377\17""1\27\377G`O" + "\377\226\245\311\377\0!\0\377\20""6\"\377\231\256\317\377\24\77\27\377" + "\0""0\0\377\0\40\0\377\202\0&\0\377\177\13C%\377\216\226\337\377\14/" + "7\377\0\12\0\377Xl\255\377Lx\273\377\0\20\1\377\0\21\12\377\40W}\377" + "Jq\302\377\0\10\24\377Lo\310\377Hp\307\377\0\3\34\377\0\20\25\377\0\26" + "\31\377\0-\20\377\0(\"\377\0\34\32\377\16L\\\377\231\244\377\377\0Sh" + "\377\0-0\377\0""5-\377\0""03\377\0\34%\377\0\"\"\377\0*\31\377\0(#\377" + "\0%5\377\0$0\377\226\252\377\377\0""0T\377-U\214\377\213\244\377\377" + "\0(R\377\0\12\32\377\0\5\26\377\0\3\32\377\0\0\36\377\0\3\22\377\0\5" + "\24\377\0\2\32\377\33%Q\377\252\221\377\377\40/K\377\0\1\15\377\0\3\17" + "\377\0\0\4\377\31.E\377\237\231\346\377\15.G\377\0\27\2\377\251\224\356" + "\377K`\205\377\0\20\22\3778F\201\377\225\223\361\377\0\0\22\377yx\324" + "\377i|\302\377\0\12\26\377<_\201\377\234\266\375\377\0&$\377\0*/\377" + "\231\257\377\377Cz\233\377\35LT\377\241\270\377\377\33\77g\377\0\33\34" + "\377\234\277\377\377Nm\217\377\0\33,\377Ll\227\377\251\265\377\377\0" + "0(\377\0\26""0\377\244\272\377\377Mi\223\377\0\21""3\377\0\31(\377w\207" + "\317\377Qf\243\377&6Y\377\271\252\377\3776tV\377\25e/\377&a;\377\33h" + "+\377\16T(\377\16Q#\377b\206\230\377\251\277\377\377\0'6\377\0$3\377" + "\0#2\377\0)8\377\0.,\377\0-&\377\0D-\377\0""37\377\0;4\377\0M6\377\0" + "QE\377\0LA\377\0D>\377\0\77.\377\0-5\377\0\36""6\377\0#0\377\0\37=\377" + "\0*;\377\0/B\377\0\36""5\377\0/D\377\0""5=\377\0\377\3\36D\377\11\35;\377\177\15" + "\31B\377\0%3\377\1.D\377\15\35G\377\16'\77\377\12%>\377\24\36""7\377" + "\23!<\377\17\33""8\377\14\36C\377\16\26X\377\14\23e\377\12\25[\377\13" + "\23X\377\23\32[\377\3\22H\377\15\11H\377\7\16L\377\0\3Z\377\0\7a\377" + "\0\0[\377\0\17U\377\0\20Z\377\0\32V\377\0+Z\377\0.S\377\0:J\377\36\12" + "\203\377\0\0i\377\0\0Z\377\0\0W\377\220\2!\377u\0\0\377W\0\0\377Y\0\0" + "\377M2\0\377W=\0\377ZI\0\377iH\0\377LH\0\377GF\0\377JH\0\377M=\0\377" + "VC\0\377]=\0\377\\J\0\377\\F\0\377UC\0\377\\H\0\377RN\0\377AT\0\377@" + "b\0\377Ab\0\377PN\0\377NX\0\377HP\0\377\77R\0\377LN\0\377M9\0\377B0\0" + "\377=\77\0\377D7\0\377K;\0\377B4\0\377;5\0\377@/\0\377G4\0\377:1\0\377" + ":*\0\3778#\0\377-$\0\37732\0\377=A\0\3773>\0\377::\0\377\77""2\0\377" + "A3\0\37703\0\377.)\0\377+'\0\377\37""5\0\377/*\0\377H>\0\377\234\207" + "]\377\262\212\200\377\260\227\201\377\273\227\205\377\333\252\261\377" + "LC\0\377m\\\36\377\265\224\177\377\257\207\204\377\261~\223\377\277\217" + "\243\377yY\77\377\36\26\0\377\25!\0\377\30,\0\377\16$\0\377=Q\2\377\315" + "\267\277\377=]\22\377\12/\0\377\3;\0\377\0+\0\377\0\34\0\377\0#\0\377" + "e_<\377\272\261\244\377\0,\0\377Ug5\377\257\253\244\377\230\225v\377" + "\266\246\237\377\216\222|\377\0\37\0\377I]5\377\236\237\227\377\200\212" + "}\377\254\253\256\377\203\204\203\377\0\32\0\377\33H\16\377\255\266\310" + "\377\36;\33\377EfT\377\247\251\323\377\177\0\21\0\377\21\77\34\377\233" + "\252\307\377\10@#\377\0+\0\377\0\30\0\377\0-\0\377\0&\0\377\14""9'\377" + "\237\246\324\377\12""9,\377\0\5\0\377\0+&\377|\221\327\377e~\261\377" + "Py\272\377l\220\340\377\0\20D\377\0\0\23\377^r\301\377cx\275\377\0\14" + "\13\377\0\27\16\377\0\37\21\377\0(!\377\0\36\27\377\0\25\33\377\7Hc\377" + "\211\262\377\377\0G\\\377\0\37%\377\0\36'\377\0\26-\377\0#\40\377\0:" + "P\377W\216\345\377e\224\335\377g\215\321\377b\177\333\377T\203\314\377" + "\0\37""8\3775`\233\377\234\236\377\377fx\336\377jw\317\377ns\315\377" + "{\177\304\377\0\7\35\377\0\0\35\377\0\2\32\377\0\0\31\377*1T\377\261" + "\237\374\377\213y\313\377zz\274\377\210x\275\377B2i\377\35->\377\261" + "\246\375\377\36DO\377\0\21\13\377\274\242\374\377SS\200\377\0\20\22\377" + "@Q\210\377\251\252\356\377\0\0\0\377\10\30C\377\241\246\357\377\206\222" + "\277\377\232\241\352\377\243\310\377\377\0!\21\377\0\"\17\377\256\303" + "\377\377D\203\223\377\0""87\377p\201\300\377\205\245\346\377~\240\320" + "\377\272\300\377\377ai\231\377\0\12#\377\10\40C\377a|\276\377_\205\265" + "\377\0\"0\377Af\222\377\226\247\371\377|\215\352\377\211\207\343\377" + "\213\215\347\377\15""0A\3772De\377\307\302\377\3779_W\377\16N5\377\40" + "`;\377\25bH\377\253\250\340\377\223\247\316\377\256\271\371\377,Zs\377" + "\0""3-\377\0,-\377\7\"8\377\4\35""9\377\0/4\377\0""35\377\0""0B\377\0" + ";=\377\0""82\377\0<6\377\0F0\377\0AF\377\0""2\77\377\0D8\377\0""0B\377" + "\0$:\377\0\"@\377\1$I\377\0&;\377\0\23F\377\0\25""0\377\0\"8\377\0""0" + "I\377\0.R\377p\0AG\377\0,K\377\0:K\377\0""2O\377\0\35=\377\3\32;\377" + "\10\14E\377\0\25""6\377\2\35>\377\0\37J\377\17%C\377\23-;\377\17%H\377" + "\26(J\377\26.<\377\20'D\377\13-I\377\14\40P\377\3\40]\377\22\23S\377" + "\15\25X\377\5\27W\377\0\20`\377\6\24X\377\0\5S\377\0\6S\377\0\0U\377" + "\0\5W\377\1\0V\377\0\4X\377\0\22\\\377\0\17Y\377\0\25V\377\0\35Y\377" + "\0.]\377\"\0\200\377\5\0\\\377\0\0K\377\0\0`\377\223\16\30\377x\0\0\377" + "T\0\0\377C\0\0\377S1\0\377S5\0\377b7\0\377S;\0\377E4\0\377MD\0\377P@" + "\0\377J<\0\377I9\0\377VB\0\377\\9\0\377[5\0\377W.\0\377W2\0\377TA\0\377" + "YA\0\377WL\0\377LM\0\377OW\0\377TU\0\377PP\0\377KN\0\377PA\0\377T;\0" + "\377PC\0\377=\77\0\377@@\0\377D5\0\377<6\0\377;2\0\377A5\0\377E<\0\377" + "A&\0\377C6\0\3777@\0\377.D\0\377*5\0\3779+\0\3778*\0\377;3\0\3778,\0" + "\3776,\0\377&0\0\377-\33\0\377\")\0\377\"5\0\377\"7\0\377\32A\0\377W" + "K\10\377\216mF\377\200o\77\377\214q;\377\211r4\377\77C\0\377SW\1\377" + "\201pB\377\211w@\377\201oO\377\206\\C\3775=\0\377\24%\0\377\16\40\0\377" + "\10$\0\377\14&\0\377*\77\0\377svE\377*F\0\377\0""9\0\377\0D\0\377\202" + "\0""9\0\377\177\0+\0\377:U\0\377ioC\377\0,\0\377\24;\0\377iqS\377ksR" + "\377cnG\377Cg'\377\0%\0\377\14A\0\377Oz;\377[{O\377\232\242\262\377\214" + "\223\210\377\0\"\0\377\14""0\0\377K`T\377\0""4\0\377\34D\25\377>kX\377" + "\0\30\0\377\0-\5\377:aR\377\0""0\0\377\0\21\0\377\0\33\0\377\0\30\0\377" + "\0\33\0\377\0,\12\377Bbp\377\0\36\16\377\0\13\0\377\0\21\0\377\77`n\377" + "4Xm\3770Z{\3770St\377\0\7\1\377\0\20\0\377\7Ie\377\10Vk\377\0\37\21\377" + "\0\37\15\377\0*\30\377\0&\24\377\0%\34\377\0""1\36\377\0""3B\3777w\240" + "\377\0""0<\377\0\37/\377\0\35)\377\0!+\377\0#,\377\0""2D\377+n\242\377" + "4p\230\377+k\245\377/g\241\377\0""9`\377\0\4""4\377\24'g\377FW\236\377" + ";Y\234\377\77`\244\377AT\211\377JY\225\377\0\7\37\377\0\2\31\377\0\1" + "\17\377\0\3\14\377\7\27""4\377UQ~\377Z]\200\377\\[\215\377ce\224\377" + "->H\377\10&#\377Wf\211\377\6""0/\377\0\24\11\377_f\217\377*\77L\377\0" + "\23\15\377$9R\377WY\207\377\0\0\26\377\0\3\2\377bO\206\377\\S\207\377" + "R]\211\377W^\212\377\0\13\17\377\0\37\26\377Ym\210\377%TT\377\0\37\37" + "\377\34F\\\377St\251\377[h\234\377[s\244\3776Fd\377\1\7+\377\0\24/\377" + "/Cg\377ET\221\377\0\14""1\377\5+C\377dq\242\377aj\246\377ae\244\377K" + "O\212\377\0\33\34\377\27(=\377ul\224\377&DL\377\27C1\377\30[(\377\35" + "O:\377s\223\235\377m\211\250\377g\221\266\377\0""24\377\0'7\377\0\36" + "6\377\0-5\377:\0,1\377\0\27.\377\0)9\377\0\37<\377\0,A\377\0/D\377\0" + "<;\377\14""2I\377\0""7\77\377\0(H\377\0""3U\377\0\37D\377\0\32B\377\0" + "\40\77\377\4\31C\377\6\30""9\377\3\24C\377\2%4\377\3\37E\377\4\36N\377" + "\0'F\377\0""5E\377\0*J\377\0+R\377\17""3T\377\5&M\377\3\24F\377\7\30" + "L\377\10\"H\377\3\34T\377\2\37I\377\24-J\377\15""3B\377\24'V\377\13#" + "I\377\24*A\377\14%E\377\33(K\377\15\27N\377\13!O\377\4\23M\377\14\30" + "N\377\6\14D\377\5\26P\377\14\1M\377\1\11K\377\2\0O\377\0\0L\377\0\0Q" + "\377\7\6O\377\10\11S\377\0\7R\377\4\27S\377\0\35S\377\0\32T\377\0\26" + "T\377'\0\200\377\10\0}\377\202\0\0V\377\177\211\13\5\377n\0\0\377E\0" + "\0\377C\0\0\377M9\0\377X<\0\377U>\0\377H1\0\377A4\0\377HC\0\377O-\0\377" + "Y9\0\377L@\0\377TH\0\377SC\0\377Y7\0\377V1\0\377V0\0\377N3\0\377N6\0" + "\377Y8\0\377QI\0\377MM\0\377TK\0\377SR\0\377LS\0\377QL\0\377VB\0\377" + "EC\0\377JD\0\377F@\0\377G6\0\377>'\0\37769\0\3774>\0\377>D\0\3772:\0" + "\377:2\0\3774E\0\377(@\0\377/2\0\377(<\0\377+/\0\377-3\0\3776&\0\377" + "&)\0\377+\40\0\377+\"\0\377(\40\0\377\33*\0\377*#\0\377\31""3\0\377\32" + "*\0\377\34-\0\377\31""0\0\377\34%\0\377\36(\0\377\24""4\0\377\17""8\0" + "\377\31$\0\377\27.\0\377\24'\0\377\34$\0\377\2""6\0\377\3-\0\377\0,\0" + "\377\2%\0\377\5(\0\377\6-\0\377\23""4\0\377\16""9\0\377\0""5\0\377\0" + "D\0\377\0+\0\377\0-\0\377\0&\0\377\0""8\0\377\5""2\0\377\2.\0\377\5""1" + "\0\377\0""9\0\377\0""2\0\377\0'\0\377\0""7\0\377\0+\0\377\24Y\0\377\212" + "\262\205\377\211\243\222\377\276\273\277\377Eg\77\377\0,\0\377\0\37\0" + "\377\0\27\0\377\0\25\0\377\0\32\0\377\0\15\0\377\0\16\0\377\0\27\0\377" + "\0\30\0\377\0\35\0\377\0\30\0\377\0\32\0\377\0\33\0\377\0\31\0\377\0" + "%\0\377\0\33\0\377\0\14\0\377\0\21\0\377\0\26\0\377\0\7\0\377\0\0\0\377" + "\0\4\0\377\0\3\0\377\0\17\0\377\0\36\0\377\0\27\4\377\0\32\0\377\0\31" + "\26\377\0\27\36\377\0\24\34\377\0&\25\377\0\35\37\377\0\32\36\377\0\37" + "$\377\0\30.\377\0\37*\377\0$:\377\177\0*(\377\0\34""2\377\0\25""5\377" + "\0\40,\377\0\37""2\377\0\31.\377\0\23%\377\0\14\"\377\0\2%\377\0\3+\377" + "\0\3-\377\0\12*\377\0\17.\377\0\13\37\377\0\1)\377\0\0/\377\0\0%\377" + "\0\1\"\377\0\3\27\377\0\1\34\377\0\20\17\377\0\17\33\377\0\11!\377\0" + "\26\31\377\0\35%\377\0&\32\377\0\"\24\377\0\22\2\377\0\22\7\377\0\20" + "\16\377\0\16\17\377\2\17\14\377\0\13\31\377\2\11\20\377\0\12\22\377\0" + "\0\15\377\0\0\32\377\0\6\30\377\0\0\37\377\4\0\35\377\0\0\23\377\0\0" + "\30\377\1\21\13\377\0!\25\377\0$\35\377\0\20\35\377\4\33!\377\11\32)" + "\377\7\31""3\377\11\6.\377\20\6*\377\20\10""1\377\12\13""0\377\1\1,\377" + "\4\4""1\377\0\2$\377\15\27/\377\6\22""1\377\14\33-\377\11\26%\377\17" + "\16*\377\12\24(\377\27\22(\377\13\32\"\377\7()\377\10""51\377\22>3\377" + "\32E7\377\27F.\377\5F>\377\0""4<\377\6-:\377\12#/\377\3'7\377\0-8\377" + "\0\34;\377\0\21""0\377\5\22D\377\4\26\77\377\12\25""9\377\4\"C\377\20" + "$>\377\16!P\377\6\31@\377\4&J\377\1#@\377\0\31E\377\0\26""3\377\4\13" + ";\377\11\13;\377\14\12>\377\7\16<\377\20\30D\377\7%M\377\4#H\377\17\37" + "E\377\6%K\377\14$U\377\16'J\377\31""2J\377\25*C\377\22\34J\377\13\34" + "V\377\21\27T\377\20'X\377\34""0N\377\33&Q\377\35$S\377#\24T\377\31\31" + "[\377\27\36R\377\40\33T\377\15\33R\377\21\31K\377\10\15J\377\34\10A\377" + "\33\12I\377\25\12G\377\21\12N\377\11\15A\377\22\6C\377\26\5H\377\15\4" + "M\377\2\1T\377\17\16R\377\14\10^\377\15\10e\377w\10\36W\377\0\35\\\377" + "\0\34M\377\4\35X\377'\0\212\377\22\0i\377\0\0N\377\0\0S\377v\6\0\377" + "V\0\0\377<\0\0\377J\0\0\377J3\0\377X,\0\377L.\0\377D6\0\377L3\0\377O" + "8\0\377R7\0\377U8\0\377O:\0\377R>\0\377O9\0\377J4\0\377T)\0\377L<\0\377" + "P=\0\377@=\0\377A8\0\377PD\0\377WE\0\377PD\0\377UB\0\377GT\0\377NC\0" + "\377JM\0\377RC\0\377MN\0\377M.\0\377C.\0\3772.\0\3777*\0\37742\0\377" + "55\0\377+8\0\3770;\0\3779\77\0\377'/\0\377*+\0\377&-\0\377*\37\0\377" + "',\0\377\40)\0\377!&\0\377\")\0\377\31-\0\377\23\33\0\377\21\36\0\377" + "\13\32\0\377\26\36\0\377\0,\0\377\10%\0\377\16+\0\377\13%\0\377\22""2" + "\0\377\34""6\0\377\17$\0\377\22-\0\377\20(\0\377\5-\0\377\2""6\0\377" + "\5,\0\377\0""4\0\377\6""6\0\377\0-\0\377\2/\0\377\5-\0\377\0(\0\377\0" + "0\0\377\3""9\0\377\0""8\0\377\3/\0\377\0""5\0\377\0;\0\377\0<\0\377\2" + "3\0\377\0""7\0\377\0""1\0\377\0:\0\377\0@\0\377\0<\0\377\0=\0\377\0""8" + "\0\377\37V\14\377\226\243\223\377\230\245\232\377\243\244\242\377\0""4" + "\0\377\0.\0\377\0(\0\377\0*\0\377\0\26\0\377\0\32\0\377\0\36\0\377\0" + "\23\0\377\0\20\0\377\0\25\0\377\0\26\0\377\0\35\0\377\0\26\0\377\0\36" + "\0\377\0\20\0\377\0\34\0\377\0\13\0\377\0\0\0\377\0\11\0\377\0\0\0\377" + "\0\10\0\377\0\25\0\377\202\0\0\0\377\177\0\15\0\377\0\16\0\377\0\17\0" + "\377\0\13\35\377\0!\24\377\0\"\21\377\0\13\15\377\0\10\11\377\0\25\25" + "\377\0\21\35\377\0\14#\377\0\22%\377\0\22(\377\0\31.\377\0\20/\377\0" + "!1\377\0\36""6\377\0\26;\377\0\22=\377\0\36""5\377\0\16,\377\0\25,\377" + "\0\26:\377\0\16=\377\0\31""9\377\0\27.\377\0\10(\377\0\3%\377\0\11)\377" + "\0\0)\377\0\3.\377\0\10\"\377\0\13&\377\0\11\26\377\0\4\31\377\0\3\13" + "\377\0\1\7\377\0\31\12\377\0\12\24\377\0\24\17\377\0\11\6\377\4\17\13" + "\377\5\13\24\377\13\10\16\377\12\5\10\377\0\0\6\377\4\0\16\377\12\0\14" + "\377\13\0\31\377\13\0\37\377\6\0\34\377\12\0\26\377\16\0\24\377\11\0" + "\35\377\20\0\27\377\13\0\21\377\11\17\21\377\5\4\35\377\4\14\25\377\22" + "\26,\377\25\14,\377\17\1-\377\15\15""6\377\13\1""5\377\31\0.\377\31\0" + "1\377\23\0$\377\22\0&\377\20\0)\377\26\13\33\377\6\5""1\377\27\22*\377" + "\31\12-\377\23\21)\377\20\36""0\377\30\25)\377\30\22,\377\23\21)\377" + "\37\25""2\377\34!D\377.)9\377%/@\377\36(H\377\24""3\77\377!.;\377\17" + "!D\377\17#6\377\17\35""1\377\17\21""8\377\22\14""6\377\5\17""6\377\5" + "\7:\377\1\10<\377\10\2:\377\23\4""7\377\11\15G\377\20\14<\377\23\15J" + "\377\26\25@\377\4\24;\377\13\7""5\377\22\5""9\377\11\0B\377\2\1\77\377" + "\2\10>\377\15\21P\377\11\15P\377\17!V\377\26\33P\377\27\40R\377\37\16" + "H\377\35\40O\377\26#Q\377\35\40b\377\34\32W\377\21\22\\\377\37\27V\377" + "\25\40[\377\30!Z\377\34\26^\377(\36Y\377\27\17U\377\30\15J\377\32\11" + "T\377\33\14S\377!\25X\377\"\14P\377{!\12P\377\27\13B\377\32\12@\377\40" + "\3F\377\11\12K\377\5\7M\377\25\15S\377\37\12O\377\16\11S\377\23\0N\377" + "\17\0b\377\32\3a\377\32\0`\377\36\12Q\377\34\21Y\377\24\22I\377\3\25" + "G\377\0'[\3772\6\203\377\22\0c\377\0\0C\377\0\0U\377~\15\0\377m\0\0\377" + "D\0\0\377\77\0\0\377>%\0\377O#\0\377\77!\0\377R#\0\377P.\0\377J6\0\377" + "N7\0\377\77""6\0\377C2\0\377C-\0\377U@\0\377T<\0\377;>\0\377CG\0\377" + "C>\0\377E.\0\377N;\0\377L3\0\377X8\0\377Q7\0\377H6\0\377-\0\377@A\0\377<=\0\37782\0\3779D\0\377\77;\0\377\77" + "7\0\377E:\0\377C3\0\3778+\0\377L(\0\377J.\0\377\77""7\0\3770>\0\377:" + "H\0\377@H\0\377:E\0\3779H\0\3779I\0\377'1\0\377-0\0\377)+\0\377,%\0\377" + "',\0\377*(\0\377((\0\377()\0\377'\"\0\377\23\37\0\377\17(\0\377\35,\0" + "\377\12*\0\377\30\35\0\377\5\32\0\377\3\36\0\377\20\25\0\377\0+\0\377" + "\12'\0\377\2!\0\377\0/\0\377\22\40\0\377\0&\0\377\0%\0\377\0'\0\377\0" + ")\0\377\0*\0\377\0+\0\377\0'\0\377\0+\0\377\0,\0\377\0""0\0\377\0""3" + "\0\377\0""4\0\377\0(\0\377\0+\0\377\0""4\0\377\0,\0\377\0%\0\377\0$\0" + "\377\0""3\0\377\1)\0\377\0(\0\377\0'\0\377\0.\0\377\0$\0\377\0\36\0\377" + "\0)\0\377\0""3\0\377\0.\0\377\0""9\0\377\0E\0\377\0A\0\377\202\0=\0\377" + "\13\0""6\0\377\0I\0\377\0M\0\377\0H\0\377\0""7\0\377\0""8\0\377\0(\0" + "\377\0*\0\377\0%\0\377\0(\0\377\0&\0\377\202\0\33\0\377\177\0\31\0\377" + "\0\22\0\377\0\23\0\377\0\14\0\377\0\25\0\377\0\15\0\377\0\27\0\377\0" + "\7\0\377\0\7\6\377\0\3\4\377\0\14\0\377\0\0\6\377\0\1\2\377\0\5\0\377" + "\0\1\7\377\0\0\6\377\0\1\20\377\0\2\13\377\0\0\13\377\0\16\20\377\0\14" + "\15\377\0\0\30\377\0\3\25\377\0\5\36\377\0\21!\377\0\16\37\377\0\26#" + "\377\0\24%\377\0\35""0\377\0\31.\377\0!.\377\0!'\377\0\22*\377\0\35\"" + "\377\0\26\30\377\0\35\31\377\0!%\377\0\16\"\377\0\30""9\377\0\24""1\377" + "\0\17(\377\0\0\"\377\0\5""3\377\0\4(\377\0\0,\377\0\6,\377\0\6\"\377" + "\0\21!\377\0\0!\377\0\0\33\377\0\7\31\377\4\10\30\377\10\20\32\377\14" + "\7\31\377\20\0\25\377\14\6\25\377\11\0\15\377\27\0\27\377\11\0\11\377" + "\30\0#\377\26\0\32\377\30\0\37\377#\0\25\377+\0!\377*\0\32\377+\0!\377" + "\37\0\37\377\34\0$\377\35\0\32\377\37\0\20\377+\0\20\377.\0\20\377\35" + "\0\26\377\40\0\34\377\"\0,\377'\0\33\377\35\0)\377\32\0+\377/\0/\377" + "+\0""7\377\"\0/\377*\0'\377\30\0'\377&\0$\377\23\0)\377\25\0#\377(\0" + ",\377-\0;\377*\0+\377\35\0@\377'\0""4\377\"\0*\377/\0.\377/\14<\377*" + "\16""6\377:\25\77\377)\36G\377-\40\77\377#&<\377\40\15E\3772\14B\377" + "\37\5\77\377\33\0D\377\26\0""1\377\30\0""7\377\33\0\77\377\26\0A\377" + "\21\0""8\377%\0;\377\"\0""0\377\23\2=\377\23\7\77\377\24\2@\377\32\0" + "7\377\26\0\77\377\21\0B\377\35\0=\377\32\0\77\377&\0\77\377!\0Z\377\30" + "\0Z\377\"\0V\377\26\0R\377\27\0T\377#\3I\377\20\3K\377\32\6Y\377\177" + "\26\1[\377\33\0\\\377\36\7\\\377*\0`\377$\0`\377'\0T\377!\7]\3776\0J" + "\377*\14N\3773\21]\377%\5i\377-\0Q\377'\13A\377!\0O\377*\0Q\377%\0Q\377" + "'\2U\377!\0N\377\30\0W\377\24\0X\377\34\0U\377\27\13\\\377\34\11e\377" + "\27\1^\377\31\0d\377\27\0Y\377#\0\\\377\33\0`\377$\4b\377\"\2X\377\"" + "\0Y\377\26\11W\377\30\3P\377e\26\233\377A\0\205\377\14\0Y\377\14\0R\377" + "\213$\0\377\177$\0\377[\0\0\377:\0\0\377/+\0\37735\0\377:)\0\3778(\0" + "\3773'\0\377:0\0\37771\0\377@(\0\377\77""8\0\3771<\0\37788\0\37797\0" + "\377,<\0\37705\0\37768\0\37790\0\37782\0\377=&\0\37727\0\377:/\0\377" + ".7\0\3774D\0\3770<\0\3774G\0\3776A\0\377*E\0\377'=\0\377\40""3\0\377" + "\30,\0\377\31""7\0\377*1\0\377/'\0\377#%\0\377\14&\0\377\24+\0\377\15" + "$\0\377\21&\0\377\15$\0\377\22%\0\377\20(\0\377\22.\0\377\15#\0\377\0" + "2\0\377\0)\0\377\0""5\0\377\0)\0\377\0\35\0\377\0'\0\377\0\40\0\377\0" + "\"\0\377\0\40\0\377\0&\0\377\0$\0\377\0#\0\377\0.\0\377\0""6\0\377\0" + "#\0\377\0""2\0\377\0!\0\377\0'\0\377\0\30\0\377\0\40\0\377\0\34\0\377" + "\0\37\0\377\0'\0\377\0(\0\377\0&\0\377\0'\0\377\0&\0\377\0#\0\377\0\40" + "\0\377\0'\0\377\0#\0\377\0\40\0\377\0#\0\377\0""1\0\377\0<\0\377\0:\0" + "\377\0;\0\377\0)\0\377\0:\0\377\0""7\0\377\0\77\0\377\0A\0\377\0I\0\377" + "\0;\0\377\16\0""4\0\377\0(\0\377\0\32\0\377\0\40\0\377\0!\0\377\0\17" + "\0\377\0\14\0\377\0\12\0\377\0\26\0\377\0\6\0\377\0\15\0\377\0\23\0\377" + "\0\7\0\377\0\0\0\377\202\0\7\0\377&\0\20\0\377\0\12\6\377\0\14\32\377" + "\0\2\4\377\0\0\2\377\0\0\3\377\0\3\7\377\0\0\6\377\0\7\2\377\0\22\23" + "\377\0\25\15\377\0\7\30\377\0\0\26\377\0\0\23\377\0\0\34\377\0\12\"\377" + "\0\26,\377\0\13*\377\0\20""0\377\0\27\"\377\0\15,\377\0\14%\377\0\27" + "\36\377\0\30#\377\0!\33\377\0\37\40\377\0\34&\377\0#\40\377\0\40""2\377" + "\0\22)\377\0\7""3\377\0\15-\377\0\4\"\377\0\3+\377\0\10&\377\0\3!\377" + "\0\3(\377\0\0#\377\202\0\0&\377\177\6\0\21\377\3\0\13\377\0\0\34\377" + "\0\0\32\377\0\0\36\377\7\7\7\377\6\12\30\377\10\21\34\377\12\0\32\377" + "\23\1\35\377\36\0\27\377\36\0\34\377\24\0\27\377\34\0\36\377\36\0\24" + "\377*\0\35\377\36\0!\377\36\0'\377\40\0\37\3771\0)\377.\0\34\377$\0*" + "\377,\0\"\3775\0)\377,\0,\3770\0.\377:\0:\377.\0,\3774\0""2\377%\0$\377" + "'\0+\377.\0""5\377\35\0*\377\37\0+\377\32\0/\377\"\0""6\3770\0""6\377" + "4\0""7\377,\0:\377-\0""7\377,\0""8\3777\0""4\3773\0""8\3772\0-\377.\0" + "1\3774\0""8\3775\0""3\377.\0;\377.\1""6\377)\12""7\377&\22""2\377$\7" + "D\3770\0>\377&\0F\377!\0@\377\40\0>\377$\0B\377\36\0\77\377\32\0D\377" + "\27\0""2\377*\0F\377\"\0E\377\35\0A\377#\0""7\377\30\0<\377\23\0""3\377" + "\13\0>\377\22\0""0\377\37\0""9\377#\0J\377\"\0J\377!\0L\377\40\0S\377" + "/\0_\377+\0\\\3773\0H\377\36\0K\377)\0W\377#\0[\377#\0X\377\27\0`\377" + "\35\0g\3771\0_\377/\0U\3774\0\\\3772\0U\377<\0b\377;\0V\377(\7`\377/" + "\2_\377/\0S\377.\0M\377\36\0Y\377'\0T\377%\0P\377\40\0Z\377\33\0Y\377" + "\25\0S\377\21\0^\377\31\0X\377,\0e\377&\1d\377\"\0^\377+\0V\377)\1f\377" + "\"\3j\377)\4i\3770\10p\3774\13h\377)\0^\377/\0b\377#\0g\377\207\22\275" + "\377N\0~\377\30\0V\377\25\0^\377\205'\0\377v!\0\377]\16\0\3774\0\0\377" + ">3\0\377\0\377\";\0\377\26""4\0\377\34""5\0\377\34'\0\377,$\0\377(-\0\377\10" + ")\0\377\14(\0\377\24\"\0\377\33%\0\377\20""1\0\377\13(\0\377\5*\0\377" + "\34,\0\377\10%\0\377\10(\0\377\0,\0\377\0;\0\377\0""0\0\377\0""4\0\377" + "\0.\0\377\0-\0\377\0\36\0\377\0\35\0\377\0(\0\377\0%\0\377\0*\0\377\0" + "'\0\377\0,\0\377\202\0*\0\377(\0\37\0\377\0$\0\377\0%\0\377\0\23\0\377" + "\0\34\0\377\0\40\0\377\0-\0\377\0'\0\377\0$\0\377\0\37\0\377\0+\0\377" + "\0!\0\377\0#\0\377\0&\0\377\0\36\0\377\0\32\0\377\0\37\0\377\0\40\0\377" + "\0=\0\377\0B\0\377\0*\0\377\0)\0\377\0\"\0\377\0!\0\377\0""0\0\377\0" + ",\0\377\0.\0\377\0""3\0\377\0""2\0\377\0;\0\377\0\"\0\377\0\40\0\377" + "\0#\1\377\0\25\0\377\0\21\2\377\0\26\3\377\0\17\0\377\0\14\7\377\0\12" + "\0\377\0\16\0\377\202\0\0\0\377\1\0\10\0\377\203\0\0\0\377\177\0\0\7" + "\377\0\0\22\377\0\0\14\377\0\0\16\377\0\10\17\377\0\4\10\377\0\0\2\377" + "\0\10\7\377\0\3\6\377\0\15\23\377\0\3\23\377\0\23\10\377\0\14'\377\0" + "\31$\377\0\10&\377\0\31.\377\0\25)\377\0\36(\377\0\27&\377\0\32(\377" + "\0\23'\377\0\23+\377\0\24""0\377\0\31""5\377\0\21(\377\0\17(\377\0\32" + "'\377\0\22$\377\0\0*\377\0\0\"\377\0\1+\377\0\0""0\377\0\1%\377\0\4\27" + "\377\0\0!\377\0\0(\377\0\1(\377\0\0#\377\0\0\37\377\0\0\13\377\0\0\34" + "\377\0\0\13\377\0\0\20\377\7\0\27\377\2\0\31\377\15\0\32\377\24\0\23" + "\377\20\1(\377\16\0\30\377\13\0\27\377\11\0\"\377\11\0\37\377\22\0\34" + "\377\37\0\37\377\21\0$\377-\0\34\377!\0\40\377$\0(\3770\0/\377%\0/\377" + "8\0""2\377.\0""0\377+\0""5\3778\0""2\3774\0F\377B\0=\3779\0""6\377\"" + "\0""5\377\"\0(\377\36\0*\377\36\0""4\377&\0\34\377$\0%\377%\0:\3779\0" + "8\377,\0""8\377=\0""9\377;\0.\3779\0:\377-\0""3\377.\0""5\377;\0A\377" + "<\0""8\377\77\0A\3776\0=\377=\0<\377D\0>\377:\0;\3777\4:\3775\6:\377" + "0\0C\3773\0C\377%\0F\3772\0<\377'\0A\377/\0K\377\33\0F\377(\0C\377!\0" + ";\377#\0;\377(\0B\377.\0""1\377\32\0A\377\40\0""6\377\31\0=\377\23\0" + ":\3771\0C\377-\0K\377.\0V\377(\0J\3773\0J\377\77\0O\377:\0R\3777\0X\377" + "6\0M\3775\0N\377;\0P\377*\0`\377(\0[\377/\0[\377/\0X\3777\0e\3772\0\\" + "\3775\0L\3773\0N\3770\0W\377.\0Z\377^%\0_\377)\0\\\377'\0U\377\36\0X" + "\377\40\0]\377\"\0^\377\33\0[\377\34\0^\377\40\0g\377\40\0d\377)\0Z\377" + "$\0X\377;\0\\\3774\0]\377.\0X\3772\0e\3772\0^\377#\20e\377/\11_\3776" + "\2h\3777\1z\3771\0w\377<\0r\3774\0l\377\235\15\324\377a\0\221\3775\0" + "k\377.\0h\377n*\0\377r)\0\377c!\0\377T\11\0\3772E\0\377\0\377" + "==\0\37726\0\3776-\0\377D&\0\377A'\0\37705\0\3774#\0\3774-\0\377$1\0" + "\377.*\0\377,'\0\3775*\0\377*:\0\377+D\0\377*9\0\377.;\0\377+:\0\377" + ",0\0\377&3\0\377&/\0\377\37\"\0\377&&\0\377!:\0\377!\40\0\377\26(\0\377" + "\35%\0\377\34\40\0\377\24(\0\377\21""2\0\377'!\0\377$+\0\377\34""4\0" + "\377&3\0\377\14.\0\377\10""6\0\377\12\36\0\377\26\37\0\377\0%\0\377\0" + "'\0\377\4""7\0\377\7""9\0\377\0""2\0\377\0.\0\377\0""8\0\377\0(\0\377" + "\0""7\0\377\0""0\0\377\0\30\0\377\0\33\0\377\0*\0\377\0)\0\377\0\36\0" + "\377\0$\0\377\0\34\0\377\0)\0\377\0&\0\377\0\40\0\377\0\37\0\377\0&\0" + "\377\202\0$\0\377\6\0\23\0\377\0%\0\377\0\34\0\377\0!\0\377\0\35\0\377" + "\0#\0\377\202\0\37\0\377\177\0%\0\377\0&\0\377\0%\0\377\0.\0\377\0&\0" + "\377\0\40\0\377\0)\0\377\0\"\0\377\0/\0\377\0'\0\377\0-\0\377\0""2\0" + "\377\0(\0\377\0)\0\377\0*\0\377\0,\0\377\0+\0\377\0\32\5\377\0\32\0\377" + "\0\34\0\377\0\25\0\377\0\20\2\377\0\20\5\377\0\30\0\377\0\17\0\377\0" + "\21\0\377\0\13\0\377\0\11\0\377\0\24\0\377\0\3\0\377\0\0\0\377\0\0\5" + "\377\0\11\0\377\0\0\24\377\0\5\0\377\0\10\5\377\0\16\16\377\0\12\5\377" + "\0\14\10\377\0\24\15\377\0\21\24\377\0\6\33\377\0\33\31\377\0\34)\377" + "\0\25""0\377\0\30)\377\0\37-\377\0\40&\377\0&\32\377\0\31.\377\0\24&" + "\377\0\31+\377\0\21$\377\0\14*\377\0\23\34\377\0\20'\377\0\13%\377\0" + "\22$\377\0\11\37\377\0\4!\377\0\6$\377\0\0""0\377\0\0!\377\0\0&\377\0" + "\5\27\377\0\11\23\377\0\0!\377\0\0\40\377\4\0\20\377\0\0\20\377\0\0\22" + "\377\10\0\12\377\0\0\7\377\4\0\25\377\21\0\40\377\3\0\35\377\23\0\"\377" + "\23\0'\377\14\0#\377\20\0#\377\11\0\35\377\23\0\14\377\2\0\27\377\7\0" + "&\377\16\0/\377\"\0\"\377(\0""4\377\31\0""7\377\20\0""7\377!\0F\377&" + "\0,\377+\0""0\377'\0""9\377-\0@\377+\0.\377*\0""3\377=\0:\377;\0\77\377" + "5\0""1\377-\0(\377\35\0""8\377!\0""5\377\35\0""4\377\"\0""9\3771\0""7" + "\3774\0""1\377<\0""2\377\77\0=\3777\0""7\377C\0\77\377C\0G\377@\0J\377" + "I\0E\377<\0C\377>\0""6\3770\0:\3774\0F\3779\0E\377D\0D\377C\0G\377J\0" + "B\3779\0F\3775\0G\3771\0F\377$\0B\377'\0N\377*\0I\377\177$\0J\377&\0" + "X\377'\0F\377(\0;\377)\0A\377\25\0=\377%\0""5\377\40\0A\377,\0J\3775" + "\0""7\3775\0G\3770\0G\377%\0H\3777\0P\377<\0A\377B\0R\377M\0U\377;\0" + "W\377A\0^\3777\0`\377<\0e\3771\0c\377/\0`\3770\0`\3771\0\\\3775\0V\377" + "5\0_\377,\0W\377.\0M\3772\0b\377!\0o\377'\0b\377&\0`\377\40\0\\\377\37" + "\0d\377(\0T\377+\0S\377\"\0a\377\36\0i\377'\0[\377-\0Z\377+\0U\377'\0" + "`\377-\0f\377.\0a\3770\0`\3770\0_\3777\5g\3778\5a\377@\0s\377B\0v\377" + "D\0\177\377E\0{\377:\0u\377G\0u\377\232\4\316\377e\0\205\377\77\0l\377" + "2\0`\377J\0\0\377L\12\0\377H\0\0\377=\0\0\377\77\0\0\377Z\16\0\377\177" + "/\0\377\217G\0\377\246R\0\377\256T\0\377\236L\15\377\240W\5\377\260Q" + "\0\377\260H\21\377\257B\10\377\255D\5\377\234C\5\377\252;\13\377\226" + "E\2\377\234H\7\377\221G\0\377\241F\4\377\231R\0\377\242P\13\377\213I" + "\3\377\220E\13\377\221:\17\377\206\77\11\377\203F\0\377\220;\12\377\210" + "<\0\377\205E\0\377\210<\0\377\211F\0\377\200>\11\377\227<\11\377\213" + "8\15\377\223;\25\377\211K\16\377~J\17\377\177A\23\377vF\22\377\201B\26" + "\377\200I\40\377mG+\377kK%\377jF9\377mM,\377bT,\377hI/\377R;0\377YD3" + "\377ZH\40\377LF,\377H5(\377:C1\377W=6\377SE4\377YG6\377VA7\377E>6\377" + "C8C\377R4=\377I1<\377L):\377S,:\377A(3\377H(6\377\177H7:\377K&3\377X" + "'9\377Q1D\377S6\77\377N08\377[7\77\377V,5\377gA>\377m1L\377kAF\377oB" + "G\377o;I\377[MK\377bIR\377Z\77F\377d7G\377Z:C\377Q8H\377\\0`\377Z7`\377" + "R9i\377U2h\377S@a\377_;`\377a4h\377M7{\377H1z\377J7z\377S4m\377F1g\377" + "M;d\377X\77k\377S8s\377F@v\377>:d\377G5_\3774.c\377:+w\37708\206\377" + "1/\207\37761\203\377,A\204\3774Bu\377\77\77t\3776>\202\377CI\203\377" + "JE\210\377BA\213\3773P\210\377\231" + "\377PK\225\377NC\217\377HF\231\377L9\243\377@:\223\377B@\220\377;\77" + "\220\377>C\227\377@=\220\377A<\220\377MC\223\377M6\222\377E5\232\377" + "I2\230\377M#\232\377T/\207\377Z-\227\377a#\226\377Z*\225\377^%\225\377" + "b+\224\377j7\224\377v*\224\377o-\223\377m)\212\377q)\212\377u*\177\377" + "y\34\213\377\204\"\226\377v\40\213\377\206)\225\377\221&\214\377}\15" + "\232\377\215\14\224\377\215\11\214\377\214\23\221\377\216\17\217\377" + "\233\17\227\377\231\20\235\377\235\27\244\377\224\17\247\377\234\16\260" + "\377\234\22\265\377\227\25\271\377\246\17\270\377\244\12\266\377\251" + "\15\266\377\253\27\253\377\243\24\252\377\227\6\262\377\220\30\251\377" + "\240\33\253\377\234\23\252\377\230\21\246\377\235\36\244\377\232\23\250" + "\377\227\12\251\377\240\31\255\377\243\25\257\377\244$\260\377\243\22" + "\261\377\252\33\250\377\262\31\253\377\255\13\252\377\254\21\273\377" + "\252\23\271\377\247\25\271\377\253\40\260\377\237!\304\377\250\27\271" + "\377\241\"\264\377\254\30\260\377\262\36\254\377~\263\10\261\377\251" + "\22\255\377\241\27\262\377\246\40\255\377\250\27\253\377\252\31\277\377" + "\246\23\264\377\241\26\275\377\251\16\273\377\247\10\305\377\232\0\272" + "\377\237\0\265\377\232\0\245\377\242\0\263\377\232\4\266\377\226\0\257" + "\377\245\0\254\377\254\0\250\377\235\1\264\377\235\0\276\377\253\0\276" + "\377\255\0\275\377\272\0\303\377\272\3\307\377\253\0\302\377\255\3\322" + "\377\256\0\332\377\256\0\337\377\263\0\331\377\256\0\334\377\241\0\310" + "\377\233\0\322\377\250\0\324\377\240\12\334\377\237\14\333\377\243\0" + "\334\377\235\10\332\377\241\0\327\377\225\0\336\377\221\0\315\377\224" + "\0\311\377\221\1\312\377\230\1\313\377\234\22\322\377\234\17\320\377" + "\217\0\334\377\214\14\337\377\232\34\325\377\220\26\332\377\252\26\333" + "\377\266\34\323\377\263\37\326\377\252\30\325\377\245\21\330\377\252" + "\13\321\377\241\34\330\377\264\"\336\377\263\27\347\377\265\27\362\377" + "\265\26\365\377\307\22\362\377\274\21\347\377\261\0\313\377m\0\221\377" + "Z\0o\377G\0q\377F\0d\377B\0\0\377>\0\0\377<\0\0\377=\0\0\3773\0\0\377" + ">\0\0\377M\0\0\377Y\0\0\377e\16\0\377s\7\0\377b\17\0\377c\22\0\377g\21" + "\0\377j\15\0\377h\12\0\377d\3\0\377g\12\0\377a\0\0\377[\3\0\377Z\7\0" + "\377R\3\0\377P\0\0\377R\6\0\377U\0\0\377S\2\0\377S\0\0\377J\0\0\377N" + "\0\0\377M\4\0\377J\0\0\377H\0\0\377Q\0\0\377L\0\0\377@\0\0\377E\0\0\377" + "D\0\0\377;\0\0\377\77\0\0\377D\0\0\377>\2\0\3775\17\0\377:\0\0\377<\0" + "\0\377<\4\0\3773\13\0\377+\4\0\377*\14\0\377\35\11\0\377#\6\0\377$\6" + "\0\377\36\0\0\377\21\0\0\377\24\0\0\377\7\0\0\377\11\0\0\377\12\0\0\377" + "\21\0\0\377\0\0\0\377\2\0\0\377\202\14\0\0\377\3\16\0\0\377\6\0\0\377" + "\26\0\0\377\202\17\0\0\377\202\22\0\0\377\177\21\0\0\377\15\0\1\377\20" + "\0\0\377\20\0\6\377\37\0\0\377\31\0\0\377\25\0\0\377\27\0\1\377$\0\1" + "\377\31\0\13\377\35\0\15\377\34\0\16\377*\0\0\377&\13\0\377&\0\1\377" + "\40\0\12\377\37\0\17\377\20\0\31\377\27\0\25\377\37\0\11\377\33\0!\377" + "\23\0$\377\17\0)\377\11\3""2\377\27\0""4\377\15\0.\377\7\0:\377\5\0""7" + "\377\13\0""8\377\14\0""8\377\15\0)\377\30\0""5\377\23\0""6\377\4\0-\377" + "\0\0""5\377\0\0""0\377\3\1!\377\0\4.\377\0\0,\377\0\0""3\377\0\0""7\377" + "\0\0""8\377\0\0:\377\0\5/\377\0\0@\377\0\13""4\377\2\3>\377\6\7""0\377" + "\4\6""4\377\0\1>\377\0\0F\377\13\4U\377\6\17U\377\0\22R\377\0\24R\377" + "\0\0O\377\0\0X\377\0\1g\377\5\0Z\377\0\0P\377\2\4^\377\11\0R\377\4\0" + "P\377\3\1R\377\2\0Q\377\23\0K\377\6\0F\377\21\0M\377\23\0S\377\32\0X" + "\377\34\0P\377\34\0N\377\37\0H\377\37\0E\377\33\0N\377$\0Y\377/\0S\377" + "&\0L\377%\0I\377$\0@\3776\0J\377:\0N\3778\0P\377Q\0M\377J\0K\377M\0R" + "\377Q\0Y\377Y\0S\377W\0R\377`\0C\377X\0O\377K\0S\377[\0`\377[\0g\377" + "T\0m\377Z\0q\377V\0r\377S\0n\377V\0j\377`\0j\377g\0g\377d\0r\377P\0l" + "\377U\0d\377_\0d\377_\0_\377U\0^\377S\0c\377`\0c\377_\0o\377_\0h\377" + "c\0i\377T\0`\377S\0k\377_\0j\377c\0l\377_\0f\377\\\0l\377f\0r\377`\0" + "n\377g\0f\377\\\0h\377b\0n\377k\0p\377Z\0k\377l\0j\377q\0n\377ds\0l\377" + "i\0o\377n\0t\377c\0x\377i\0k\377m\0l\377`\0\206\377_\0~\377b\0s\377`" + "\0j\377k\0u\377l\0s\377`\0u\377e\0v\377W\0x\377T\0p\377X\0u\377b\0g\377" + "g\0o\377`\0o\377g\0s\377n\0o\377h\0w\377t\0\207\377h\0\213\377h\0\211" + "\377p\0\232\377u\0\240\377n\0\233\377d\0\226\377b\0\237\377e\0\243\377" + "c\0\224\377a\0\220\377]\0\226\377J\0\221\377T\0\225\377X\0\223\377^\0" + "\221\377J\0\212\377R\0\214\377X\0\211\377\\\0\231\377`\0\230\377Z\0\241" + "\377[\0\226\377Q\0\233\377W\0\226\377U\0\231\377l\0\213\377n\0\212\377" + "i\0\220\377r\0\227\377h\0\227\377o\0\225\377x\0\242\377v\0\255\377u\0" + "\255\377|\0\250\377y\0\245\377|\0\223\377|\0\204\377[\0e\377P\0g\377" + "]\0s\377N\0r\377N\0h\3778\0\0\3775\0\0\377=\0\0\377>\0\0\3778\0\0\377" + "C\0\0\377;\0\0\377B\0\0\3770\0\0\3779\0\0\3773\0\0\3770\0\0\377;\0\0" + "\377G\0\0\377B\0\0\377\77\0\0\3774\0\0\3776\0\0\377\"\0\0\377'\0\0\377" + "\32\0\0\377\25\0\0\377\16\0\0\377\37\0\0\377#\0\0\377\26\0\0\377\31\0" + "\0\377\34\0\0\377\32\0\0\377\31\0\0\377\14\0\0\377\31\0\0\377\14\0\0" + "\377\203\15\0\0\377\10\10\0\0\377\5\0\0\377\20\0\0\377\21\0\0\377\10" + "\0\0\377\24\0\0\377\14\0\0\377\4\0\0\377\260\0\0\0\377\5\0\0\5\377\0" + "\0\0\377\0\0\11\377\0\0\0\377\0\0\7\377\202\0\0\6\377\211\0\0\0\377\36" + "\0\0\16\377\0\0\21\377\0\0\13\377\0\0\5\377\0\0\15\377\0\0\10\377\0\0" + "\12\377\0\0\2\377\0\0\6\377\0\0\15\377\0\0\11\377\0\0\27\377\0\0\35\377" + "\0\0\37\377\0\0\17\377\0\0\"\377\0\0+\377\0\0\37\377\0\0&\377\0\0$\377" + "\0\0\"\377\0\0\32\377\0\0\31\377\0\0\17\377\0\0\35\377\0\0\17\377\0\0" + "\24\377\0\0\27\377\0\0\20\377\0\0\37\377\202\0\0\20\377{\0\0\22\377\0" + "\0!\377\0\0\27\377\0\0\24\377\0\0\37\377\0\0\30\377\0\0\26\377\0\0\37" + "\377\1\0\26\377\21\0\34\377\24\0\34\377\23\0\15\377\26\0\32\377\32\0" + ")\377(\0*\377'\0\"\377&\0\"\377'\0\31\377\37\0#\377\37\0\40\377\"\0\37" + "\377%\0'\377,\0)\377$\0""2\377)\0B\377\37\0\77\377'\0=\377,\0\77\377" + "$\0""6\377*\0""3\377/\0""6\377'\0/\377%\0""3\377\40\0+\377$\0)\377\37" + "\0-\377'\0""8\377\33\0:\377*\0=\377-\0""6\377\34\0I\377\34\0@\377*\0" + "/\377-\0;\3772\0""8\377,\0""5\3774\0""4\3771\0""4\377$\0""2\3773\0""6" + "\3779\0<\377,\0""8\3774\0@\3779\0K\377;\0;\3777\0A\3778\0""7\377@\0A" + "\377<\0A\3773\0:\377C\0>\377C\0@\3778\0C\3777\0=\3776\0/\377A\0=\377" + "6\0<\3779\0C\3776\0G\3774\0<\3777\0<\377-\0>\377A\0@\3778\0""9\3771\0" + "A\3772\0I\377B\0R\377;\0G\377A\0W\377D\0U\377;\0d\377<\0k\377=\0n\377" + "@\0f\377:\0c\377>\0k\3774\0b\377.\0g\3770\0U\377%\0]\377\34\0]\377\32" + "\0T\377%\0^\377\36\0W\377\32\0^\377\34\0b\377\32\0f\377&\0U\377)\0m\377" + "-\0e\377%\0d\377$\0[\377.\0d\377-\0\\\377<\0]\377\77\0^\377:\0Y\377A" + "\0]\377G\0Z\377B\0Z\377F\0r\377C\0r\377G\0|\377O\0r\377N\0n\377X\0q\377" + "Z\0n\377Z\0i\377O\0t\377T\0g\377I\0h\377L\0w\377<\0\0\377\2027\0\0\377" + "\17>\0\0\377;\0\0\377G\0\0\377I\0\0\377<\0\0\377:\0\0\3772\0\0\377;\0" + "\0\3775\0\0\3779\0\0\377H\0\0\377F\0\0\3779\0\0\377B\0\0\3778\0\0\377" + "\202\"\0\0\377\12(\0\0\377\"\0\0\377\35\0\0\377\26\0\0\377\33\0\0\377" + "\36\0\0\377\35\0\0\377$\0\0\377%\0\0\377\32\0\0\377\202\21\0\0\377\12" + "\23\0\0\377\15\0\0\377\13\0\0\377\17\0\0\377\10\0\0\377\4\0\0\377\6\0" + "\0\377\12\0\0\377\30\0\0\377\24\0\0\377\202\10\0\0\377\1\1\0\0\377\260" + "\0\0\0\377\12\0\0\11\377\0\0\0\377\0\0\7\377\0\0\13\377\0\0\25\377\0" + "\0\14\377\0\0\10\377\0\0\0\377\0\0\5\377\0\0\3\377\204\0\0\0\377\3\0" + "\0\7\377\0\0\1\377\0\0\16\377\202\0\0\11\377\30\0\0\0\377\0\0\5\377\0" + "\0\4\377\0\0\0\377\0\0\17\377\0\0\20\377\0\0\22\377\0\0\40\377\0\0\32" + "\377\0\0\34\377\0\0\21\377\0\0\34\377\0\0#\377\0\0\"\377\0\0\25\377\0" + "\0\31\377\0\0&\377\0\0*\377\0\0\34\377\0\0\21\377\0\0\25\377\0\0\26\377" + "\0\0\25\377\0\0\27\377\202\0\0\26\377|\0\0\24\377\0\0\20\377\0\0\26\377" + "\0\0\32\377\0\0!\377\0\0\26\377\0\0\"\377\0\0\31\377\0\0\35\377\0\0\26" + "\377\0\0\31\377\15\0\25\377\31\0\30\377\23\0\17\377\26\0\37\377\"\0\37" + "\377$\0\"\377)\0\35\377(\0&\377\40\0!\377'\0#\377\33\0&\377\32\0""0\377" + "(\0#\3771\0(\377.\0""3\377#\0>\377-\0G\377$\0@\377!\0=\377,\0:\377\"" + "\0=\377-\0""0\3770\0+\377$\0""3\377,\0'\377#\0*\377#\0""7\377\37\0""9" + "\377#\0:\377,\0;\377.\0:\377\37\0""7\377&\0:\377+\0>\377$\0""1\377#\0" + "5\3772\0""0\3771\0>\3772\0\77\377.\0;\377-\0<\377:\0H\3770\0=\3777\0" + "B\3779\0M\3774\0D\3779\0B\377B\0<\377<\0>\377@\0G\3779\0;\377>\0;\377" + "8\0A\377<\0@\3775\0\77\3778\0:\377@\0""6\3779\0@\377\77\0D\377<\0C\377" + "7\0G\3772\0F\377<\0A\377A\0E\377@\0""4\3776\0F\3775\0I\3779\0F\3779\0" + "M\3778\0_\377B\0]\377>\0a\3773\0e\377;\0i\377;\0]\377<\0W\3771\0e\377" + "5\0`\377.\0i\3771\0b\377&\0U\377\31\0P\377\27\0N\377\30\0^\377\24\0W" + "\377\32\0`\377&\0a\377'\0f\377\30\0_\377#\0_\377\40\0^\377'\0f\377&\0" + "_\377*\0g\3775\0g\377<\0h\377>\0h\3778\0Z\3774\0d\377C\0c\377;\0]\377" + "K\0`\377E\0p\377Z\0v\377L\0h\377T\0n\377^\0j\377V\0o\377Y\0i\377Z\0o" + "\377T\0w\377P\0{\377T\0r\377", +}; + + diff --git a/plugins/GSdx/GSRenderer.cpp b/plugins/GSdx/GSRenderer.cpp index a028e33983..8c17249dd9 100644 --- a/plugins/GSdx/GSRenderer.cpp +++ b/plugins/GSdx/GSRenderer.cpp @@ -25,6 +25,8 @@ GSRenderer::GSRenderer() : m_dev(NULL) , m_shader(0) + , m_shift_key(false) + , m_control_key(false) { m_GStitleInfoBuffer[0] = 0; @@ -421,6 +423,10 @@ void GSRenderer::VSync(int field) shift = !!(::GetAsyncKeyState(VK_SHIFT) & 0x8000); + #else + + shift = m_shift_key; + #endif if(!m_dump && shift) @@ -454,6 +460,10 @@ void GSRenderer::VSync(int field) control = !!(::GetAsyncKeyState(VK_CONTROL) & 0x8000); + #else + + control = m_control_key; + #endif m_dump.VSync(field, !control, m_regs); @@ -514,9 +524,9 @@ void GSRenderer::EndCapture() void GSRenderer::KeyEvent(GSKeyEventData* e) { +#ifdef _WINDOWS if(e->type == KEYPRESS) { - #ifdef _WINDOWS int step = (::GetAsyncKeyState(VK_SHIFT) & 0x8000) ? -1 : 1; @@ -548,10 +558,62 @@ void GSRenderer::KeyEvent(GSKeyEventData* e) return; } - #else - - // TODO: linux - - #endif } +#else + if(e->type == KEYPRESS) + { + int step = m_shift_key ? -1 : 1; + + switch(e->key) + { + case XK_F5: + m_interlace = (m_interlace + 7 + step) % 7; + fprintf(stderr, "GSdx: Set deinterlace mode to %d (%s).\n", (int)m_interlace, theApp.m_gs_interlace.at(m_interlace).name.c_str()); + return; + case XK_F6: + if( m_wnd.IsManaged() ) + m_aspectratio = (m_aspectratio + 3 + step) % 3; + return; + case XK_F7: + m_shader = (m_shader + 3 + step) % 3; + fprintf(stderr,"GSdx: Set shader %d.\n", (int)m_shader); + return; + case XK_Delete: + m_aa1 = !m_aa1; + fprintf(stderr,"GSdx: (Software) aa1 is now %s.\n", m_aa1 ? "enabled" : "disabled"); + return; + case XK_Insert: + m_mipmap = !m_mipmap; + fprintf(stderr,"GSdx: (Software) mipmapping is now %s.\n", m_mipmap ? "enabled" : "disabled"); + return; + case XK_Prior: + m_fxaa = !m_fxaa; + fprintf(stderr,"GSdx: fxaa is now %s.\n", m_fxaa ? "enabled" : "disabled"); + return; + case XK_Shift_L: + case XK_Shift_R: + m_shift_key = true; + return; + case XK_Control_L: + case XK_Control_R: + m_control_key = true; + return; + } + + } + else if(e->type == KEYRELEASE) + { + switch(e->key) + { + case XK_Shift_L: + case XK_Shift_R: + m_shift_key = false; + return; + case XK_Control_L: + case XK_Control_R: + m_control_key = false; + return; + } + } +#endif } diff --git a/plugins/GSdx/GSRenderer.h b/plugins/GSdx/GSRenderer.h index caff24478e..9450bf331d 100644 --- a/plugins/GSdx/GSRenderer.h +++ b/plugins/GSdx/GSRenderer.h @@ -35,6 +35,10 @@ class GSRenderer : public GSState bool Merge(int field); + // Only used on linux + bool m_shift_key; + bool m_control_key; + protected: int m_interlace; int m_aspectratio; diff --git a/plugins/GSdx/GSRendererOGL.cpp b/plugins/GSdx/GSRendererOGL.cpp new file mode 100644 index 0000000000..9aa25b62f9 --- /dev/null +++ b/plugins/GSdx/GSRendererOGL.cpp @@ -0,0 +1,497 @@ +/* + * Copyright (C) 2011-2011 Gregory hainaut + * Copyright (C) 2007-2009 Gabest + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GSRendererOGL.h" +#include "GSRenderer.h" + + +GSRendererOGL::GSRendererOGL() + : GSRendererHW(new GSTextureCacheOGL(this)) +{ + m_logz = !!theApp.GetConfig("logz", 0); + m_fba = !!theApp.GetConfig("fba", 1); + UserHacks_AlphaHack = !!theApp.GetConfig("UserHacks_AlphaHack", 0) && !!theApp.GetConfig("UserHacks", 0); + UserHacks_WildHack = !!theApp.GetConfig("UserHacks", 0) ? theApp.GetConfig("UserHacks_WildHack", 0) : 0; + m_pixelcenter = GSVector2(-0.5f, -0.5f); +} + +bool GSRendererOGL::CreateDevice(GSDevice* dev) +{ + if(!GSRenderer::CreateDevice(dev)) + return false; + + return true; +} + +void GSRendererOGL::SetupIA() +{ + GSDeviceOGL* dev = (GSDeviceOGL*)m_dev; + + void* ptr = NULL; + + dev->IASetVertexState(); + + if(dev->IAMapVertexBuffer(&ptr, sizeof(GSVertex), m_vertex.next)) + { + GSVector4i::storent(ptr, m_vertex.buff, sizeof(GSVertex) * m_vertex.next); + + if(UserHacks_WildHack && !isPackedUV_HackFlag) + { + GSVertex* RESTRICT d = (GSVertex*)ptr; + + for(unsigned int i = 0; i < m_vertex.next; i++, d++) + if(PRIM->TME && PRIM->FST) + d->UV &= UserHacks_WildHack == 1 ? 0x3FEF3FEF : 0x3FF73FF7; + } + + dev->IAUnmapVertexBuffer(); + } + + dev->IASetIndexBuffer(m_index.buff, m_index.tail); + + GLenum t; + + switch(m_vt.m_primclass) + { + case GS_POINT_CLASS: + t = GL_POINTS; + break; + case GS_LINE_CLASS: + case GS_SPRITE_CLASS: + t = GL_LINES; + break; + case GS_TRIANGLE_CLASS: + t = GL_TRIANGLES; + break; + default: + __assume(0); + } + + dev->IASetPrimitiveTopology(t); +} + + +void GSRendererOGL::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* tex) +{ + GSDrawingEnvironment& env = m_env; + GSDrawingContext* context = m_context; + + const GSVector2i& rtsize = rt->GetSize(); + const GSVector2& rtscale = rt->GetScale(); + + bool DATE = m_context->TEST.DATE && context->FRAME.PSM != PSM_PSMCT24; + + //OGL GSTexture* rtcopy = NULL; + + ASSERT(m_dev != NULL); + + GSDeviceOGL* dev = (GSDeviceOGL*)m_dev; + + if(DATE) + { + // Note at the moment OGL has always stencil. Rt can be disabled + if(dev->HasStencil()) + { + GSVector4 s = GSVector4(rtscale.x / rtsize.x, rtscale.y / rtsize.y); + GSVector4 o = GSVector4(-1.0f, 1.0f); + + GSVector4 src = ((m_vt.m_min.p.xyxy(m_vt.m_max.p) + o.xxyy()) * s.xyxy()).sat(o.zzyy()); + GSVector4 dst = src * 2.0f + o.xxxx(); + + GSVertexPT1 vertices[] = + { +#if 0 + {GSVector4(dst.x, -dst.y, 0.5f, 1.0f), GSVector2(src.x, src.y)}, + {GSVector4(dst.z, -dst.y, 0.5f, 1.0f), GSVector2(src.z, src.y)}, + {GSVector4(dst.x, -dst.w, 0.5f, 1.0f), GSVector2(src.x, src.w)}, + {GSVector4(dst.z, -dst.w, 0.5f, 1.0f), GSVector2(src.z, src.w)}, +#else + {GSVector4(dst.x, -dst.w, 0.5f, 1.0f), GSVector2(src.x, src.y)}, + {GSVector4(dst.z, -dst.w, 0.5f, 1.0f), GSVector2(src.z, src.y)}, + {GSVector4(dst.x, -dst.y, 0.5f, 1.0f), GSVector2(src.x, src.w)}, + {GSVector4(dst.z, -dst.y, 0.5f, 1.0f), GSVector2(src.z, src.w)}, +#endif + }; + //fprintf(stderr, "DATE A:%fx%f B:%fx%f\n", dst.x, -dst.y, dst.z, -dst.w); + //fprintf(stderr, "DATE SR: %f %f %f %f\n", src.x, src.y, src.z, src.w); + //fprintf(stderr, "DATE offset: %f\n", o.x); + + dev->SetupDATE(rt, ds, vertices, m_context->TEST.DATM); + } + else + { + //OGL rtcopy = dev->CreateRenderTarget(rtsize.x, rtsize.y, false, rt->GetFormat()); + + //OGL // I'll use VertexTrace when I consider it more trustworthy + + //OGL dev->CopyRect(rt, rtcopy, GSVector4i(rtsize).zwxy()); + } + } + + // + + dev->BeginScene(); + + // om + + GSDeviceOGL::OMDepthStencilSelector om_dssel; + + if(context->TEST.ZTE) + { + om_dssel.ztst = context->TEST.ZTST; + om_dssel.zwe = !context->ZBUF.ZMSK; + } + else + { + om_dssel.ztst = ZTST_ALWAYS; + } + + if(m_fba) + { + om_dssel.fba = context->FBA.FBA; + } + + GSDeviceOGL::OMBlendSelector om_bsel; + + if(!IsOpaque()) + { + om_bsel.abe = PRIM->ABE || PRIM->AA1 && m_vt.m_primclass == GS_LINE_CLASS; + + om_bsel.a = context->ALPHA.A; + om_bsel.b = context->ALPHA.B; + om_bsel.c = context->ALPHA.C; + om_bsel.d = context->ALPHA.D; + + if(env.PABE.PABE) + { + if(om_bsel.a == 0 && om_bsel.b == 1 && om_bsel.c == 0 && om_bsel.d == 1) + { + // this works because with PABE alpha blending is on when alpha >= 0x80, but since the pixel shader + // cannot output anything over 0x80 (== 1.0) blending with 0x80 or turning it off gives the same result + + om_bsel.abe = 0; + } + else + { + //Breath of Fire Dragon Quarter triggers this in battles. Graphics are fine though. + //ASSERT(0); + } + } + } + + om_bsel.wrgba = ~GSVector4i::load((int)context->FRAME.FBMSK).eq8(GSVector4i::xffffffff()).mask(); + + // vs + + GSDeviceOGL::VSSelector vs_sel; + + vs_sel.tme = PRIM->TME; + vs_sel.fst = PRIM->FST; + vs_sel.logz = dev->HasDepth32() ? 0 : m_logz ? 1 : 0; + //OGL vs_sel.rtcopy = !!rtcopy; + vs_sel.rtcopy = false; + + // The real GS appears to do no masking based on the Z buffer format and writing larger Z values + // than the buffer supports seems to be an error condition on the real GS, causing it to crash. + // We are probably receiving bad coordinates from VU1 in these cases. + + if(om_dssel.ztst >= ZTST_ALWAYS && om_dssel.zwe) + { + if(context->ZBUF.PSM == PSM_PSMZ24) + { + if(m_vt.m_max.p.z > 0xffffff) + { + ASSERT(m_vt.m_min.p.z > 0xffffff); + // Fixme :Following conditional fixes some dialog frame in Wild Arms 3, but may not be what was intended. + if (m_vt.m_min.p.z > 0xffffff) + { + vs_sel.bppz = 1; + om_dssel.ztst = ZTST_ALWAYS; + } + } + } + else if(context->ZBUF.PSM == PSM_PSMZ16 || context->ZBUF.PSM == PSM_PSMZ16S) + { + if(m_vt.m_max.p.z > 0xffff) + { + ASSERT(m_vt.m_min.p.z > 0xffff); // sfex capcom logo + // Fixme : Same as above, I guess. + if (m_vt.m_min.p.z > 0xffff) + { + vs_sel.bppz = 2; + om_dssel.ztst = ZTST_ALWAYS; + } + } + } + } + + // FIXME Opengl support half pixel center (as dx10). Code could be easier!!! + GSDeviceOGL::VSConstantBuffer vs_cb; + + float sx = 2.0f * rtscale.x / (rtsize.x << 4); + float sy = 2.0f * rtscale.y / (rtsize.y << 4); + float ox = (float)(int)context->XYOFFSET.OFX; + float oy = (float)(int)context->XYOFFSET.OFY; + float ox2 = 2.0f * m_pixelcenter.x / rtsize.x; + float oy2 = 2.0f * m_pixelcenter.y / rtsize.y; + + //This hack subtracts around half a pixel from OFX and OFY. (Cannot do this directly, + //because DX10 and DX9 have a different pixel center.) + // + //The resulting shifted output aligns better with common blending / corona / blurring effects, + //but introduces a few bad pixels on the edges. + + if(rt->LikelyOffset) + { + // DX9 has pixelcenter set to 0.0, so give it some value here + + if(m_pixelcenter.x == 0 && m_pixelcenter.y == 0) { ox2 = -0.0003f; oy2 = -0.0003f; } + + ox2 *= rt->OffsetHack_modx; + oy2 *= rt->OffsetHack_mody; + } + + vs_cb.VertexScale = GSVector4(sx, -sy, ldexpf(1, -32), 0.0f); + vs_cb.VertexOffset = GSVector4(ox * sx + ox2 + 1, -(oy * sy + oy2 + 1), 0.0f, -1.0f); + // END of FIXME + + // gs + + GSDeviceOGL::GSSelector gs_sel; + + gs_sel.iip = PRIM->IIP; + gs_sel.prim = m_vt.m_primclass; + + // ps + + GSDeviceOGL::PSSelector ps_sel; + GSDeviceOGL::PSSamplerSelector ps_ssel; + GSDeviceOGL::PSConstantBuffer ps_cb; + + if(DATE) + { + if(dev->HasStencil()) + { + om_dssel.date = 1; + } + else + { + ps_sel.date = 1 + context->TEST.DATM; + } + } + + if(env.COLCLAMP.CLAMP == 0 && /* hack */ !tex && PRIM->PRIM != GS_POINTLIST) + { + ps_sel.colclip = 1; + } + + ps_sel.clr1 = om_bsel.IsCLR1(); + ps_sel.fba = context->FBA.FBA; + ps_sel.aout = context->FRAME.PSM == PSM_PSMCT16 || context->FRAME.PSM == PSM_PSMCT16S || (context->FRAME.FBMSK & 0xff000000) == 0x7f000000 ? 1 : 0; + + if(UserHacks_AlphaHack) ps_sel.aout = 1; + + if(PRIM->FGE) + { + ps_sel.fog = 1; + + ps_cb.FogColor_AREF = GSVector4::rgba32(env.FOGCOL.u32[0]) / 255; + } + + if(context->TEST.ATE) + { + ps_sel.atst = context->TEST.ATST; + + switch(ps_sel.atst) + { + case ATST_LESS: + ps_cb.FogColor_AREF.a = (float)((int)context->TEST.AREF - 1); + break; + case ATST_GREATER: + ps_cb.FogColor_AREF.a = (float)((int)context->TEST.AREF + 1); + break; + default: + ps_cb.FogColor_AREF.a = (float)(int)context->TEST.AREF; + break; + } + } + else + { + ps_sel.atst = ATST_ALWAYS; + } + + if(tex) + { + ps_sel.wms = context->CLAMP.WMS; + ps_sel.wmt = context->CLAMP.WMT; + ps_sel.fmt = tex->m_fmt; + ps_sel.aem = env.TEXA.AEM; + ps_sel.tfx = context->TEX0.TFX; + ps_sel.tcc = context->TEX0.TCC; + ps_sel.ltf = m_filter == 2 ? m_vt.IsLinear() : m_filter; + ps_sel.rt = tex->m_target; + + int w = tex->m_texture->GetWidth(); + int h = tex->m_texture->GetHeight(); + + int tw = (int)(1 << context->TEX0.TW); + int th = (int)(1 << context->TEX0.TH); + + GSVector4 WH(tw, th, w, h); + + if(PRIM->FST) + { + vs_cb.TextureScale = GSVector4(1.0f / 16) / WH.xyxy(); + //Maybe better? + //vs_cb.TextureScale = GSVector4(1.0f / 16) * GSVector4(tex->m_texture->GetScale()).xyxy() / WH.zwzw(); + ps_sel.fst = 1; + } + + ps_cb.WH = WH; + ps_cb.HalfTexel = GSVector4(-0.5f, 0.5f).xxyy() / WH.zwzw(); + ps_cb.MskFix = GSVector4i(context->CLAMP.MINU, context->CLAMP.MINV, context->CLAMP.MAXU, context->CLAMP.MAXV); + + GSVector4 clamp(ps_cb.MskFix); + GSVector4 ta(env.TEXA & GSVector4i::x000000ff()); + + ps_cb.MinMax = clamp / WH.xyxy(); + ps_cb.MinF_TA = (clamp + 0.5f).xyxy(ta) / WH.xyxy(GSVector4(255, 255)); + + ps_ssel.tau = (context->CLAMP.WMS + 3) >> 1; + ps_ssel.tav = (context->CLAMP.WMT + 3) >> 1; + ps_ssel.ltf = ps_sel.ltf; + } + else + { + ps_sel.tfx = 4; + } + + // rs + + GSVector4i scissor = GSVector4i(GSVector4(rtscale).xyxy() * context->scissor.in).rintersect(GSVector4i(rtsize).zwxy()); + + dev->OMSetRenderTargets(rt, ds, &scissor); + dev->PSSetShaderResource(0, tex ? tex->m_texture : NULL); + dev->PSSetShaderResource(1, tex ? tex->m_palette : NULL); + //OGL dev->PSSetShaderResource(2, rtcopy); + + uint8 afix = context->ALPHA.FIX; + + SetupIA(); + + dev->SetupOM(om_dssel, om_bsel, afix); + dev->SetupVS(vs_sel, &vs_cb); + dev->SetupGS(gs_sel); + dev->SetupPS(ps_sel, &ps_cb, ps_ssel); + + // draw + + if(context->TEST.DoFirstPass()) + { + dev->DrawIndexedPrimitive(); + + if (env.COLCLAMP.CLAMP == 0 && /* hack */ !tex && PRIM->PRIM != GS_POINTLIST) + { + GSDeviceOGL::OMBlendSelector om_bselneg(om_bsel); + GSDeviceOGL::PSSelector ps_selneg(ps_sel); + + om_bselneg.negative = 1; + ps_selneg.colclip = 2; + + dev->SetupOM(om_dssel, om_bselneg, afix); + dev->SetupPS(ps_selneg, &ps_cb, ps_ssel); + + dev->DrawIndexedPrimitive(); + } + } + + if(context->TEST.DoSecondPass()) + { + ASSERT(!env.PABE.PABE); + + static const uint32 iatst[] = {1, 0, 5, 6, 7, 2, 3, 4}; + + ps_sel.atst = iatst[ps_sel.atst]; + + switch(ps_sel.atst) + { + case ATST_LESS: + ps_cb.FogColor_AREF.a = (float)((int)context->TEST.AREF - 1); + break; + case ATST_GREATER: + ps_cb.FogColor_AREF.a = (float)((int)context->TEST.AREF + 1); + break; + default: + ps_cb.FogColor_AREF.a = (float)(int)context->TEST.AREF; + break; + } + + dev->SetupPS(ps_sel, &ps_cb, ps_ssel); + + bool z = om_dssel.zwe; + bool r = om_bsel.wr; + bool g = om_bsel.wg; + bool b = om_bsel.wb; + bool a = om_bsel.wa; + + switch(context->TEST.AFAIL) + { + case 0: z = r = g = b = a = false; break; // none + case 1: z = false; break; // rgba + case 2: r = g = b = a = false; break; // z + case 3: z = a = false; break; // rgb + default: __assume(0); + } + + if(z || r || g || b || a) + { + om_dssel.zwe = z; + om_bsel.wr = r; + om_bsel.wg = g; + om_bsel.wb = b; + om_bsel.wa = a; + + dev->SetupOM(om_dssel, om_bsel, afix); + + dev->DrawIndexedPrimitive(); + + if (env.COLCLAMP.CLAMP == 0 && /* hack */ !tex && PRIM->PRIM != GS_POINTLIST) + { + GSDeviceOGL::OMBlendSelector om_bselneg(om_bsel); + GSDeviceOGL::PSSelector ps_selneg(ps_sel); + + om_bselneg.negative = 1; + ps_selneg.colclip = 2; + + dev->SetupOM(om_dssel, om_bselneg, afix); + dev->SetupPS(ps_selneg, &ps_cb, ps_ssel); + + dev->DrawIndexedPrimitive(); + } + } + } + + dev->EndScene(); + + //OGL dev->Recycle(rtcopy); + + if(om_dssel.fba) UpdateFBA(rt); +} diff --git a/plugins/GSdx/GSRendererOGL.h b/plugins/GSdx/GSRendererOGL.h new file mode 100644 index 0000000000..4f3ce236b1 --- /dev/null +++ b/plugins/GSdx/GSRendererOGL.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2011-2011 Gregory hainaut + * Copyright (C) 2007-2009 Gabest + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#pragma once + +#include "GSRendererHW.h" + +#include "GSRenderer.h" +#include "GSTextureCacheOGL.h" +#include "GSVertexHW.h" + +// FIXME does it need a GSVertexHWOGL ??? Data order can be easily programmed on opengl (the only potential +// issue is the unsupported praga push/pop +// Note it impact GSVertexTrace.cpp => void GSVertexTrace::Update(const GSVertexHWOGL* v, int count, GS_PRIM_CLASS primclass) +class GSRendererOGL : public GSRendererHW +//class GSRendererOGL : public GSRendererHW +{ + private: + GSVector2 m_pixelcenter; + bool m_logz; + bool m_fba; + bool UserHacks_AlphaHack; + int UserHacks_WildHack; + + protected: + void SetupIA(); + + public: + GSRendererOGL(); + virtual ~GSRendererOGL() {}; + + bool CreateDevice(GSDevice* dev); + + void UpdateFBA(GSTexture* rt) {} + + void DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* tex); +}; diff --git a/plugins/GSdx/GSSetting.h b/plugins/GSdx/GSSetting.h index 503b535bb8..1f8264c771 100644 --- a/plugins/GSdx/GSSetting.h +++ b/plugins/GSdx/GSSetting.h @@ -21,6 +21,8 @@ #pragma once +#include "stdafx.h" + struct GSSetting { uint32 id; diff --git a/plugins/GSdx/GSTexture.h b/plugins/GSdx/GSTexture.h index 43ac3f6bfa..cd779c4c8c 100644 --- a/plugins/GSdx/GSTexture.h +++ b/plugins/GSdx/GSTexture.h @@ -35,7 +35,7 @@ protected: public: struct GSMap {uint8* bits; int pitch;}; - enum {RenderTarget = 1, DepthStencil, Texture, Offscreen}; + enum {RenderTarget = 1, DepthStencil, Texture, Offscreen, Backbuffer}; public: GSTexture(); diff --git a/plugins/GSdx/GSTextureCacheOGL.cpp b/plugins/GSdx/GSTextureCacheOGL.cpp new file mode 100644 index 0000000000..98023ae518 --- /dev/null +++ b/plugins/GSdx/GSTextureCacheOGL.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2011-2011 Gregory hainaut + * Copyright (C) 2007-2009 Gabest + * http://www.gabest.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "stdafx.h" +#include "GSTextureCacheOGL.h" + +GSTextureCacheOGL::GSTextureCacheOGL(GSRenderer* r) + : GSTextureCache(r) +{ +} + +void GSTextureCacheOGL::Read(Target* t, const GSVector4i& r) +{ + if(t->m_type != RenderTarget) + { + assert(0); + + return; + } + + const GIFRegTEX0& TEX0 = t->m_TEX0; + + if(TEX0.PSM != PSM_PSMCT32 + && TEX0.PSM != PSM_PSMCT24 + && TEX0.PSM != PSM_PSMCT16 + && TEX0.PSM != PSM_PSMCT16S) + { + //ASSERT(0); + + return; + } + + if(!t->m_dirty.empty()) + { + return; + } + + // printf("GSRenderTarget::Read %d,%d - %d,%d (%08x)\n", r.left, r.top, r.right, r.bottom, TEX0.TBP0); + + int w = r.width(); + int h = r.height(); + + GSVector4 src = GSVector4(r) * GSVector4(t->m_texture->GetScale()).xyxy() / GSVector4(t->m_texture->GetSize()).xyxy(); + + GLuint format = TEX0.PSM == PSM_PSMCT16 || TEX0.PSM == PSM_PSMCT16S ? GL_R16UI : GL_RGBA8; + //if (format == GL_R16UI) fprintf(stderr, "Format 16 bits integer\n"); +#if 0 + DXGI_FORMAT format = TEX0.PSM == PSM_PSMCT16 || TEX0.PSM == PSM_PSMCT16S ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R8G8B8A8_UNORM; +#endif + + if(GSTexture* offscreen = m_renderer->m_dev->CopyOffscreen(t->m_texture, src, w, h, format)) + { + GSTexture::GSMap m; + + if(offscreen->Map(m)) + { + // TODO: block level write + + GSOffset* o = m_renderer->m_mem.GetOffset(TEX0.TBP0, TEX0.TBW, TEX0.PSM); + + switch(TEX0.PSM) + { + case PSM_PSMCT32: + m_renderer->m_mem.WritePixel32(m.bits, m.pitch, o, r); + break; + case PSM_PSMCT24: + m_renderer->m_mem.WritePixel24(m.bits, m.pitch, o, r); + break; + case PSM_PSMCT16: + case PSM_PSMCT16S: + m_renderer->m_mem.WritePixel16(m.bits, m.pitch, o, r); + break; + default: + ASSERT(0); + } + + offscreen->Unmap(); + } + + m_renderer->m_dev->Recycle(offscreen); + } +} + diff --git a/plugins/GSdx/GSTextureCacheOGL.h b/plugins/GSdx/GSTextureCacheOGL.h new file mode 100644 index 0000000000..f86554c84a --- /dev/null +++ b/plugins/GSdx/GSTextureCacheOGL.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2011-2011 Gregory hainaut + * Copyright (C) 2007-2009 Gabest + * http://www.gabest.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#pragma once + +#include "GSTextureCache.h" +#include "GSDeviceOGL.h" + +class GSTextureCacheOGL : public GSTextureCache +{ +protected: + int Get8bitFormat() { return GL_R8; /* TODO return DXGI_FORMAT_A8_UNORM;*/} + + void Read(Target* t, const GSVector4i& r); + +public: + GSTextureCacheOGL(GSRenderer* r); +}; diff --git a/plugins/GSdx/GSTextureFXOGL.cpp b/plugins/GSdx/GSTextureFXOGL.cpp new file mode 100644 index 0000000000..e7eb694af3 --- /dev/null +++ b/plugins/GSdx/GSTextureFXOGL.cpp @@ -0,0 +1,334 @@ +/* + * Copyright (C) 2011-2011 Gregory hainaut + * Copyright (C) 2007-2009 Gabest + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "stdafx.h" +#include "GSDeviceOGL.h" +#include "GSTables.h" + +void GSDeviceOGL::CreateTextureFX() +{ + m_vs_cb = new GSUniformBufferOGL(4, sizeof(VSConstantBuffer)); + m_ps_cb = new GSUniformBufferOGL(5, sizeof(PSConstantBuffer)); + + glGenSamplers(1, &m_rt_ss); + // FIXME, seem to have no difference between sampler !!! + m_palette_ss = m_rt_ss; + + glSamplerParameteri(m_rt_ss, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glSamplerParameteri(m_rt_ss, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glSamplerParameteri(m_rt_ss, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + glSamplerParameteri(m_rt_ss, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glSamplerParameteri(m_rt_ss, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + // FIXME which value for GL_TEXTURE_MIN_LOD + glSamplerParameteri(m_rt_ss, GL_TEXTURE_MAX_LOD, FLT_MAX); + // FIXME: seems there is 2 possibility in opengl + // DX: sd.ComparisonFunc = D3D11_COMPARISON_NEVER; + // glSamplerParameteri(m_rt_ss, GL_TEXTURE_COMPARE_MODE, GL_NONE); + glSamplerParameteri(m_rt_ss, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); + glSamplerParameteri(m_rt_ss, GL_TEXTURE_COMPARE_FUNC, GL_NEVER); + // FIXME: need ogl extension sd.MaxAnisotropy = 16; + + //{"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0}, + //{"TEXCOORD", 1, DXGI_FORMAT_R32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0}, + //float2 t : TEXCOORD0; + //float q : TEXCOORD1; + // + //{"POSITION", 0, DXGI_FORMAT_R16G16_UINT, 0, 16, D3D11_INPUT_PER_VERTEX_DATA, 0}, + //{"POSITION", 1, DXGI_FORMAT_R32_UINT, 0, 20, D3D11_INPUT_PER_VERTEX_DATA, 0}, + //uint2 p : POSITION0; + //uint z : POSITION1; + // + //{"COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 8, D3D11_INPUT_PER_VERTEX_DATA, 0}, + //{"COLOR", 1, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 28, D3D11_INPUT_PER_VERTEX_DATA, 0}, + //float4 c : COLOR0; + //float4 f : COLOR1; + + GSInputLayoutOGL vert_format[] = + { + // FIXME + {0 , 2 , GL_FLOAT , GL_FALSE , sizeof(GSVertex) , (const GLvoid*)(0) } , + {1 , 4 , GL_UNSIGNED_BYTE , GL_TRUE , sizeof(GSVertex) , (const GLvoid*)(8) } , + {2 , 1 , GL_FLOAT , GL_FALSE , sizeof(GSVertex) , (const GLvoid*)(12) } , + {3 , 2 , GL_UNSIGNED_SHORT , GL_FALSE , sizeof(GSVertex) , (const GLvoid*)(16) } , + {4 , 1 , GL_UNSIGNED_INT , GL_FALSE , sizeof(GSVertex) , (const GLvoid*)(20) } , + // note: there is a 32 bits pad + {5 , 2 , GL_UNSIGNED_SHORT , GL_FALSE , sizeof(GSVertex) , (const GLvoid*)(24) } , + {6 , 4 , GL_UNSIGNED_BYTE , GL_TRUE , sizeof(GSVertex) , (const GLvoid*)(28) } , + }; + m_vb = new GSVertexBufferStateOGL(sizeof(GSVertex), vert_format, countof(vert_format)); +} + +void GSDeviceOGL::SetupVS(VSSelector sel, const VSConstantBuffer* cb) +{ + // ************************************************************* + // Static + // ************************************************************* + auto i = m_vs.find(sel); + + if(i == m_vs.end()) + { + std::string macro = format("#define VS_BPPZ %d\n", sel.bppz) + + format("#define VS_TME %d\n", sel.tme) + + format("#define VS_FST %d\n", sel.fst) + + format("#define VS_RTCOPY %d\n", sel.rtcopy); + + GLuint vs; + CompileShaderFromSource("tfx.glsl", "vs_main", GL_VERTEX_SHADER, &vs, macro); + + m_vs[sel] = vs; + i = m_vs.find(sel); + } + + // ************************************************************* + // Dynamic + // ************************************************************* + if(m_vs_cb_cache.Update(cb)) { + SetUniformBuffer(m_vs_cb); + m_vs_cb->upload(cb); + } + + VSSetShader(i->second); +} + +void GSDeviceOGL::SetupGS(GSSelector sel) +{ + // ************************************************************* + // Static + // ************************************************************* + GLuint gs = 0; +#ifdef AMD_DRIVER_WORKAROUND + if (true) +#else + if(sel.prim > 0 && (sel.iip == 0 || sel.prim == 3)) +#endif + { + auto i = m_gs.find(sel); + + if(i == m_gs.end()) { + std::string macro = format("#define GS_IIP %d\n", sel.iip) + + format("#define GS_PRIM %d\n", sel.prim); + + CompileShaderFromSource("tfx.glsl", "gs_main", GL_GEOMETRY_SHADER, &gs, macro); + + m_gs[sel] = gs; + } else { + gs = i->second; + } + } + // ************************************************************* + // Dynamic + // ************************************************************* + GSSetShader(gs); +} + +void GSDeviceOGL::SetupPS(PSSelector sel, const PSConstantBuffer* cb, PSSamplerSelector ssel) +{ + // ************************************************************* + // Static + // ************************************************************* + GLuint ps; + auto i = m_ps.find(sel); + + if (i == m_ps.end()) + { + std::string macro = format("#define PS_FST %d\n", sel.fst) + + format("#define PS_WMS %d\n", sel.wms) + + format("#define PS_WMT %d\n", sel.wmt) + + format("#define PS_FMT %d\n", sel.fmt) + + format("#define PS_AEM %d\n", sel.aem) + + format("#define PS_TFX %d\n", sel.tfx) + + format("#define PS_TCC %d\n", sel.tcc) + + format("#define PS_ATST %d\n", sel.atst) + + format("#define PS_FOG %d\n", sel.fog) + + format("#define PS_CLR1 %d\n", sel.clr1) + + format("#define PS_FBA %d\n", sel.fba) + + format("#define PS_AOUT %d\n", sel.aout) + + format("#define PS_LTF %d\n", sel.ltf) + + format("#define PS_COLCLIP %d\n", sel.colclip) + + format("#define PS_DATE %d\n", sel.date) + + format("#define PS_SPRITEHACK %d\n", sel.spritehack); + + CompileShaderFromSource("tfx.glsl", "ps_main", GL_FRAGMENT_SHADER, &ps, macro); + + m_ps[sel] = ps; + i = m_ps.find(sel); + } else { + ps = i->second; + } + + // ************************************************************* + // Dynamic + // ************************************************************* + if(m_ps_cb_cache.Update(cb)) { + SetUniformBuffer(m_ps_cb); + m_ps_cb->upload(cb); + } + + GLuint ss0, ss1; + ss0 = ss1 = 0; + if(sel.tfx != 4) + { + if(!(sel.fmt < 3 && sel.wms < 3 && sel.wmt < 3)) + { + ssel.ltf = 0; + } + + auto i = m_ps_ss.find(ssel); + + if(i != m_ps_ss.end()) + { + ss0 = i->second; + } + else + { + // ************************************************************* + // Static + // ************************************************************* + glGenSamplers(1, &ss0); + if (ssel.ltf) { + glSamplerParameteri(ss0, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glSamplerParameteri(ss0, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } else { + glSamplerParameteri(ss0, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glSamplerParameteri(ss0, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } + + // FIXME ensure U -> S, V -> T and W->R + if (ssel.tau) + glSamplerParameteri(ss0, GL_TEXTURE_WRAP_S, GL_REPEAT); + else + glSamplerParameteri(ss0, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + + if (ssel.tav) + glSamplerParameteri(ss0, GL_TEXTURE_WRAP_T, GL_REPEAT); + else + glSamplerParameteri(ss0, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glSamplerParameteri(ss0, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + + // FIXME which value for GL_TEXTURE_MIN_LOD + glSamplerParameteri(m_rt_ss, GL_TEXTURE_MAX_LOD, FLT_MAX); + // FIXME: seems there is 2 possibility in opengl + // DX: sd.ComparisonFunc = D3D11_COMPARISON_NEVER; + // glSamplerParameteri(m_rt_ss, GL_TEXTURE_COMPARE_MODE, GL_NONE); + glSamplerParameteri(m_rt_ss, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); + glSamplerParameteri(m_rt_ss, GL_TEXTURE_COMPARE_FUNC, GL_NEVER); + // FIXME: need ogl extension sd.MaxAnisotropy = 16; + + m_ps_ss[ssel] = ss0; + } + + if(sel.fmt >= 3) + { + ss1 = m_palette_ss; + } + } + + PSSetSamplerState(ss0, ss1, sel.date ? m_rt_ss : 0); + + PSSetShader(ps); +} + +void GSDeviceOGL::SetupOM(OMDepthStencilSelector dssel, OMBlendSelector bsel, uint8 afix) +{ + auto i = m_om_dss.find(dssel); + + // ************************************************************* + // Static + // ************************************************************* + if (i == m_om_dss.end()) + { + GSDepthStencilOGL* dss = new GSDepthStencilOGL(); + + if (dssel.date) + { + dss->EnableStencil(); + dss->SetStencil(GL_EQUAL, GL_KEEP); + } + + if(dssel.ztst != ZTST_ALWAYS || dssel.zwe) + { + static const GLenum ztst[] = + { + GL_NEVER, + GL_ALWAYS, + GL_GEQUAL, + GL_GREATER + }; + dss->EnableDepth(); + dss->SetDepth(ztst[dssel.ztst], dssel.zwe); + } + + m_om_dss[dssel] = dss; + i = m_om_dss.find(dssel); + } + + // ************************************************************* + // Dynamic + // ************************************************************* + OMSetDepthStencilState(i->second, 1); + + // ************************************************************* + // Static + // ************************************************************* + auto j = m_om_bs.find(bsel); + + if(j == m_om_bs.end()) + { + GSBlendStateOGL* bs = new GSBlendStateOGL(); + + if(bsel.abe) + { + int i = ((bsel.a * 3 + bsel.b) * 3 + bsel.c) * 3 + bsel.d; + + bs->EnableBlend(); + bs->SetRGB(m_blendMapD3D9[i].op, m_blendMapD3D9[i].src, m_blendMapD3D9[i].dst); + + if(m_blendMapD3D9[i].bogus == 1) + { + if (bsel.a == 0) + bs->SetRGB(m_blendMapD3D9[i].op, GL_ONE, m_blendMapD3D9[i].dst); + else + bs->SetRGB(m_blendMapD3D9[i].op, m_blendMapD3D9[i].src, GL_ONE); + + const string afixstr = format("%d >> 7", afix); + const char *col[3] = {"Cs", "Cd", "0"}; + const char *alpha[3] = {"As", "Ad", afixstr.c_str()}; + + // FIXME, need to investigate OGL capabilities. Maybe for OGL5 ;) + fprintf(stderr, "Impossible blend for D3D: (%s - %s) * %s + %s\n", col[bsel.a], col[bsel.b], alpha[bsel.c], col[bsel.d]); + } + + // Not very good but I don't wanna write another 81 row table + if(bsel.negative) bs->RevertOp(); + } + + bs->SetMask(bsel.wr, bsel.wg, bsel.wb, bsel.wa); + + m_om_bs[bsel] = bs; + j = m_om_bs.find(bsel); + } + + // ************************************************************* + // Dynamic + // ************************************************************* + OMSetBlendState(j->second, (float)(int)afix / 0x80); +} diff --git a/plugins/GSdx/GSTextureOGL.cpp b/plugins/GSdx/GSTextureOGL.cpp new file mode 100644 index 0000000000..73aef4a5d9 --- /dev/null +++ b/plugins/GSdx/GSTextureOGL.cpp @@ -0,0 +1,448 @@ +/* + * Copyright (C) 2011-2011 Gregory hainaut + * Copyright (C) 2007-2009 Gabest + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#pragma once + +#include "GSTextureOGL.h" +static int g_state_texture_unit = -1; +static int g_state_texture_id = -1; + +GSTextureOGL::GSTextureOGL(int type, int w, int h, bool msaa, int format, GLuint fbo_read) + : m_pbo_id(0), + m_pbo_size(0) +{ + // ************************************************************* + // Opengl world + + // Render work in parallal with framebuffer object (FBO) http://www.opengl.org/wiki/Framebuffer_Objects + // render are attached to FBO through : glFramebufferRenderbuffer. You can query the number of colorattachement with GL_MAX_COLOR_ATTACHMENTS + // FBO : constructor -> glGenFramebuffers, glDeleteFramebuffers + // binding -> glBindFramebuffer (target can be read/write/both) + // blit -> glBlitFramebuffer (read FB to draw FB) + // info -> glIsFramebuffer, glGetFramebufferAttachmentParameter, glCheckFramebufferStatus + // + // There are two types of framebuffer-attachable images; texture images and renderbuffer images. + // If an image of a texture object is attached to a framebuffer, OpenGL performs "render to texture". + // And if an image of a renderbuffer object is attached to a framebuffer, then OpenGL performs "offscreen rendering". + // Blitting: + // glDrawBuffers + // glReadBuffer + // glBlitFramebuffer + + // ************************************************************* + // m_size.x = w; + // m_size.y = h; + // FIXME + m_size.x = max(1,w); + m_size.y = max(1,h); + m_format = format; + m_type = type; + m_msaa = msaa; + m_fbo_read = fbo_read; + + // Generate the buffer + switch (m_type) { + case GSTexture::Offscreen: + //FIXME I not sure we need a pixel buffer object. It seems more a texture + // glGenBuffers(1, &m_texture_id); + // m_texture_target = GL_PIXEL_UNPACK_BUFFER; + // assert(0); + // Note there is also a buffer texture!!! + // http://www.opengl.org/wiki/Buffer_Texture + // Note: in this case it must use in GLSL + // gvec texelFetch(gsampler sampler, ivec texCoord, int lod[, int sample]); + // corollary we can maybe use it for multisample stuff + case GSTexture::Texture: + case GSTexture::RenderTarget: + case GSTexture::DepthStencil: + glGenTextures(1, &m_texture_id); + m_texture_target = GL_TEXTURE_2D; + break; + case GSTexture::Backbuffer: + m_texture_target = 0; + m_texture_id = 0; + default: + break; + } + // Extra buffer to handle various pixel transfer + glGenBuffers(1, &m_pbo_id); + + uint msaa_level; + if (m_msaa) { + // FIXME which level of MSAA + msaa_level = 1; + } else { + msaa_level = 0; + } + + // Allocate the buffer + switch (m_type) { + case GSTexture::DepthStencil: + EnableUnit(0); + glTexImage2D(m_texture_target, 0, m_format, m_size.x, m_size.y, 0, GL_DEPTH_STENCIL, GL_FLOAT_32_UNSIGNED_INT_24_8_REV, NULL); + break; + + case GSTexture::Offscreen: + // Allocate a pbo with the texture + if (m_format == GL_RGBA8) m_pbo_size = m_size.x * m_size.y * 4; + else if (m_format == GL_R16UI) m_pbo_size = m_size.x * m_size.y * 2; + else { + fprintf(stderr, "wrong texture pixel format :%x\n", m_format); + assert(0); // TODO Later + } + + glBindBuffer(GL_PIXEL_PACK_BUFFER, m_pbo_id); + glBufferData(GL_PIXEL_PACK_BUFFER, m_pbo_size, NULL, GL_STREAM_DRAW); + glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); + + case GSTexture::RenderTarget: + case GSTexture::Texture: + // FIXME + // Howto allocate the texture unit !!! + // In worst case the HW renderer seems to use 3 texture unit + // For the moment SW renderer only use 1 so don't bother + EnableUnit(0); + // Did we need to setup a default sampler of the texture now? + // 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_MAG_FILTER, GL_LINEAR); + // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + if (m_format == GL_RGBA8) + glTexImage2D(m_texture_target, 0, m_format, m_size.x, m_size.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + else if (m_format == GL_R16UI) + glTexImage2D(m_texture_target, 0, m_format, m_size.x, m_size.y, 0, GL_RED_INTEGER, GL_UNSIGNED_SHORT, NULL); + else if (m_format == GL_R8) + glTexImage2D(m_texture_target, 0, m_format, m_size.x, m_size.y, 0, GL_RED, GL_UNSIGNED_BYTE, NULL); + else { + fprintf(stderr, "wrong texture pixel format :%x\n", m_format); + assert(0); // TODO Later + } + break; + default: break; + } +} + +GSTextureOGL::~GSTextureOGL() +{ + glDeleteBuffers(1, &m_pbo_id); + glDeleteTextures(1, &m_texture_id); +} + +void GSTextureOGL::Attach(GLenum attachment) +{ + //fprintf(stderr, "format %d,%x\n", m_type, m_format); + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attachment, m_texture_target, m_texture_id, 0); + // FIXME DEBUG + //fprintf(stderr, "FB status %x\n", glCheckFramebufferStatus(GL_FRAMEBUFFER)); +} + +bool GSTextureOGL::Update(const GSVector4i& r, const void* data, int pitch) +{ + if (m_type == GSTexture::DepthStencil || m_type == GSTexture::Offscreen) assert(0); + + // FIXME warning order of the y axis + // FIXME I'm not confident with GL_UNSIGNED_BYTE type + + EnableUnit(0); + + // pitch could be different of width*element_size + glPixelStorei(GL_UNPACK_ROW_LENGTH, pitch>>2); + // FIXME : it crash on colin mcrae rally 3 (others game too) when the size is 16 + //fprintf(stderr, "Texture %dx%d with a pitch of %d\n", m_size.x, m_size.y, pitch >>2); + //fprintf(stderr, "Box (%d,%d)x(%d,%d)\n", r.x, r.y, r.width(), r.height()); + + if (m_format == GL_RGBA8) + glTexSubImage2D(m_texture_target, 0, r.x, r.y, r.width(), r.height(), GL_RGBA, GL_UNSIGNED_BYTE, data); + else if (m_format == GL_R16UI) + glTexSubImage2D(m_texture_target, 0, r.x, r.y, r.width(), r.height(), GL_RED_INTEGER, GL_R16UI, data); + else if (m_format == GL_R8) + glTexSubImage2D(m_texture_target, 0, r.x, r.y, r.width(), r.height(), GL_RED, GL_R8, data); + else { + fprintf(stderr, "wrong texture pixel format :%x\n", m_format); + assert(0); + } +#if 0 + //if (m_size.x != 16) + if (r.width() > 16 && r.height() > 16) + glTexSubImage2D(m_texture_target, 0, r.x, r.y, r.width(), r.height(), GL_RGBA, GL_UNSIGNED_BYTE, data); + else { + fprintf(stderr, "skip texture upload\n"); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); // Restore default behavior + return false; + } +#endif + + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); // Restore default behavior + + return true; +#if 0 + if(m_dev && m_texture) + { + D3D11_BOX box = {r.left, r.top, 0, r.right, r.bottom, 1}; + + m_ctx->UpdateSubresource(m_texture, 0, &box, data, pitch, 0); + + return true; + } + + return false; +#endif +} + +void GSTextureOGL::EnableUnit(uint unit) +{ + if (!IsBackbuffer()) { + // FIXME + // Howto allocate the texture unit !!! + // In worst case the HW renderer seems to use 3 texture unit + // For the moment SW renderer only use 1 so don't bother + if (g_state_texture_unit != unit) { + g_state_texture_unit = unit; + glActiveTexture(GL_TEXTURE0 + unit); + // When you change the texture unit, texture must be rebinded + g_state_texture_id = m_texture_id; + glBindTexture(m_texture_target, m_texture_id); + } else if (g_state_texture_id != m_texture_id) { + g_state_texture_id = m_texture_id; + glBindTexture(m_texture_target, m_texture_id); + } + } +} + +bool GSTextureOGL::Map(GSMap& m, const GSVector4i* r) +{ + // The function allow to modify the texture from the CPU + // Set m.bits <- pointer to the data + // Set m.pitch <- size of a row + // I think in opengl we need to copy back the data to the RAM: glReadPixels — read a block of pixels from the frame buffer + // + // glMapBuffer — map a buffer object's data store + // Can be used on GL_PIXEL_UNPACK_BUFFER or GL_TEXTURE_BUFFER + if (m_type == GSTexture::Offscreen) { + // Bind the texture to the read framebuffer to avoid any disturbance + EnableUnit(0); + glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo_read); + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_texture_target, m_texture_id, 0); + glReadBuffer(GL_COLOR_ATTACHMENT0); + + // FIXME It might be possible to only read a subrange of the texture based on r object + // Load the PBO with the data + glBindBuffer(GL_PIXEL_PACK_BUFFER, m_pbo_id); + if (m_format == GL_RGBA8) { + glPixelStorei(GL_PACK_ALIGNMENT, 4); + glReadPixels(0, 0, m_size.x, m_size.y, GL_RGBA, GL_UNSIGNED_BYTE, 0); + } else if (m_format == GL_R16UI) { + glPixelStorei(GL_PACK_ALIGNMENT, 2); + glReadPixels(0, 0, m_size.x, m_size.y, GL_RED_INTEGER, GL_UNSIGNED_SHORT, 0); + } else if (m_format == GL_R8) { + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glReadPixels(0, 0, m_size.x, m_size.y, GL_RED, GL_UNSIGNED_BYTE, 0); + } else { + fprintf(stderr, "wrong texture pixel format :%x\n", m_format); + assert(0); + } + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + + // Give access from the CPU + m.bits = (uint8*) glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, m_pbo_size, GL_MAP_READ_BIT); + m.pitch = m_size.x; + + if ( m.bits ) { + return true; + } else { + fprintf(stderr, "bad mapping of the pbo\n"); + glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); + return false; + } + } + + return false; +#if 0 + if(m_texture && m_desc.Usage == D3D11_USAGE_STAGING) + { + D3D11_MAPPED_SUBRESOURCE map; + + if(SUCCEEDED(m_ctx->Map(m_texture, 0, D3D11_MAP_READ_WRITE, 0, &map))) + { + m.bits = (uint8*)map.pData; + m.pitch = (int)map.RowPitch; + + return true; + } + } + + return false; +#endif +} + +void GSTextureOGL::Unmap() +{ + if (m_type == GSTexture::Offscreen) { + glUnmapBuffer(GL_PIXEL_PACK_BUFFER); + glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); + + } +} + +#ifndef _WINDOWS + +#pragma pack(push, 1) + +struct BITMAPFILEHEADER +{ + uint16 bfType; + uint32 bfSize; + uint16 bfReserved1; + uint16 bfReserved2; + uint32 bfOffBits; +}; + +struct BITMAPINFOHEADER +{ + uint32 biSize; + int32 biWidth; + int32 biHeight; + uint16 biPlanes; + uint16 biBitCount; + uint32 biCompression; + uint32 biSizeImage; + int32 biXPelsPerMeter; + int32 biYPelsPerMeter; + uint32 biClrUsed; + uint32 biClrImportant; +}; + +#define BI_RGB 0 + +#pragma pack(pop) + +#endif +void GSTextureOGL::Save(const string& fn, const void* image, uint32 pitch) +{ + // Build a BMP file + FILE* fp = fopen(fn.c_str(), "wb"); + + BITMAPINFOHEADER bih; + + memset(&bih, 0, sizeof(bih)); + + bih.biSize = sizeof(bih); + bih.biWidth = m_size.x; + bih.biHeight = m_size.y; + bih.biPlanes = 1; + bih.biBitCount = 32; + bih.biCompression = BI_RGB; + bih.biSizeImage = m_size.x * m_size.y << 2; + + BITMAPFILEHEADER bfh; + + memset(&bfh, 0, sizeof(bfh)); + + uint8* bfType = (uint8*)&bfh.bfType; + + // bfh.bfType = 'MB'; + bfType[0] = 0x42; + bfType[1] = 0x4d; + bfh.bfOffBits = sizeof(bfh) + sizeof(bih); + bfh.bfSize = bfh.bfOffBits + bih.biSizeImage; + bfh.bfReserved1 = bfh.bfReserved2 = 0; + + fwrite(&bfh, 1, sizeof(bfh), fp); + fwrite(&bih, 1, sizeof(bih), fp); + + uint8* data = (uint8*)image + (m_size.y - 1) * pitch; + + for(int h = m_size.y; h > 0; h--, data -= pitch) + { + if (IsDss()) { + // Only get the depth and convert it to an integer + uint8* better_data = data; + for (int w = m_size.x; w > 0; w--, better_data += 8) { + float* input = (float*)better_data; + // FIXME how to dump 32 bits value into 8bits component color + uint32 depth = (uint32)ldexpf(*input, 32); + uint8 small_depth = depth >> 24; + uint8 better_data[4] = {small_depth, small_depth, small_depth, 0 }; + fwrite(&better_data, 1, 4, fp); + } + } else { + // swap red and blue + uint8* better_data = data; + for (int w = m_size.x; w > 0; w--, better_data += 4) { + uint8 red = better_data[2]; + better_data[2] = better_data[0]; + better_data[0] = red; + fwrite(better_data, 1, 4, fp); + } + } + } + + fclose(fp); +} + +bool GSTextureOGL::Save(const string& fn, bool dds) +{ + // Collect the texture data + uint32 pitch = 4 * m_size.x; + if (IsDss()) pitch *= 2; + char* image = (char*)malloc(pitch * m_size.y); + bool status = true; + + // FIXME instead of swapping manually B and R maybe you can request the driver to do it + // for us + if (IsBackbuffer()) { + //glReadBuffer(GL_BACK); + //glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + glReadPixels(0, 0, m_size.x, m_size.y, GL_RGBA, GL_UNSIGNED_BYTE, image); + } else if(IsDss()) { + EnableUnit(0); + glGetTexImage(m_texture_target, 0, GL_DEPTH_STENCIL, GL_FLOAT_32_UNSIGNED_INT_24_8_REV, image); + } else { + glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo_read); + + EnableUnit(0); + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_texture_target, m_texture_id, 0); + + glReadBuffer(GL_COLOR_ATTACHMENT0); + if (m_format == GL_RGBA8) + glReadPixels(0, 0, m_size.x, m_size.y, GL_RGBA, GL_UNSIGNED_BYTE, image); + else if (m_format == GL_R16UI) + { + glReadPixels(0, 0, m_size.x, m_size.y, GL_RED_INTEGER, GL_UNSIGNED_SHORT, image); + // Not supported in Save function + status = false; + } + else if (m_format == GL_R8) + { + glReadPixels(0, 0, m_size.x, m_size.y, GL_RED, GL_UNSIGNED_BYTE, image); + // Not supported in Save function + status = false; + } + + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + } + + if (status) Save(fn, image, pitch); + free(image); + + return status; +} + diff --git a/plugins/GSdx/GSTextureOGL.h b/plugins/GSdx/GSTextureOGL.h new file mode 100644 index 0000000000..017431a128 --- /dev/null +++ b/plugins/GSdx/GSTextureOGL.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2011-2011 Gregory hainaut + * Copyright (C) 2007-2009 Gabest + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#pragma once + +#include "GSTexture.h" + +class GSTextureOGL : public GSTexture +{ + private: + GLenum m_texture_target; // texture target: 2D, rectangle etc... + GLuint m_texture_id; // the texture id + uint m_pbo_id; + int m_pbo_size; + GLuint m_fbo_read; + + public: + explicit GSTextureOGL(int type, int w, int h, bool msaa, int format, GLuint fbo_read); + virtual ~GSTextureOGL(); + + bool Update(const GSVector4i& r, const void* data, int pitch); + bool Map(GSMap& m, const GSVector4i* r = NULL); + void Unmap(); + bool Save(const string& fn, bool dds = false); + void Save(const string& fn, const void* image, uint32 pitch); + + void EnableUnit(uint unit); + void Attach(GLenum attachment); + + bool IsBackbuffer() { return (m_type == GSTexture::Backbuffer); } + bool IsDss() { return (m_type == GSTexture::DepthStencil); } + + GLuint GetID() { return m_texture_id; } + GLenum GetTarget() { return m_texture_target; } +}; diff --git a/plugins/GSdx/GSVertex.h b/plugins/GSdx/GSVertex.h index 1976a82669..94457f9560 100644 --- a/plugins/GSdx/GSVertex.h +++ b/plugins/GSdx/GSVertex.h @@ -52,7 +52,7 @@ struct GSVertexP GSVector4 p; }; -struct GSVertexPT1 +__aligned(struct, 32) GSVertexPT1 { GSVector4 p; GSVector2 t; diff --git a/plugins/GSdx/GSVertexList.h b/plugins/GSdx/GSVertexList.h index adc6efda1d..e300ab88f1 100644 --- a/plugins/GSdx/GSVertexList.h +++ b/plugins/GSdx/GSVertexList.h @@ -33,7 +33,7 @@ public: { m_base = _aligned_malloc(sizeof(Vertex) * countof(m_v), 32); - for(int i = 0; i < countof(m_v); i++) + for(uint i = 0; i < countof(m_v); i++) { m_v[i] = &((Vertex*)m_base)[i]; } diff --git a/plugins/GSdx/GSWnd.cpp b/plugins/GSdx/GSWnd.cpp index 02afc92ad7..aee95c24ba 100644 --- a/plugins/GSdx/GSWnd.cpp +++ b/plugins/GSdx/GSWnd.cpp @@ -212,105 +212,6 @@ void GSWnd::HideFrame() #else -/* -GSWnd::GSWnd() - : m_display(NULL) - , m_window(0) - , m_managed(true) - , m_frame(true) -{ -} - -GSWnd::~GSWnd() -{ - if(m_display != NULL) - { - if(m_window != 0) - { - XDestroyWindow(m_display, m_window); - } - - XCloseDisplay(m_display); - } -} - -bool GSWnd::Create(const string& title, int w, int h) -{ - if(m_display != NULL) return false; - - if(!XInitThreads()) return false; - - m_display = XOpenDisplay(0); - - if(m_display == NULL) return false; - - m_window = XCreateSimpleWindow(m_display, RootWindow(m_display, 0), 0, 0, 640, 480, 0, BlackPixel(m_display, 0), BlackPixel(m_display, 0)); - - XFlush(m_display); - - return true; -} - -GSVector4i GSWnd::GetClientRect() -{ - int x, y; - unsigned int w, h; - unsigned int border, depth; - Window root; - - XLockDisplay(m_display); - XGetGeometry(m_display, m_window, &root, &x, &y, &w, &h, &border, &depth); - XUnlockDisplay(m_display); - - return GSVector4i(0, 0, w, h); -} - -// Returns FALSE if the window has no title, or if th window title is under the strict -// management of the emulator. - -bool GSWnd::SetWindowText(const char* title) -{ - if(!m_managed) return false; - - XTextProperty p; - - p.value = (unsigned char*)title; - p.encoding = XA_STRING; - p.format = 8; - p.nitems = strlen(title); - - XSetWMName(m_display, m_window, &p); - XFlush(m_display); - - return m_frame; -} - -void GSWnd::Show() -{ - if(!m_managed) return; - - XMapWindow(m_display, m_window); - XFlush(m_display); -} - -void GSWnd::Hide() -{ - if(!m_managed) return; - - XUnmapWindow(m_display, m_window); - XFlush(m_display); -} - -void GSWnd::HideFrame() -{ - if(!m_managed) return; - - // TODO - - m_frame = false; -} -*/ - GSWnd::GSWnd() : m_window(NULL), m_Xwindow(0), m_XDisplay(NULL) { @@ -318,22 +219,104 @@ GSWnd::GSWnd() GSWnd::~GSWnd() { +#ifdef ENABLE_SDL_DEV if(m_window != NULL && m_managed) { SDL_DestroyWindow(m_window); m_window = NULL; } +#endif if (m_XDisplay) { XCloseDisplay(m_XDisplay); m_XDisplay = NULL; } } +bool GSWnd::CreateContext(int major, int minor) +{ + if ( !m_XDisplay || !m_Xwindow ) + { + fprintf( stderr, "Wrong X11 display/window\n" ); + exit(1); + } + + // Get visual information + static int attrListDbl[] = + { + GLX_X_RENDERABLE , True, + GLX_DRAWABLE_TYPE , GLX_WINDOW_BIT, + GLX_RENDER_TYPE , GLX_RGBA_BIT, + GLX_RED_SIZE , 8, + GLX_GREEN_SIZE , 8, + GLX_BLUE_SIZE , 8, + GLX_DEPTH_SIZE , 24, + GLX_DOUBLEBUFFER , True, + None + }; + + PFNGLXCHOOSEFBCONFIGPROC glXChooseFBConfig = (PFNGLXCHOOSEFBCONFIGPROC) glXGetProcAddress((GLubyte *) "glXChooseFBConfig"); + int fbcount = 0; + GLXFBConfig *fbc = glXChooseFBConfig(m_XDisplay, DefaultScreen(m_XDisplay), attrListDbl, &fbcount); + if (!fbc || fbcount < 1) return false; + + PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC)glXGetProcAddress((const GLubyte*) "glXCreateContextAttribsARB"); + if (!glXCreateContextAttribsARB) return false; + + // Create a context + int context_attribs[] = + { + GLX_CONTEXT_MAJOR_VERSION_ARB, major, + GLX_CONTEXT_MINOR_VERSION_ARB, minor, + // FIXME : Request a debug context to ease opengl development + // Note: don't support deprecated feature (pre openg 3.1) + //GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_DEBUG_BIT_ARB | GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, + GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_DEBUG_BIT_ARB, + None + }; + + m_context = glXCreateContextAttribsARB(m_XDisplay, fbc[0], 0, true, context_attribs); + if (!m_context) return false; + + XSync( m_XDisplay, false); +} + +void GSWnd::AttachContext() +{ + glXMakeCurrent(m_XDisplay, m_Xwindow, m_context); +} + +void GSWnd::DetachContext() +{ + glXMakeCurrent(m_XDisplay, None, NULL); +} + +void GSWnd::CheckContext() +{ + int glxMajorVersion, glxMinorVersion; + glXQueryVersion(m_XDisplay, &glxMajorVersion, &glxMinorVersion); + if (glXIsDirect(m_XDisplay, m_context)) + fprintf(stderr, "glX-Version %d.%d with Direct Rendering\n", glxMajorVersion, glxMinorVersion); + else + fprintf(stderr, "glX-Version %d.%d with Indirect Rendering !!! It will be slow\n", glxMajorVersion, glxMinorVersion); +} + bool GSWnd::Attach(void* handle, bool managed) { m_Xwindow = *(Window*)handle; m_managed = managed; + m_renderer = theApp.GetConfig("renderer", 0) / 3; + if (m_renderer != 2) { + m_XDisplay = XOpenDisplay(NULL); + + // Note: 4.2 crash on latest nvidia drivers! + if (!CreateContext(3, 3)) return false; + + AttachContext(); + + CheckContext(); + } + return true; } @@ -341,10 +324,17 @@ void GSWnd::Detach() { // Actually the destructor is not called when there is only a GSclose/GSshutdown // The window still need to be closed - if(m_window != NULL && m_managed) - { - SDL_DestroyWindow(m_window); - m_window = NULL; + if (m_renderer == 2) { +#ifdef ENABLE_SDL_DEV + if(m_window != NULL && m_managed) + { + SDL_DestroyWindow(m_window); + m_window = NULL; + } +#endif + } else { + DetachContext(); + if (m_context) glXDestroyContext(m_XDisplay, m_context); } if (m_XDisplay) { XCloseDisplay(m_XDisplay); @@ -361,40 +351,83 @@ bool GSWnd::Create(const string& title, int w, int h) h = theApp.GetConfig("ModeHeight", 480); } -#ifdef _LINUX - // When you reconfigure the plugins during the play, SDL is shutdown so SDL_GetNumVideoDisplays return 0 - // and the plugins is badly closed. NOTE: SDL is initialized in SDL_CreateWindow. - // - // I'm not sure this sanity check is still useful, normally (I hope) SDL_CreateWindow will return a null - // hence a false for this current function. - // For the moment do an init -- Gregory - if(!SDL_WasInit(SDL_INIT_VIDEO)) - if(SDL_Init(SDL_INIT_VIDEO) < 0) return false; + m_managed = true; - // Sanity check; if there aren't any video displays available, we can't create a window. - if (SDL_GetNumVideoDisplays() <= 0) return false; + if (m_renderer == 2) { +#ifdef ENABLE_SDL_DEV + +#ifdef _LINUX + // When you reconfigure the plugins during the play, SDL is shutdown so SDL_GetNumVideoDisplays return 0 + // and the plugins is badly closed. NOTE: SDL is initialized in SDL_CreateWindow. + // + // I'm not sure this sanity check is still useful, normally (I hope) SDL_CreateWindow will return a null + // hence a false for this current function. + // For the moment do an init -- Gregory + if(!SDL_WasInit(SDL_INIT_VIDEO)) + if(SDL_Init(SDL_INIT_VIDEO) < 0) return false; + + // Sanity check; if there aren't any video displays available, we can't create a window. + if (SDL_GetNumVideoDisplays() <= 0) return false; #endif - m_managed = true; - m_window = SDL_CreateWindow(title.c_str(), 100, 100, w, h, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE); + m_window = SDL_CreateWindow(title.c_str(), 100, 100, w, h, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE); - // Get the X window from the newly created window - // It would be needed to get the current size - SDL_SysWMinfo wminfo; - memset(&wminfo, 0, sizeof(wminfo)); + // Get the X window from the newly created window + // It would be needed to get the current size + SDL_SysWMinfo wminfo; + memset(&wminfo, 0, sizeof(wminfo)); - wminfo.version.major = SDL_MAJOR_VERSION; - wminfo.version.minor = SDL_MINOR_VERSION; + wminfo.version.major = SDL_MAJOR_VERSION; + wminfo.version.minor = SDL_MINOR_VERSION; - SDL_GetWindowWMInfo(m_window, &wminfo); - m_Xwindow = wminfo.info.x11.window; + SDL_GetWindowWMInfo(m_window, &wminfo); + m_Xwindow = wminfo.info.x11.window; +#endif + return (m_window != NULL); - return (m_window != NULL); + } else { + + // note this part must be only executed when replaying .gs debug file + m_XDisplay = XOpenDisplay(NULL); + + int attrListDbl[] = { GLX_RGBA, GLX_DOUBLEBUFFER, + GLX_RED_SIZE, 8, + GLX_GREEN_SIZE, 8, + GLX_BLUE_SIZE, 8, + GLX_DEPTH_SIZE, 24, + None + }; + XVisualInfo* vi = glXChooseVisual(m_XDisplay, DefaultScreen(m_XDisplay), attrListDbl); + + /* create a color map */ + XSetWindowAttributes attr; + attr.colormap = XCreateColormap(m_XDisplay, RootWindow(m_XDisplay, vi->screen), + vi->visual, AllocNone); + attr.border_pixel = 0; + attr.event_mask = ExposureMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | + StructureNotifyMask | SubstructureRedirectMask | SubstructureNotifyMask | + EnterWindowMask | LeaveWindowMask | FocusChangeMask ; + + // Create a window at the last position/size + m_Xwindow = XCreateWindow(m_XDisplay, RootWindow(m_XDisplay, vi->screen), + 0 , 0 , w, h, 0, vi->depth, InputOutput, vi->visual, + CWBorderPixel | CWColormap | CWEventMask, &attr); + + XMapWindow (m_XDisplay, m_Xwindow); + XFree(vi); + + if (!CreateContext(3, 3)) return false; + + AttachContext(); + + return (m_Xwindow != 0); + } } Display* GSWnd::GetDisplay() { +#ifdef ENABLE_SDL_DEV SDL_SysWMinfo wminfo; memset(&wminfo, 0, sizeof(wminfo)); @@ -405,6 +438,10 @@ Display* GSWnd::GetDisplay() SDL_GetWindowWMInfo(m_window, &wminfo); return wminfo.subsystem == SDL_SYSWM_X11 ? wminfo.info.x11.display : NULL; +#else + // note this part must be only executed when replaying .gs debug file + return m_XDisplay; +#endif } GSVector4i GSWnd::GetClientRect() @@ -425,7 +462,10 @@ GSVector4i GSWnd::GetClientRect() // In real world...: if (!m_XDisplay) m_XDisplay = XOpenDisplay(NULL); XGetGeometry(m_XDisplay, m_Xwindow, &winDummy, &xDummy, &yDummy, &w, &h, &borderDummy, &depthDummy); - SDL_SetWindowSize(m_window, w, h); +#ifdef ENABLE_SDL_DEV + if (m_renderer == 2) + SDL_SetWindowSize(m_window, w, h); +#endif return GSVector4i(0, 0, (int)w, (int)h); } @@ -437,6 +477,9 @@ bool GSWnd::SetWindowText(const char* title) { if (!m_managed) return true; + if (m_renderer == 2) { +#ifdef ENABLE_SDL_DEV + // Do not find anyway to check the current fullscreen status // Better than nothing heuristic, check the window position. Fullscreen = (0,0) // if(!(m_window->flags & SDL_WINDOW_FULLSCREEN) ) // Do not compile @@ -445,23 +488,67 @@ bool GSWnd::SetWindowText(const char* title) // but we not use this function anyway. // FIXME: it does not feel a good solution -- Gregory // NOte: it might be more thread safe to use a call to XGetGeometry - SDL_PumpEvents(); - int x,y = 0; - SDL_GetWindowPosition(m_window, &x, &y); - if ( x && y ) - SDL_SetWindowTitle(m_window, title); + int x,y = 0; + SDL_PumpEvents(); + SDL_GetWindowPosition(m_window, &x, &y); + if ( x && y ) + SDL_SetWindowTitle(m_window, title); + +#endif + } else { + XTextProperty prop; + + memset(&prop, 0, sizeof(prop)); + + char* ptitle = (char*)title; + if (XStringListToTextProperty(&ptitle, 1, &prop)) { + XSetWMName(m_XDisplay, m_Xwindow, &prop); + } + + XFree(prop.value); + XFlush(m_XDisplay); + } return true; } +void GSWnd::Flip() +{ + if (m_renderer == 2) { +#ifdef ENABLE_SDL_DEV +#if SDL_VERSION_ATLEAST(1,3,0) + SDL_GL_SwapWindow(m_window); +#else + SDL_GL_SwapBuffers(); +#endif +#endif + } else { + glXSwapBuffers(m_XDisplay, m_Xwindow); + } +} + void GSWnd::Show() { - SDL_ShowWindow(m_window); + if (m_renderer == 2) { +#ifdef ENABLE_SDL_DEV + SDL_ShowWindow(m_window); +#endif + } else { + XMapRaised(m_XDisplay, m_Xwindow); + XFlush(m_XDisplay); + } } void GSWnd::Hide() { - SDL_HideWindow(m_window); + if (m_renderer == 2) { +#ifdef ENABLE_SDL_DEV + SDL_HideWindow(m_window); +#endif + } else { + XUnmapWindow(m_XDisplay, m_Xwindow); + XFlush(m_XDisplay); + } } void GSWnd::HideFrame() diff --git a/plugins/GSdx/GSWnd.h b/plugins/GSdx/GSWnd.h index 9969638361..6e2967fad0 100644 --- a/plugins/GSdx/GSWnd.h +++ b/plugins/GSdx/GSWnd.h @@ -88,15 +88,24 @@ public: }; */ #include +#ifdef ENABLE_SDL_DEV #include "../../3rdparty/SDL-1.3.0-5387/include/SDL.h" #include "../../3rdparty/SDL-1.3.0-5387/include/SDL_syswm.h" +#endif class GSWnd { +#ifdef ENABLE_SDL_DEV SDL_Window* m_window; - Window m_Xwindow; - Display* m_XDisplay; +#else + void* m_window; +#endif + Window m_Xwindow; + Display* m_XDisplay; + bool m_managed; + int m_renderer; + GLXContext m_context; public: GSWnd(); @@ -111,11 +120,19 @@ public: void* GetHandle() {return (void*)m_Xwindow;} GSVector4i GetClientRect(); bool SetWindowText(const char* title); +#ifdef ENABLE_SDL_DEV void SetWindow(SDL_Window* current_window) { if (current_window) m_window = current_window; } +#endif + + bool CreateContext(int major, int minor); + void AttachContext(); + void DetachContext(); + void CheckContext(); void Show(); void Hide(); void HideFrame(); + void Flip(); }; #endif diff --git a/plugins/GSdx/GSdx.cpp b/plugins/GSdx/GSdx.cpp index 8289c57c40..adf6921a8c 100644 --- a/plugins/GSdx/GSdx.cpp +++ b/plugins/GSdx/GSdx.cpp @@ -116,6 +116,8 @@ GSdxApp::GSdxApp() m_gs_renderers.push_back(GSSetting(8, "SDL 1.3", "Null")); m_gs_renderers.push_back(GSSetting(10, "Null", "Software")); m_gs_renderers.push_back(GSSetting(11, "Null", "Null")); + m_gs_renderers.push_back(GSSetting(12, "OpenGL", "Hardware")); + m_gs_renderers.push_back(GSSetting(13, "OpenGL", "Software")); m_gs_interlace.push_back(GSSetting(0, "None", "")); m_gs_interlace.push_back(GSSetting(1, "Weave tff", "saw-tooth")); diff --git a/plugins/GSdx/linux_replay.cpp b/plugins/GSdx/linux_replay.cpp new file mode 100644 index 0000000000..f2d25ab8c9 --- /dev/null +++ b/plugins/GSdx/linux_replay.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (C) 20011-2012 Hainaut gregory + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "stdafx.h" + +EXPORT_C GSsetSettingsDir(const char* dir); +EXPORT_C GSReplay(char* lpszCmdLine, int renderer); + + +void help() +{ + fprintf(stderr, "Loader gs file\n"); + fprintf(stderr, "ARG1 Ini directory\n"); + fprintf(stderr, "ARG2 .gs file\n"); + exit(1); +} + +int main ( int argc, char *argv[] ) +{ + if ( argc != 3 ) help(); + + GSsetSettingsDir(argv[1]); + GSReplay(argv[2], 12); +} diff --git a/plugins/GSdx/res/convert.glsl b/plugins/GSdx/res/convert.glsl new file mode 100644 index 0000000000..11fb55a058 --- /dev/null +++ b/plugins/GSdx/res/convert.glsl @@ -0,0 +1,167 @@ +//#version 420 // Keep it for editor detection + +struct vertex_basic +{ + vec4 p; + vec2 t; +}; + + +#ifdef VERTEX_SHADER + +out gl_PerVertex { + vec4 gl_Position; + float gl_PointSize; + float gl_ClipDistance[]; +}; + +layout(location = 0) in vec4 POSITION; +layout(location = 1) in vec2 TEXCOORD0; + +// FIXME set the interpolation (don't know what dx do) +// flat means that there is no interpolation. The value given to the fragment shader is based on the provoking vertex conventions. +// +// noperspective means that there will be linear interpolation in window-space. This is usually not what you want, but it can have its uses. +// +// smooth, the default, means to do perspective-correct interpolation. +// +// The centroid qualifier only matters when multisampling. If this qualifier is not present, then the value is interpolated to the pixel's center, anywhere in the pixel, or to one of the pixel's samples. This sample may lie outside of the actual primitive being rendered, since a primitive can cover only part of a pixel's area. The centroid qualifier is used to prevent this; the interpolation point must fall within both the pixel's area and the primitive's area. +layout(location = 0) out vertex_basic VSout; + +void vs_main() +{ + VSout.p = POSITION; + VSout.t = TEXCOORD0; + gl_Position = POSITION; // NOTE I don't know if it is possible to merge POSITION_OUT and gl_Position +} + +#endif + +#ifdef GEOMETRY_SHADER +in gl_PerVertex { + vec4 gl_Position; + float gl_PointSize; + float gl_ClipDistance[]; +} gl_in[]; + +out gl_PerVertex { + vec4 gl_Position; + float gl_PointSize; + float gl_ClipDistance[]; +}; + +// FIXME +// AMD Driver bug again !!!! +//layout(location = 0) in vertex GSin[]; +in vertex_basic GSin[]; + +layout(location = 0) out vertex_basic GSout; +layout(triangles) in; +layout(triangle_strip, max_vertices = 3) out; + +void gs_main() +{ + for(int i = 0; i < gl_in.length(); i++) { + gl_Position = gl_in[i].gl_Position; + GSout = GSin[i]; + EmitVertex(); + } + EndPrimitive(); +} +#endif + +#ifdef FRAGMENT_SHADER +// NOTE: pixel can be clip with "discard" + +layout(location = 0) in vertex_basic PSin; + +layout(location = 0) out vec4 SV_Target0; +layout(location = 1) out uint SV_Target1; + +layout(binding = 0) uniform sampler2D TextureSampler; + +vec4 sample_c() +{ + return texture(TextureSampler, PSin.t ); +} + +vec4 ps_crt(uint i) +{ + vec4 mask[4] = + { + vec4(1, 0, 0, 0), + vec4(0, 1, 0, 0), + vec4(0, 0, 1, 0), + vec4(1, 1, 1, 0) + }; + + return sample_c() * clamp((mask[i] + 0.5f), 0.0f, 1.0f); +} + +void ps_main0() +{ + SV_Target0 = sample_c(); +} + +void ps_main1() +{ + vec4 c = sample_c(); + + c.a *= 256.0f / 127.0f; // hm, 0.5 won't give us 1.0 if we just multiply with 2 + + highp uvec4 i = uvec4(c * vec4(0x001f, 0x03e0, 0x7c00, 0x8000)); + + SV_Target1 = (i.x & uint(0x001f)) | (i.y & uint(0x03e0)) | (i.z & uint(0x7c00)) | (i.w & uint(0x8000)); +} + +void ps_main7() +{ + vec4 c = sample_c(); + + c.a = dot(c.rgb, vec3(0.299, 0.587, 0.114)); + + SV_Target0 = c; +} + +void ps_main5() // triangular +{ + highp uvec4 p = uvec4(PSin.p); + + vec4 c = ps_crt(((p.x + ((p.y >> 1u) & 1u) * 3u) >> 1u) % 3u); + + SV_Target0 = c; +} + +void ps_main6() // diagonal +{ + uvec4 p = uvec4(PSin.p); + + vec4 c = ps_crt((p.x + (p.y % 3u)) % 3u); + + SV_Target0 = c; +} + +void ps_main2() +{ + if((sample_c().a - 128.0f / 255) < 0) // >= 0x80 pass + discard; + + SV_Target0 = vec4(0.0f, 0.0f, 0.0f, 0.0f); +} +void ps_main3() +{ + if((127.95f / 255 - sample_c().a) <0) // < 0x80 pass (== 0x80 should not pass) + discard; + + SV_Target0 = vec4(0.0f, 0.0f, 0.0f, 0.0f); +} +void ps_main4() +{ + // FIXME mod and fmod are different when value are negative + // output.c = fmod(sample_c(input.t) * 255 + 0.5f, 256) / 255; + vec4 c = mod(sample_c() * 255 + 0.5f, 256) / 255; + + SV_Target0 = c; +} + +#endif diff --git a/plugins/GSdx/res/interlace.glsl b/plugins/GSdx/res/interlace.glsl new file mode 100644 index 0000000000..97e567fa1b --- /dev/null +++ b/plugins/GSdx/res/interlace.glsl @@ -0,0 +1,59 @@ +//#version 420 // Keep it for editor detection + +struct vertex_basic +{ + vec4 p; + vec2 t; +}; + +#ifdef FRAGMENT_SHADER +layout(location = 0) in vertex_basic PSin; + +layout(location = 0) out vec4 SV_Target0; + +layout(std140, binding = 2) uniform cb0 +{ + vec2 ZrH; + float hH; +}; + +layout(binding = 0) uniform sampler2D TextureSampler; + +// TODO ensure that clip (discard) is < 0 and not <= 0 ??? +void ps_main0() +{ + // I'm not sure it impact us but be safe to lookup texture before conditional if + // see: http://www.opengl.org/wiki/GLSL_Sampler#Non-uniform_flow_control + vec4 c = texture(TextureSampler, PSin.t); + if (fract(PSin.t.y * hH) - 0.5 < 0.0) + discard; + + SV_Target0 = c; +} + +void ps_main1() +{ + // I'm not sure it impact us but be safe to lookup texture before conditional if + // see: http://www.opengl.org/wiki/GLSL_Sampler#Non-uniform_flow_control + vec4 c = texture(TextureSampler, PSin.t); + if (0.5 - fract(PSin.t.y * hH) < 0.0) + discard; + + SV_Target0 = c; +} + +void ps_main2() +{ + vec4 c0 = texture(TextureSampler, PSin.t - ZrH); + vec4 c1 = texture(TextureSampler, PSin.t); + vec4 c2 = texture(TextureSampler, PSin.t + ZrH); + + SV_Target0 = (c0 + c1 * 2 + c2) / 4; +} + +void ps_main3() +{ + SV_Target0 = texture(TextureSampler, PSin.t); +} + +#endif diff --git a/plugins/GSdx/res/logo-ogl.bmp b/plugins/GSdx/res/logo-ogl.bmp new file mode 100644 index 0000000000..1c3d92b437 Binary files /dev/null and b/plugins/GSdx/res/logo-ogl.bmp differ diff --git a/plugins/GSdx/res/merge.glsl b/plugins/GSdx/res/merge.glsl new file mode 100644 index 0000000000..e9a93bdc88 --- /dev/null +++ b/plugins/GSdx/res/merge.glsl @@ -0,0 +1,35 @@ +//#version 420 // Keep it for editor detection + +struct vertex_basic +{ + vec4 p; + vec2 t; +}; + +#ifdef FRAGMENT_SHADER +layout(location = 0) in vertex_basic PSin; + +layout(location = 0) out vec4 SV_Target0; + +layout(std140, binding = 1) uniform cb0 +{ + vec4 BGColor; +}; + +layout(binding = 0) uniform sampler2D TextureSampler; + +void ps_main0() +{ + vec4 c = texture(TextureSampler, PSin.t); + c.a = min(c.a * 2, 1.0); + SV_Target0 = c; +} + +void ps_main1() +{ + vec4 c = texture(TextureSampler, PSin.t); + c.a = BGColor.a; + SV_Target0 = c; +} + +#endif diff --git a/plugins/GSdx/res/shadeboost.glsl b/plugins/GSdx/res/shadeboost.glsl new file mode 100644 index 0000000000..c98222d80a --- /dev/null +++ b/plugins/GSdx/res/shadeboost.glsl @@ -0,0 +1,61 @@ +//#version 420 // Keep it for editor detection + +/* +** Contrast, saturation, brightness +** Code of this function is from TGM's shader pack +** http://irrlicht.sourceforge.net/phpBB2/viewtopic.php?t=21057 +*/ + +struct vertex_basic +{ + vec4 p; + vec2 t; +}; + +#ifdef FRAGMENT_SHADER + +layout(location = 0) in vertex_basic PSin; + +layout(location = 0) out vec4 SV_Target0; + +layout(std140, binding = 6) uniform cb0 +{ + vec4 BGColor; +}; + +layout(binding = 0) uniform sampler2D TextureSampler; + +// For all settings: 1.0 = 100% 0.5=50% 1.5 = 150% +vec4 ContrastSaturationBrightness(vec4 color) +{ + const float sat = SB_SATURATION / 50.0; + const float brt = SB_BRIGHTNESS / 50.0; + const float con = SB_CONTRAST / 50.0; + + // Increase or decrease theese values to adjust r, g and b color channels seperately + const float AvgLumR = 0.5; + const float AvgLumG = 0.5; + const float AvgLumB = 0.5; + + const vec3 LumCoeff = vec3(0.2125, 0.7154, 0.0721); + + vec3 AvgLumin = vec3(AvgLumR, AvgLumG, AvgLumB); + vec3 brtColor = color.rgb * brt; + float dot_intensity = dot(brtColor, LumCoeff); + vec3 intensity = vec3(dot_intensity, dot_intensity, dot_intensity); + vec3 satColor = mix(intensity, brtColor, sat); + vec3 conColor = mix(AvgLumin, satColor, con); + + color.rgb = conColor; + return color; +} + + +void ps_main() +{ + vec4 c = texture(TextureSampler, PSin.t); + SV_Target0 = ContrastSaturationBrightness(c); +} + + +#endif diff --git a/plugins/GSdx/res/tfx.glsl b/plugins/GSdx/res/tfx.glsl new file mode 100644 index 0000000000..92ce5fb75e --- /dev/null +++ b/plugins/GSdx/res/tfx.glsl @@ -0,0 +1,658 @@ +//#version 420 // Keep it for text editor detection + +// note lerp => mix + +#define FMT_32 0 +#define FMT_24 1 +#define FMT_16 2 +#define FMT_8H 3 +#define FMT_4HL 4 +#define FMT_4HH 5 +#define FMT_8 6 + +#ifndef VS_BPPZ +#define VS_BPPZ 0 +#define VS_TME 1 +#define VS_FST 1 +#endif + +#ifndef GS_IIP +#define GS_IIP 0 +#define GS_PRIM 3 +#endif + +#ifndef PS_FST +#define PS_FST 0 +#define PS_WMS 0 +#define PS_WMT 0 +#define PS_FMT FMT_8 +#define PS_AEM 0 +#define PS_TFX 0 +#define PS_TCC 1 +#define PS_ATST 1 +#define PS_FOG 0 +#define PS_CLR1 0 +#define PS_FBA 0 +#define PS_AOUT 0 +#define PS_LTF 1 +#define PS_COLCLIP 0 +#define PS_DATE 0 +#define PS_SPRITEHACK 0 +#endif + +struct vertex +{ + vec4 p; + vec4 t; + vec4 tp; + vec4 c; +}; + +#ifdef VERTEX_SHADER +layout(location = 0) in vec2 i_st; +layout(location = 1) in vec4 i_c; +layout(location = 2) in float i_q; +layout(location = 3) in uvec2 i_p; +layout(location = 4) in uint i_z; +layout(location = 5) in uvec2 i_uv; +layout(location = 6) in vec4 i_f; + +layout(location = 0) out vertex VSout; + +out gl_PerVertex { + vec4 gl_Position; + float gl_PointSize; + float gl_ClipDistance[]; +}; + +layout(std140, binding = 4) uniform cb0 +{ + vec4 VertexScale; + vec4 VertexOffset; + vec2 TextureScale; +}; + +void vs_main() +{ + uint z; + if(VS_BPPZ == 1) // 24 + z = i_z & uint(0xffffff); + else if(VS_BPPZ == 2) // 16 + z = i_z & uint(0xffff); + else + z = i_z; + + // pos -= 0.05 (1/320 pixel) helps avoiding rounding problems (integral part of pos is usually 5 digits, 0.05 is about as low as we can go) + // example: ceil(afterseveralvertextransformations(y = 133)) => 134 => line 133 stays empty + // input granularity is 1/16 pixel, anything smaller than that won't step drawing up/left by one pixel + // example: 133.0625 (133 + 1/16) should start from line 134, ceil(133.0625 - 0.05) still above 133 + + vec4 p = vec4(i_p, z, 0) - vec4(0.05f, 0.05f, 0, 0); + vec4 final_p = p * VertexScale - VertexOffset; + // FIXME + // FLIP vertically + final_p.y *= -1.0f; + + VSout.p = final_p; + gl_Position = final_p; // NOTE I don't know if it is possible to merge POSITION_OUT and gl_Position +#if VS_RTCOPY + VSout.tp = final_p * vec4(0.5, -0.5, 0, 0) + 0.5; +#endif + + if(VS_TME != 0) + { + if(VS_FST != 0) + { + //VSout.t.xy = i_t * TextureScale; + VSout.t.xy = i_uv * TextureScale; + VSout.t.w = 1.0f; + } + else + { + //VSout.t.xy = i_t; + VSout.t.xy = i_st; + VSout.t.w = i_q; + } + } + else + { + VSout.t.xy = vec2(0.0f, 0.0f); + VSout.t.w = 1.0f; + } + + VSout.c = i_c; + VSout.t.z = i_f.r; +} + +#endif + +#ifdef GEOMETRY_SHADER +in gl_PerVertex { + vec4 gl_Position; + float gl_PointSize; + float gl_ClipDistance[]; +} gl_in[]; + +out gl_PerVertex { + vec4 gl_Position; + float gl_PointSize; + float gl_ClipDistance[]; +}; + +// FIXME +// AMD Driver bug again !!!! +//layout(location = 0) in vertex GSin[]; +in vertex GSin[]; + +layout(location = 0) out vertex GSout; + +#if GS_PRIM == 0 +layout(points) in; +layout(points, max_vertices = 1) out; + +void gs_main() +{ + for(int i = 0; i < gl_in.length(); i++) { + gl_Position = gl_in[i].gl_Position; // FIXME is it useful + GSout = GSin[i]; + EmitVertex(); + } + EndPrimitive(); +} + +#elif GS_PRIM == 1 +layout(lines) in; +layout(line_strip, max_vertices = 2) out; + +void gs_main() +{ + for(int i = 0; i < gl_in.length(); i++) { + gl_Position = gl_in[i].gl_Position; // FIXME is it useful + GSout = GSin[i]; +#if GS_IIP == 0 + if (i == 0) + GSout.c = GSin[1].c; +#endif + EmitVertex(); + } + EndPrimitive(); +} + +#elif GS_PRIM == 2 +layout(triangles) in; +layout(triangle_strip, max_vertices = 3) out; + +void gs_main() +{ + for(int i = 0; i < gl_in.length(); i++) { + gl_Position = gl_in[i].gl_Position; // FIXME is it useful + GSout = GSin[i]; +#if GS_IIP == 0 + if (i == 0 || i == 1) + GSout.c = GSin[2].c; +#endif + EmitVertex(); + } + EndPrimitive(); +} + +#elif GS_PRIM == 3 +layout(lines) in; +layout(triangle_strip, max_vertices = 6) out; + +void gs_main() +{ + // left top => GSin[0]; + // right bottom => GSin[1]; + vertex rb = GSin[1]; + vertex lt = GSin[0]; + + lt.p.z = rb.p.z; + lt.t.zw = rb.t.zw; + #if GS_IIP == 0 + lt.c = rb.c; + #endif + + vertex lb = rb; + lb.p.x = lt.p.x; + lb.t.x = lt.t.x; + + vertex rt = rb; + rt.p.y = lt.p.y; + rt.t.y = lt.t.y; + + // Triangle 1 + gl_Position = lt.p; + GSout = lt; + EmitVertex(); + + gl_Position = lb.p; + GSout = lb; + EmitVertex(); + + gl_Position = rt.p; + GSout = rt; + EmitVertex(); + + EndPrimitive(); + + // Triangle 2 + gl_Position = lb.p; + GSout = lb; + EmitVertex(); + + gl_Position = rt.p; + GSout = rt; + EmitVertex(); + + gl_Position = rb.p; + GSout = rb; + EmitVertex(); + + EndPrimitive(); + +} + +#endif + +#endif + +#ifdef FRAGMENT_SHADER +layout(location = 0) in vertex PSin; + +// Same buffer but 2 colors for dual source blending +#ifndef DISABLE_DUAL_BLEND +layout(location = 0, index = 1) out vec4 SV_Target0; +layout(location = 0, index = 0) out vec4 SV_Target1; +#else +layout(location = 0) out vec4 SV_Target1; +#endif + +layout(binding = 0) uniform sampler2D TextureSampler; +layout(binding = 1) uniform sampler2D PaletteSampler; +layout(binding = 2) uniform sampler2D RTCopySampler; + +layout(std140, binding = 5) uniform cb1 +{ + vec3 FogColor; + float AREF; + vec4 HalfTexel; + vec4 WH; + vec4 MinMax; + vec2 MinF; + vec2 TA; + uvec4 MskFix; +}; + +vec4 sample_c(vec2 uv) +{ + // FIXME I'm not sure it is a good solution to flip texture + return texture(TextureSampler, uv); + //FIXME another way to FLIP vertically + //return texture(TextureSampler, vec2(uv.x, 1.0f-uv.y) ); +} + +vec4 sample_p(float u) +{ + //FIXME do we need a 1D sampler. Big impact on opengl to find 1 dim + // So for the moment cheat with 0.0f dunno if it work + return texture(PaletteSampler, vec2(u, 0.0f)); +} + +vec4 sample_rt(vec2 uv) +{ + return texture(RTCopySampler, uv); +} + +vec4 wrapuv(vec4 uv) +{ + vec4 uv_out = uv; + + if(PS_WMS == PS_WMT) + { + if(PS_WMS == 2) + { + uv_out = clamp(uv, MinMax.xyxy, MinMax.zwzw); + } + else if(PS_WMS == 3) + { + uv_out = vec4(((ivec4(uv * WH.xyxy) & ivec4(MskFix.xyxy)) | ivec4(MskFix.zwzw)) / WH.xyxy); + } + } + else + { + if(PS_WMS == 2) + { + uv_out.xz = clamp(uv.xz, MinMax.xx, MinMax.zz); + } + else if(PS_WMS == 3) + { + uv_out.xz = vec2(((ivec2(uv.xz * WH.xx) & ivec2(MskFix.xx)) | ivec2(MskFix.zz)) / WH.xx); + } + if(PS_WMT == 2) + { + uv_out.yw = clamp(uv.yw, MinMax.yy, MinMax.ww); + } + else if(PS_WMT == 3) + { + uv_out.yw = vec2(((ivec2(uv.yw * WH.yy) & ivec2(MskFix.yy)) | ivec2(MskFix.ww)) / WH.yy); + } + } + + return uv_out; +} + +vec2 clampuv(vec2 uv) +{ + vec2 uv_out = uv; + + if(PS_WMS == 2 && PS_WMT == 2) + { + uv_out = clamp(uv, MinF, MinMax.zw); + } + else if(PS_WMS == 2) + { + uv_out.x = clamp(uv.x, MinF.x, MinMax.z); + } + else if(PS_WMT == 2) + { + uv_out.y = clamp(uv.y, MinF.y, MinMax.w); + } + + return uv_out; +} + +mat4 sample_4c(vec4 uv) +{ + mat4 c; + + c[0] = sample_c(uv.xy); + c[1] = sample_c(uv.zy); + c[2] = sample_c(uv.xw); + c[3] = sample_c(uv.zw); + + return c; +} + +vec4 sample_4a(vec4 uv) +{ + vec4 c; + + c.x = sample_c(uv.xy).a; + c.y = sample_c(uv.zy).a; + c.z = sample_c(uv.xw).a; + c.w = sample_c(uv.zw).a; + + return c; +} + +mat4 sample_4p(vec4 u) +{ + mat4 c; + + c[0] = sample_p(u.x); + c[1] = sample_p(u.y); + c[2] = sample_p(u.z); + c[3] = sample_p(u.w); + + return c; +} + +vec4 sample_color(vec2 st, float q) +{ + if(PS_FST == 0) + { + st /= q; + } + + vec4 t; + if((PS_FMT <= FMT_16) && (PS_WMS < 3) && (PS_WMT < 3)) + { + t = sample_c(clampuv(st)); + } + else + { + vec4 uv; + vec2 dd; + + if(PS_LTF != 0) + { + uv = st.xyxy + HalfTexel; + dd = fract(uv.xy * WH.zw); + } + else + { + uv = st.xyxy; + } + + uv = wrapuv(uv); + + mat4 c; + + if(PS_FMT == FMT_8H) + { + c = sample_4p(sample_4a(uv)); + } + else if(PS_FMT == FMT_4HL) + { + // FIXME mod and fmod are different when value are negative + c = sample_4p(mod(sample_4a(uv), 1.0f / 16)); + } + else if(PS_FMT == FMT_4HH) + { + // FIXME mod and fmod are different when value are negative + c = sample_4p(mod(sample_4a(uv) * 16, 1.0f / 16)); + } + else if(PS_FMT == FMT_8) + { + c = sample_4p(sample_4a(uv)); + } + else + { + c = sample_4c(uv); + } + + if(PS_LTF != 0) + { + t = mix(mix(c[0], c[1], dd.x), mix(c[2], c[3], dd.x), dd.y); + } + else + { + t = c[0]; + } + } + + if(PS_FMT == FMT_32) + { + ; + } + else if(PS_FMT == FMT_24) + { + // FIXME GLSL any only support bvec so try to mix it with notEqual + bvec3 rgb_check = notEqual( t.rgb, vec3(0.0f, 0.0f, 0.0f) ); + t.a = ( (PS_AEM == 0) || any(rgb_check) ) ? TA.x : 0.0f; + } + else if(PS_FMT == FMT_16) + { + // a bit incompatible with up-scaling because the 1 bit alpha is interpolated + // FIXME GLSL any only support bvec so try to mix it with notEqual + bvec3 rgb_check = notEqual( t.rgb, vec3(0.0f, 0.0f, 0.0f) ); + t.a = t.a >= 0.5 ? TA.y : ( (PS_AEM == 0) || any(rgb_check) ) ? TA.x : 0.0f; + } + + return t; +} + +vec4 tfx(vec4 t, vec4 c) +{ + vec4 c_out = c; + if(PS_TFX == 0) + { + if(PS_TCC != 0) + { + c_out = c * t * 255.0f / 128; + } + else + { + c_out.rgb = c.rgb * t.rgb * 255.0f / 128; + } + } + else if(PS_TFX == 1) + { + if(PS_TCC != 0) + { + c_out = t; + } + else + { + c_out.rgb = t.rgb; + } + } + else if(PS_TFX == 2) + { + c_out.rgb = c.rgb * t.rgb * 255.0f / 128 + c.a; + + if(PS_TCC != 0) + { + c_out.a += t.a; + } + } + else if(PS_TFX == 3) + { + c_out.rgb = c.rgb * t.rgb * 255.0f / 128 + c.a; + + if(PS_TCC != 0) + { + c_out.a = t.a; + } + } + + return clamp(c_out, vec4(0.0f, 0.0f, 0.0f, 0.0f), vec4(1.0f, 1.0f, 1.0f, 1.0f)); +} + +void datst() +{ +#if PS_DATE > 0 + float alpha = sample_rt(PSin.tp.xy).a; + float alpha0x80 = 128. / 255; + + if (PS_DATE == 1 && alpha >= alpha0x80) + discard; + else if (PS_DATE == 2 && alpha < alpha0x80) + discard; +#endif +} + +void atst(vec4 c) +{ + float a = trunc(c.a * 255); + + if(PS_ATST == 0) // never + { + discard; + } + else if(PS_ATST == 1) // always + { + // nothing to do + } + else if(PS_ATST == 2) + { + } + else if(PS_ATST == 2 ) // l + { + if (PS_SPRITEHACK == 0) + if ((AREF - a) < 0.0f) + discard; + } + else if(PS_ATST == 2 ) // le + { + if ((AREF - a) < 0.0f) + discard; + } + else if(PS_ATST == 4) // e + { + if ((0.5f - abs(a - AREF)) < 0.0f) + discard; + } + else if(PS_ATST == 5 || PS_ATST == 6) // ge, g + { + if ((a-AREF) < 0.0f) + discard; + } + else if(PS_ATST == 7) // ne + { + if ((abs(a - AREF) - 0.5f) < 0.0f) + discard; + } +} + +vec4 fog(vec4 c, float f) +{ + vec4 c_out = c; + if(PS_FOG != 0) + { + c_out.rgb = mix(FogColor, c.rgb, f); + } + + return c_out; +} + +vec4 ps_color() +{ + datst(); + + vec4 t = sample_color(PSin.t.xy, PSin.t.w); + + vec4 c = tfx(t, PSin.c); + + atst(c); + + c = fog(c, PSin.t.z); + + if (PS_COLCLIP == 2) + { + c.rgb = 256.0f/255.0f - c.rgb; + } + if (PS_COLCLIP > 0) + { + // FIXME !!!! + //c.rgb *= c.rgb < 128./255; + bvec3 factor = bvec3(128.0f/255.0f, 128.0f/255.0f, 128.0f/255.0f); + c.rgb *= vec3(factor); + } + + if(PS_CLR1 != 0) // needed for Cd * (As/Ad/F + 1) blending modes + { + c.rgb = vec3(1.0f, 1.0f, 1.0f); + } + + return c; +} + +void ps_main() +{ + //FIXME + vec4 c = ps_color(); + + // FIXME: I'm not sure about the value of others field + // output.c1 = c.a * 2; // used for alpha blending + + float alpha = c.a * 2; + + if(PS_AOUT != 0) // 16 bit output + { + float a = 128.0f / 255; // alpha output will be 0x80 + + c.a = (PS_FBA != 0) ? a : step(0.5, c.a) * a; + } + else if(PS_FBA != 0) + { + if(c.a < 0.5) c.a += 0.5; + } + + SV_Target1 = c; +#ifndef DISABLE_DUAL_BLEND + SV_Target0 = vec4(alpha, alpha, alpha, alpha); +#endif +} +#endif diff --git a/plugins/GSdx/stdafx.h b/plugins/GSdx/stdafx.h index 2fe70e9053..8d6eb83806 100644 --- a/plugins/GSdx/stdafx.h +++ b/plugins/GSdx/stdafx.h @@ -198,6 +198,11 @@ using namespace std; //#include //#include + #include + #include + #include + #include + //using namespace __gnu_cxx; #define DIRECTORY_SEPARATOR '/'