From d874429b7cf7d07528b8fdc4ac7b07933f89a6ef Mon Sep 17 00:00:00 2001 From: kervala Date: Thu, 25 Feb 2016 21:19:27 +0100 Subject: [PATCH 01/32] Added: Initial commit for Ryzom Installer (WIP) --- code/ryzom/tools/client/CMakeLists.txt | 1 + .../client/ryzom_installer/CMakeLists.txt | 51 + .../client/ryzom_installer/res/background.png | Bin 0 -> 110522 bytes .../ryzom_installer/res/modern-header.bmp | Bin 0 -> 17162 bytes .../ryzom_installer/res/modern-wizard.bmp | Bin 0 -> 134446 bytes .../client/ryzom_installer/res/resources.qrc | 8 + .../client/ryzom_installer/res/resources.rc | 39 + .../client/ryzom_installer/res/ryzom.ico | Bin 0 -> 160871 bytes .../client/ryzom_installer/src/archive.cpp | 785 ++++++++++ .../client/ryzom_installer/src/archive.h | 96 ++ .../client/ryzom_installer/src/configfile.cpp | 564 ++++++++ .../client/ryzom_installer/src/configfile.h | 166 +++ .../src/configurationsdialog.cpp | 38 + .../src/configurationsdialog.h | 34 + .../client/ryzom_installer/src/downloader.cpp | 394 ++++++ .../client/ryzom_installer/src/downloader.h | 110 ++ .../tools/client/ryzom_installer/src/main.cpp | 108 ++ .../client/ryzom_installer/src/mainwindow.cpp | 389 +++++ .../client/ryzom_installer/src/mainwindow.h | 81 ++ .../tools/client/ryzom_installer/src/qzip.cpp | 1260 +++++++++++++++++ .../client/ryzom_installer/src/qzipreader.h | 117 ++ .../client/ryzom_installer/src/qzipwriter.h | 114 ++ .../ryzom_installer/src/settingsdialog.cpp | 38 + .../ryzom_installer/src/settingsdialog.h | 34 + .../client/ryzom_installer/src/stdpch.cpp | 17 + .../tools/client/ryzom_installer/src/stdpch.h | 65 + .../client/ryzom_installer/src/utils.cpp | 182 +++ .../tools/client/ryzom_installer/src/utils.h | 47 + .../ryzom_installer/src/wizarddialog.cpp | 219 +++ .../client/ryzom_installer/src/wizarddialog.h | 53 + .../ryzom_installer/ui/configurations.ui | 305 ++++ .../client/ryzom_installer/ui/mainwindow.ui | 212 +++ .../client/ryzom_installer/ui/settings.ui | 113 ++ .../tools/client/ryzom_installer/ui/wizard.ui | 260 ++++ 34 files changed, 5900 insertions(+) create mode 100644 code/ryzom/tools/client/ryzom_installer/CMakeLists.txt create mode 100644 code/ryzom/tools/client/ryzom_installer/res/background.png create mode 100644 code/ryzom/tools/client/ryzom_installer/res/modern-header.bmp create mode 100644 code/ryzom/tools/client/ryzom_installer/res/modern-wizard.bmp create mode 100644 code/ryzom/tools/client/ryzom_installer/res/resources.qrc create mode 100644 code/ryzom/tools/client/ryzom_installer/res/resources.rc create mode 100644 code/ryzom/tools/client/ryzom_installer/res/ryzom.ico create mode 100644 code/ryzom/tools/client/ryzom_installer/src/archive.cpp create mode 100644 code/ryzom/tools/client/ryzom_installer/src/archive.h create mode 100644 code/ryzom/tools/client/ryzom_installer/src/configfile.cpp create mode 100644 code/ryzom/tools/client/ryzom_installer/src/configfile.h create mode 100644 code/ryzom/tools/client/ryzom_installer/src/configurationsdialog.cpp create mode 100644 code/ryzom/tools/client/ryzom_installer/src/configurationsdialog.h create mode 100644 code/ryzom/tools/client/ryzom_installer/src/downloader.cpp create mode 100644 code/ryzom/tools/client/ryzom_installer/src/downloader.h create mode 100644 code/ryzom/tools/client/ryzom_installer/src/main.cpp create mode 100644 code/ryzom/tools/client/ryzom_installer/src/mainwindow.cpp create mode 100644 code/ryzom/tools/client/ryzom_installer/src/mainwindow.h create mode 100644 code/ryzom/tools/client/ryzom_installer/src/qzip.cpp create mode 100644 code/ryzom/tools/client/ryzom_installer/src/qzipreader.h create mode 100644 code/ryzom/tools/client/ryzom_installer/src/qzipwriter.h create mode 100644 code/ryzom/tools/client/ryzom_installer/src/settingsdialog.cpp create mode 100644 code/ryzom/tools/client/ryzom_installer/src/settingsdialog.h create mode 100644 code/ryzom/tools/client/ryzom_installer/src/stdpch.cpp create mode 100644 code/ryzom/tools/client/ryzom_installer/src/stdpch.h create mode 100644 code/ryzom/tools/client/ryzom_installer/src/utils.cpp create mode 100644 code/ryzom/tools/client/ryzom_installer/src/utils.h create mode 100644 code/ryzom/tools/client/ryzom_installer/src/wizarddialog.cpp create mode 100644 code/ryzom/tools/client/ryzom_installer/src/wizarddialog.h create mode 100644 code/ryzom/tools/client/ryzom_installer/ui/configurations.ui create mode 100644 code/ryzom/tools/client/ryzom_installer/ui/mainwindow.ui create mode 100644 code/ryzom/tools/client/ryzom_installer/ui/settings.ui create mode 100644 code/ryzom/tools/client/ryzom_installer/ui/wizard.ui diff --git a/code/ryzom/tools/client/CMakeLists.txt b/code/ryzom/tools/client/CMakeLists.txt index 3cdaf0136..7fa015c47 100644 --- a/code/ryzom/tools/client/CMakeLists.txt +++ b/code/ryzom/tools/client/CMakeLists.txt @@ -3,6 +3,7 @@ IF(WITH_RYZOM_CLIENT) IF(WITH_QT OR WITH_QT5) ADD_SUBDIRECTORY(client_config_qt) + ADD_SUBDIRECTORY(ryzom_installer) ENDIF() ENDIF() diff --git a/code/ryzom/tools/client/ryzom_installer/CMakeLists.txt b/code/ryzom/tools/client/ryzom_installer/CMakeLists.txt new file mode 100644 index 000000000..4471628e0 --- /dev/null +++ b/code/ryzom/tools/client/ryzom_installer/CMakeLists.txt @@ -0,0 +1,51 @@ +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/src ${CMAKE_CURRENT_SOURCE_DIR} ${NEL_INCLUDE_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/ryzom/client/src/seven_zip ${ZLIB_INCLUDE_DIR}) + +FILE(GLOB SRC src/*.cpp src/*.h res/*.rc) +FILE(GLOB CLIENT_INSTALL_HDR src/*.h) +FILE(GLOB CLIENT_INSTALL_UIS ui/*.ui) +FILE(GLOB CLIENT_INSTALL_TRANS translations/*.ts) +FILE(GLOB CLIENT_INSTALL_RCS res/*.qrc) + +#CONFIGURE_FILE(translations/translations.qrc ${CMAKE_CURRENT_BINARY_DIR}/translations.qrc COPYONLY) +#SET(CLIENT_INSTALL_RCS resources.qrc ${CMAKE_CURRENT_BINARY_DIR}/translations.qrc) + +IF(WITH_QT) + INCLUDE_DIRECTORIES(${QT_INCLUDES}) + INCLUDE(${QT_USE_FILE}) + + QT4_ADD_TRANSLATION(CLIENT_INSTALL_QM ${CLIENT_INSTALL_TRANS}) + QT4_ADD_RESOURCES(CLIENT_INSTALL_RC_SRCS ${CLIENT_INSTALL_RCS}) + QT4_WRAP_CPP(CLIENT_INSTALL_MOC_SRC ${CLIENT_INSTALL_HDR}) + QT4_WRAP_UI(CLIENT_INSTALL_UI_HDRS ${CLIENT_INSTALL_UIS}) + + ADD_DEFINITIONS(${QT_DEFINITIONS}) +ELSE() + IF(WIN32) + FIND_PACKAGE(Qt5WinExtras) + SET(QT_LIBRARIES Qt5::WinExtras ${QT_LIBRARIES}) + ENDIF() + + QT5_ADD_TRANSLATION(CLIENT_INSTALL_QM ${CLIENT_INSTALL_TRANS}) + QT5_ADD_RESOURCES(CLIENT_INSTALL_RC_SRCS ${CLIENT_INSTALL_RCS}) + QT5_WRAP_CPP(CLIENT_INSTALL_MOC_SRC ${CLIENT_INSTALL_HDR}) + QT5_WRAP_UI(CLIENT_INSTALL_UI_HDRS ${CLIENT_INSTALL_UIS}) +ENDIF() + +SOURCE_GROUP("Source" FILES ${SRC}) +SOURCE_GROUP("Resources" FILES ${CLIENT_INSTALL_RCS}) +SOURCE_GROUP("Forms" FILES ${CLIENT_INSTALL_UIS}) +SOURCE_GROUP("Generated Files" FILES ${CLIENT_INSTALL_UI_HDRS} ${CLIENT_INSTALL_MOC_SRC} ${CLIENT_INSTALL_RC_SRCS}) +SOURCE_GROUP("Translation Files" FILES ${CLIENT_INSTALL_TRANS}) + +ADD_EXECUTABLE(ryzom_installer_qt WIN32 ${SRC} ${CLIENT_INSTALL_MOC_SRC} ${CLIENT_INSTALL_UI_HDRS} ${CLIENT_INSTALL_RC_SRCS} ${CLIENT_INSTALL_TRANS} ${CLIENT_INSTALL_QM}) +NL_DEFAULT_PROPS(ryzom_installer_qt "Ryzom, Tools: Ryzom Installer" ) +NL_ADD_RUNTIME_FLAGS(ryzom_installer_qt) +NL_ADD_LIB_SUFFIX(ryzom_installer_qt) +TARGET_LINK_LIBRARIES(ryzom_installer_qt nelmisc ryzom_sevenzip ${QT_LIBRARIES}) + +IF(WITH_PCH) + ADD_NATIVE_PRECOMPILED_HEADER(ryzom_installer_qt ${CMAKE_CURRENT_SOURCE_DIR}/src/stdpch.h ${CMAKE_CURRENT_SOURCE_DIR}/src/stdpch.cpp) +ENDIF() + +INSTALL(TARGETS ryzom_installer_qt RUNTIME DESTINATION ${RYZOM_GAMES_PREFIX} COMPONENT client) diff --git a/code/ryzom/tools/client/ryzom_installer/res/background.png b/code/ryzom/tools/client/ryzom_installer/res/background.png new file mode 100644 index 0000000000000000000000000000000000000000..26548eaa63c5bc3f9948cd1a657fb45ef4e2c4fe GIT binary patch literal 110522 zcmXt9cQ{+$|G##qk=Qi$s#$xqiV~`7Z))o&YHvzwD=}(SOA@tOLF^Kp{>!g{R-eF?kWdHzx>8>Hn^0F-f0E#3KcZ%yJ0iT(R4qk%IfQj^~$_H;CZtHk?L+)>-b>}E;K;g0LsucO{m(#5gB z#p+7XoS)k1X{yBREEh2xmC}Vi8tOaOzOcL4jyrX|D7fD9_Zx{gdodK)3)zq;;4+hI zTZ;Lu(q6|oBI%{;eKLSn+n+&?o~`^KpN($(VnrUNzJK3*())L0>0Q*p4JI}be_QlE zdB6Bp3&mup}+3ht+gmt~u;;aI>Q!7#J7hl7-J4@jF8+>YjJqx@ zkv8)ne##|-3WJ8Q$$Vlf-;hG`FaZED6N)OIgh%}sFx+AQIVC8v7*jd-!!SBh!71UU#8SgP;W?IOJVWhQ69f8h3VIyGvbX&ra7lalhvk zM%koqkF*!kbdu5gcm0$|RIcdRFV>1V9G!LTjXK@v{X2Vc5_rB5Sgair5!@7W`0sfC z;%^=J$yw^s#kZR4q5NH~(qg22{+H-*#aqlneL;no7NS)7D`_u=TU9qO{ z47#SyZ1wSUHTDMMc(FEf7#GG(Z zGlO&x@q}G6V9jK-1^nG?suvOh1R+gS`A!&b#XCA7f}&+$s;@_{KRNT`63Sx4D~ z9!kLNbIG_LHAV(O6hc*YPR~D1?&Ob*?u~ljg!@M$9!JG=&-(@L2gDwAEkQ=)q5EXA z>yw|t$&JK)o)s?bQ-+D9p4jl;=XB~+aIbgBp@x|K5=QR?u%ZI;+ffpwY=i3__ zq%+cJ-?@8elSQ*aML?5gX5ZmAVU|ASXLhr8W!@1(tE9fOkS67%*@h(-7w2k6A}(v? z5ws9tj(2aKFYNH45QN-@$qqnvz?cFG`!>?X;(YaV-~)w`0wAhGP5HyeStk^)ogJPA29w@1#)ARU`-OWJk(@#g z)V$(RnG^3l5hk+wcGOqMt(e2GHX^bHbZdchEACuV*`(Kzw7B@9{IqXn zR{LUr7`YE!Cc7W49eV{Xy(2!G{JmLn&yO6i&oy#+Q;&ztu%dgW8cm1}HUCX!W<0@% z7Vk`?u(A+&`B_<#%Y(Aq37Y#QpKAw~bdPb;nz^rR-)mWKTZ(Nyy`v z`t_jq@m3dkdwaXB_oDk||K}8L#^I^(qVw&Bi|7ugvf!uR=#_kxz)jh(xw zzj1ql5)cUWO{iXyAr=lNe2~FCvJM4w;dC$$IxJ4S2~be1PSP;;4k>NjAuy$RmB54r zy@DG@Ms_fJ33E^i$cET~qvEOGe#!3PYb|EJvBBD?4}1V90k6PSf(haNr+!jcT_}iS zf4LVj877^^XSKskaA10S>~4H|rXU?Q5-zoVe!e1a+_dL_I%_c)ty!84lEalcVtkapa*v) zC8CpflEHk9fZ&y2QRbo5Qf#T5AQbl4|z+8-OKbg=CUhjaOwV};eT09qNfjH1g_Erb{*~n_IT_5i#y-%jXC^t zoWbSVk0EOB2JZ70--?RWvIk(3eY z-vZIbU%Oli_=*to04%YSLOxVbScgzrme%AC9wva1NXXAMTJx^b`u&CyO{LVlgd~Fg z)W=A^xZ38kwTCIQ3p(WKIC}8TEM0Ase%h4zsC5Rq`bDchh=;=H<79rNP%ccI_9#E} zV@JL+^W?yK#KeJ*PXJ7LT_M>pd4r!auoX=rCm@&J#roRz{QGq|VUNk9#MYfj^~+aH zO-=nDRuh0F!kz2XqAX$Lf|$v$xWB7yvt}hRr@u@r%u~6$EP;5BhZ!gGyLzCJbymnw zu%!x|b{U*}cOf3}mRGpr_Om{Cz%pP=h&OkoQHIkCu{Mh+(zkmuMUZj0a0A$)Uu*pB zXUU1LoV`rjY4LH)JDoO~>rOpNZ0d!tkzGkQOWj6c00z}!?9EGG=Iy~I%tpg0sA^@9 zZn5C5&QB2oO>OlTO%7JazWi_L+3MVZGb-h1e^Ksb#>hy`O1oFNgjszTW}c5ZX7b&? z$&rzajMG!1c=EK*!TRQPm)_X3jRWH49Gt!V?c9C#kH77nU5!(wp7FrcM3H?Wm3MG1 zSh&SxGTh%N3OX6mK@GDK5!R*sA?OZOR1gscv))y(QCh(#keWOM$Etj$Z$H(`G`Y)@ ztA(dA9aPO)_2}^aN)!AX$5|=xgbN=Rvh(fYuSn2_K&X|dx)>agkWbC55_c2MH$GQp3SLVc}-_NTwSWS6z)8$uNrgY1|@zZ`%ayqM1Z zK@tf4&0C@UZ!H-JfN$HHnALEnaW9OrLl8k7PCgTT=X0&2J{|l+dc>c~CaXc#O2VSa z_5P`=0nH@&GNe}A`M;p9eXf{E)vh!0;Rf8u6NOp#2o4Vq5B=ZKJntkQE%jfABUk)c zaC`KxWWDCgRkH(&?v%g(aKf4Tw3W?3wj!U^V^wE>F~N{B{#()0D!3&~7v>?3ZHT89 z5*B^n0T@a?1xxYT*lBFS%BIUlrZGFS+go^GD^&B3RocO7`hq}biTnUcjYI*gRN~10 z{A-ZGa#y-J=g44sCj2PrU`j@Z*vg!Pp~s_J!_6`V5|fX*oKYdfeo2#&^+a&?P|VXa zmzc9J6t+9zlM(u%j6c^$_EWr1z723Gq<9-$ca1$d_9~&uONMJ_p04JKA*f% zf(-3Lf>WnM9E^`noo8|JV##y;28rN=mx|0H3Tchi_YfFPWMDvGWqvCcF=KB%x@&&# zNNs7;^WHO@LAElnPn&S+DELi&{UG;eW zme&oz#9EC?vb(Xe)}_-A!{x)^m`}?cY!+u0Fw0K_r<>my9;z#;8~S|>;6Rx&opuRO*$0#$w5p93hp!6CF7SL7%OA_j#v^!%zXOcY5PArf zBbG96j5Vex{!t;Oeur4E_eQ)mU}JkQ0YV~-Lg?{b0Scz{PZMEOR7!3)!s(`qtbI#5 zvqCqMw#dRCi~+D`E!jmQtw>3jS5cV94dgs|s4{RK`egR(^bcv_mR$xbL`Z>xP2nuR z=f2*pS9KYnQ7unr07m<26vaFH-<=+h#dB zbuau{zqHS6!ZzfT{+QUx+IM!Q#~9qym!9PFV+xTk?6M_NYG{hRS zP`|ek!0ibW1$H~h>S8ddjeZm2b&B;89^?dg<%#y5lAx$?Z6vKAOnfcwci;uzxmBBh zuQPR6qSZwTj^TkDEg(;YyX}8|PpKrY(=!L`d7q-Gb*IJNLbhUIpFe`-W>B_?UOe>g zLCx$&yQA~Met_2L?!Zs=-NVyVk_)M`{y$bxTb0nilOuM-J)iD*(+<%B|LUJZJCTYfWk5Nmp;n%GfYBj<(0gjYSh$B zsDO(6&Ui_&{AFf`x*gk*gG0KyQnVGL@gc5aX2Fm)&QV>}Q zB!U>;=;#Ycuj@)$eNs0qWT5ExcqhQ{l=hm@Zb1!`eD{-s?I3mT+YeDAJCz;z`Cks6 zZw3Yg#2%pICjN||yXffao~s8hT?ghF;|^+iof@t){pi|vi9M>Eot-svIsabK!N*MJ zp7p_vtUnPpp*phu7NQVIr5119QZ>V`wizauNvazp{!TA(IpU?+xk-9YFv|@|L6J6( zQo*QJ`rBH+crNH}af~CU2?VWtQx5TbD}5a$MaE=c1IQw4ODSG(2NsKj9ytHgi4dd= zekN4E1zL-Tb1>-GT=QXuiW)rx>k59-w=vO02qkkbzKeCXPJ7rz%Cd5lwF@LK$`sU8 z=kM%{MD8zoD~R79vkbR5$&mKkd%5W0jrKBeN2%KHVow7e2hX>XYU~<3L1x98--*ls zRO(*#M!t0Nr&>7kuyLljk$aj}Wv3&5^L+If%IZ2~XsDGMkMSmTcedTELa|XQ-m|Z} z&Q*is75rB85sbYvGj%?3qvUtAv)S!CK)wFIe~4uBef5TSU0q2R{rC+Q!6|G1U6^UV z>FzZIBlr8O{jF>A&@*{QQ^vWfw}z=xYkr1bjxr)9HWkT7N}F-q+0d`E`vGsi@AaKG zoUivPCl@p%K1o$INnzxAm_1Z6xmafmBH%l&=N`a)wrA##sZ%z~9kGQGkPi)+E zq?Gu9@h8?>0d!Iu*mRB#pytN2(1SnUx9!YsT|I3Q~U)Wv7Kj1F-MbEg2H}uhW~@qx+fcC8j|hV>2vY)=Add_N;k`?kT3ZvToNPx zlM1gpUgo8&d{$N1^s@yAcQ`6qZgu8=A(2QCT_%%%{(NLJ5dMHZQp*Zz>IUat!WV9| zq{-0FcE`HV33qN%t@qd2>#h)wrB~5TE4bELxE0{Gq zKX2;|R}puV;pF1tT3s<$?8kVEW*T_TaHFl?Qk9{RLgtxyS%VhI{U$hu$jz`vE>E=g z51MTVwXGcj(UOAhW7t*YbxTgljwh&qpU-VnV3gFtf|NSTZ2Nye{aV+R<7Y*0y*B)Y z(4OOq`}0NH;kJrn&mB;b=ej(kJHCv~Lq_YjM-`Bqyul0$U`VFj9X_2h;qSz7QNLoa zr~0;_%mmAO6nezmSlfr)L18jz-(C%wZ4r%K*wa3pZg|jfvUhQGUyC`vY@c)F#c(i- zm0}z7zYNz)2&2iY@Ghpkw>Pf+s~=Z4!VpU9{mW+ddh*@XM* z25rl!_;eBWd!@ z)YUxY;<6B7cu;-vR%)kTmSMAUHquxrzWP6%|60L!@3EGNGbo`8l-J7(Lv;UOybG^Y zR(r3)aW{4&o6_uv8NC;KfemeTk&$gNvrh$2`|6*W_2dt9*o+;ct4M*kqHLXRdl}oHX(2-c>g#t9hEisRJQ2 z_-2D&urjs{$H%i*l`^P3;Pv{!G?9C+mG4{adJ4RG&aDH|KDc+Gu>R$a_NL_Qh*e!t zLP@$L_8}lFI5?!S#0N0pp?vcc3%Bp$oijLPaH4NMD{twK#_K+ODNKqjN+G%TUVKX_2ID0PO+Z3>^?XXyfOG4UsP=8JX;Z3hL4L%5) zJxNI)OF}=UaRq;_fzm_3JfEA_6&+G@KXcPOm6mE*CS6+%gYNSOSZ=?Jxfm#L?TJ0M zv`0;sSJlsQ^`1>GUCN|UlLHD8*EI%JlXF6}7*~Uf(otCdS#&Tr#yeeM2Hv7)$&UII zQ236$tQ5d0zSZd9KpEuQ$@0fZsM_|myKn^@ijoAH6A7z6` zd6=6JAYBm;8r$nYCTQ%Ylw~yeQ!05&I<|b*olL2Ic6#|`FqY}q@R-1j{lz6}=8W}? zg5LAdz>D*X?jHNNp97mLk!_Q)u9wPJkW=kU&F7xpj_@_hS`;#hJ(ru(=vvBl{`53! z`)I{}Z8o)s!Q_y6U!{4h$)HM0@vTAxp@;`UZ~b1DRzV`px?gaX5dhgfZM5P2`MxFl zO2_2h&y0hnhcWP9-cOUYbs-nk$3Iv794A86kL8L!cRAEhnb+K|LEBMN411}TGZRax zX~FW>$*E=FGSEFg5UrBL%hsLl;M<>khR|Jm@X6M**tlyOqALS-+E#4A+XDI$iTrc0TfGvh#>F{an2b0*h1 zrpe_}vO9?R{4I%zEv>VRD!wBLOyh2VJ=? z{}{0XUBiUiuyl;2rRnRN>NnmK_>?}qUf6Ka_su?I)zJ=a&sNb&K; zaln}%e!k^z+F5KoLGZ+d^ekJ;2ab&gKWX*w?;`Q}g#WieyA8)5*iG;#(!kXI=>(v% zMUvx}*%O1n?%@(Y6q$xrf9BX^AY1t882w*INnu!5BbU10_4F+IwZ;Ey)K>i1aPdp+ zy6gG(bN8cx&5}KSe(=xZX0QE#z)N)sql}H3H85AjNo6w#apRRaV2Y2~(^sfm>;-lL zVxi6xVPiw_$y>GrhUTg1JJ`TMGtO`q{BMybTWTAXUOO`qFY>TUiCWu8Ei6~8=-E+r zzLUTYdlk_Ot+5p2gu5Ub3w+}JcuI3H({N6FwI$*}HABF>?rvJj`@yn8#b9DlZmk`>m-jm1d>O z|4HrjtV1X)%!{*_G+Xo;G(C)0{34^QqbvGHsW`)4f&2YfZK01rsRPXiW78PvTua7D zW&WYpdR>k!P4x7mHm5amX5WM6hUWa+xccPJ&v&%XH~uG()|BiCkWu2O?U91(1v>{m zI9Og^Va_nG_4FH!ihjcILTeogTTIlZ6IOp(6;*i)W)Xf?9^iOAF8h`2T!7uIzeX&~PIL1@6#?Fxt z_D5!tGzq{f5Q&};KPIb~JM}ly)L!44BY?_Fyq^6j*3|Lq%e&O?+0=z1{C%Ge1~(RQ zI!``rZ}(y8a_;h}3|4LiJE~n=KL669BzHuiPRqY82XV-l$w*X$><06b zFw+b~WY?mnOle-u9N9MQ(O=hvVJGF_a_?#32BlaX9h6sct`F|%vON8$!+DFR{a*{s z$jI3*lQJUIkzwY5RT3Bkq{H#XbFUhBAVwC#L3TlrRytJ5s}Jym4?=?abfTD(o9u*n zNbZ++?u=kCyBlvPVoEvp1TYz^kTLrDLiwfeHn(WCd+#@HO9g-=Q=W%$hh`5o^d6JH zPx`*x*groE)V|o|5fV8*h-muWhqh~Q!!6#*tb$_@i*3$Cb0Y>H8%=s#nn!(sch)YY z!j#YaHb^y%bHvYttq!2-=-VSkj?@RD<*GvzXk{FlXg~kv`=Mv{6oJ^*TOZG5T#(k4 zO86>AP!=mQqY_j%x7nXx?VeG7rk$k(|IS|t8ROv08x9;N{@^z2@Wz2O#*)xsJK6lM5y{lIt8kv1TP~DtSIH`41oj-f% zOTS^FnB?8i8v#O_)*+@EYA^i5OD5bPV;bXFuMb5W@OLaxa<2cJyo4wyP|-_Amu#78 z=5MBs;U;yYRN1Cmwa*KRAO(DSR2itbaol=Uv#NCa(N}2Vmi^@7-EU4^t3z5I-f(ix z!}{hbMG>z92@`6v`l+@MB;SKz4uQi(Sx-7Z-ct z;+h?0Nij|K{6}4Cfs2hln!U`1K7tgImp2?4DU6W14-vzoMhZF)V3oYjrCzTs>uhC( z$yFzRCiy%O(+yE)PCbjI10>o8z*QU8D&dclcZzvk0WbiEK;B?WLmHy5mOOS1i$t!! z_-3QIzxl&UX!uTx9*q_hB4wK>FZN1IzaQ7?ziKR5d|s9$sqxC(f$qqfRhW`DkAjz# z(zZey#ZyMb8(jEeDO<;)!Ig8fqjTtQ0G}gO1A|^YN4o|z#hG}ZD_`+|c4zd`ZVKRL zOO)k`@$(G4GRgqNEcwr%Z{qTBt}-dB697@41wJW1(5@4zlC*3)#pnD?g!ZMM1L=LY z)KrI@m`*<61~R6W=$TI2>}}ZA4}qpO@wbbzf?grUp5`H6y`Vf;q3YvCAfI+Hw;A#;QE4$1Dtj z99)^-9Pd3_RfFYZVDeRodq2;M5tyQa3>#v2pli1eF}BJ6q2#66bORdkrT=pa;>DPi z2PGhjHx~Mlx})-(dqJoo8DRB@u^gANY%}tlMJ7*T9W%>LXyZq@%fEX%QEnL1Z62ni z36*!Dxk@Jd`;{5hA-4w4ZGUx$7o5dm7*eti-SLMs0Jf~@P@?GYIV(C@xtL$Myj2{sEdREyilpdmf zr<`CySmEG7NN8`@%}dL@)wyjxU{aYwS)5g{oM#P*QGT2Ps>c3@Zf2RGufzM1aTj4~ zI#{MPo=*U^ZlqqMwAieftqq;t3IAg=iOwVH{!Yo5Y|`zZ(ApbRYAV2=ypUJ7a+ROK zB{&j9AnVpr+na+2itGp^efyUw5Qxn9!bJCud5{6pNtQ#U6ac{LQ$Fp7pO|y_7GB|} zrdl}XX&}htT;IW}=g>zeYJ(u1Zzx&3DMBrlMNPx%W%hDl4GnW?CzV66r<*WylbHCV zM}2rkM8^3e&8$9dwVk~UkGQ#Yr?0L4DDet*&!W5P)E@N*p`XMylR^DXAp+SK9_kq=QM%a-aza#&>dejH>f3SzL=U zd}$-)o9in=A@*0#+||db@)5}CZ(LqBx4xz#uZfnh0Dp(p9%m!HYeUD^+-Q^GSht79 z*SHy?|JDZAhMR(R1tNlN_5_tO zf+SrYr}uc{IiRpN7=NP5W$VCpUIj$rksYtc6W)|_ippTHK9U^b zx5GSOh0@$sSr<$>jQ4+VtV(vDt|lJPW*6UenAfM)=Z}{N&Ci3Cy6{He|~8ae9cVLxrW)WhjDX z8vh;;OtCj($eG;A5E5=q28WD=@~Q`a#MsspOBr&ll~-8i5Hf}P8JAI4D3zOJ^@oCG z4&F%=R-oX;_dGUk&^v^hFR7^@Cer*Q!*!#JRrNyu;T^{oe?rde(aMkiJkfds?B$o_ z7P(=1_z;DOSF6<7F6~$|ScZOq74MKj{fx?THby6t_l}4$^vwq7o z`0Id5uXvavFHq~TK^DasMnY07-$sox$-iLrG*{AXV6}$Z7H-FvBs(&|M<=~6%S+3( zw@R;XwqtXBxr&|$Ur#J6zc78tw~+DD?*<)Xq$u@EC6N9Q#Y7A_2_gkOyJg8Lm=p{1 zwVRET8O>HFSQS3<#H`l>0*k!d zV1}WYVJ4D%BXmD9plA1`#=3FEBT0LzCbmSG8#c1oTrh~<{g-YPFr1k6{JS-wP%`;i zaMc~11VO=m@IA>@Bx<8?~^Ty3I3$|4{W?rFH>rnY~PFZ#gQc|da@Uufp zvE}safO86cM8e*J?e1Djif!(~j58%@vTo+*liriI1%eE#rYE9o?L>rMT5g#AK$7)! z);cW+rWD6bPxuXe<&NVu>5zN=p@;R*d;h&Bv=}trN%R18n1hS4)zDYOM^GuGai!<= zkMtDw2#&WMoO@j^RtllimNZI%-^#~0yG9#bmyQTfbPgteM1AA+e+)uJK+pqmJ++$3 zM;2#aVVPcsY^Irp-A`&pR=#oCQ+LPHeTZ6qngc|UEt}H($B=1-p2c~-!PD1=G?Uaz}+yN$6TAW;rM ztI-2cw61|Jph&gR>=;%jtPlDy^zx# z&glWrsU7((hkw1HKFe7&v$Ln3wcO+4pFLDISQnk#^jI||n43Uw)?4HDoE$zcM1Caf z%-<i3@?xmtI@&Y>0@xq^k+&&Qu}CXG8+ zys+e|Vi)UIHO#qY%os30U;-SyYlQkqx(|K2sR(1HhF&sVuhlc2n zWuGLZ&c{{U3H@(I=`cSQK63ofc=phu_O+4~Orf9&CnM9l(yDewobWf;aT{TSUq-$bFBa`2X9#0@vQ9pahhQ5)O)h+? z?ob?rGgle<>N5!`&-y`=*4=qBtr4vhe3f_^1uD9B3^1Ey`umamHTjz*&&@7QcaS%e zqfXXqDXe4(te#KQ$w>%9^---k9#QEE>wJa7OTcW%`zR|1t3O?y>=WP2dQWs}y*^O& zx8?nJY|$={Gb#1@EFFP~uAe(N2aynYp-X(2;?bymli!{^%krQ|c^OSZL^JmU+%8dZnBOsj88^WjoL;&&_Cx{s>kny}qw{hG?CFr} zPIpEXxs*cdx&ooh&DKQO3ZcrWK~T21D>(k67v)s#gP=+7tL_#`)0u_}gXNqaL7vl; zh>X}KZKzyBy^(2Ko7(A_JI?WP-vG|Pt0`P6iaQIizuC|kj8>hB_9)h?wq#HfkwD+x zC~7Xh88y&>RV@Tbi@J_nQ3I@Gv0_PPki61R z6Qo8ex6v2?mX}XJ3)#-J5MDbsCp!lXj^^@QDe5Yi?|-pviTnL74P26%WtO(+rgf*k zhL^FCALLFh4wV#(bk>$Qa-d=2kgk%+plWwOt|r*2#G_wF$%z$wvn_1*<)z2{sD+x) zz+Sxb*arnL!~PpI^Gz<%XEN-k9)7_5kST(Bm{2p_nNMw)+SjC-+&`r}s-Bf{P53~x z{a9z|s|UgG>R7jWd3i$AM-2)STSVskxL`9!-9OJUhiM?e=M99O6^W#{%)h1vpD#Vv zb&|6q+Zyp&v10GUjZe%Kel$%ok&w*3^O8s6twYWb;bAQ2$k{Gp3OPWYJrLb!+q{VK8r!KxNicdclEmSt?x8`bkIAO^^H=VeA zTh~uF^LFU_uUQ=&Hj%jFVLw6OO+2$Ci;${t!tiuyJH5pioA4msw8+xCOSRHsI!Rf$ z9P2N=T6lOG0}@jG@a~cNt{zxa>p!rlhf-~UZMOc6(e>vjq+^P5F*COYpL?o8h|LwM z(X6SUTYp~F6?0f*h2Smp__`fOL4>EXlG_dHiBo^X_*jenhf*ygOY=iORZT=fZ3jt>O^jcqVx3p0qb#hIHIY3Az;%3omAOD}_O#^Xz zA3URXN=s&Y?q>VxA#V00VrhHjlPDmZrSPYs@kN1Mmvdcu)huVYnK2!;b#SDw$tb}h zg*!DL@$tR!6bb+C*dT|N4llj(T({((s81bS>535R>uT5BUt~kOjOQ42Tl`1}?NJoB zr(fM&$;%+W5|Fw2sCy{D$C1IpI$HnfErHol+aC7{g7FLR!mthQ=1a#(eD_bL?mrLx zzjJCqym1{Al+51CV4mukXswcr7mGFffA0C>kd6%@?{9@Ip{Z{4gYQ?LVF&w3-Xd6{ zBbi1pw=;p_3#Y}xl>c@M_g8AM`G~0rc`YCQldGm*=+` z?%$e_Fe^lLv3>_z3^@8$*oLJGfuB0o)YAYUdV+%Aal>+ptiDN}CEOn~*c@?h-il7iEYB!%EzjX2RzYn^aAK(Bj$ zzY(c20XLvlU^aDnt2(NHX}N;6MWcm(gFVAwI%1YO`c{Z!Y6VA7-OM(8Ro+7q%$79`^u2&zU5eTE2H&@&5857^i=f=X{&<@3BpKD4kI1+zA>Sxamt(^XT za@gIOi~Z)LvwI$Gn1+as-~1i-rnpsRoR+`w%-zyrU%5owC#Bv_o=M2^i0rCS(Jf&Z zFk_~)6d^wDSW#PH6KOWtLLB9gNy+!1Hn7=$+kyV3^s|p-Jt-ds|Nd5HXtjy$nJ#E{ z=b4hrt(9)yh8G$@K!+*O@bs6&%I#+D3jgc>eK*~xmx{prsdTKI4d|qyN^)jWD;W|l z_K!L{yoRaQ{X~*;@V%PS!qh9jfA?o>^uN2?M@kP0oH-gx-VD`$YK`m^hdA>*M-UKK z%l9?-7jS6MoJ!8-_~)60UW;a0GVEQ#XYgky>x%Di;#Wp#3LVlrfh-?!a#oL%oKT3- z12Rr_C-wKrfBo*v?{*HIh(&(rH**}nl^MsWpOOOFhxUOlFtiC3DuFg6 z#5`pCAm+Oah{_8;9)kiJ>vz2FtFcWe?`Z|X*d}a-@Ufx$!%RuOG{E**&Z)P#ig-j* z=iPpUVM132I8g*pbgyTsn{}bqL9g(E!0%Y<0b3g?j@%iqtDQ_DYW`B65;mQUyh9xM ze0+7-sVdm5d{a&coG=fMR4)@uWZ#Ly>Ay^5hbmJ7eVU$*sO@4c7rKE<+e`G{tmTiz z31-9$$#S;xP=auKS{8EkPt!~ zNr68kY4zrA;u;lT<~X8uiH+=nwN03OdvqQE|?LZ{@aFJK| zu{W&SGHK>(r`&JN>{U!e$K%ZR##_8i2*lT`Us@c_85u#vKld(hxD3E#UE_K`dtnD| zBSz$8>SF1Ovrxt7l9Nz3^EduOiwB^wnm73;T-%oG65!bvRWtr?NkS@&=pd4IDq^kZ z^yl$EMHkl_(dP!YzGpLU8743IGZX0hW`-;3p57BjJJ8{+(IBB#OQ_%=f^4~S9uNqx znG$DWvhJ>oHlD^CqlM$s{lmDaLWJA9HG=TUmI(Obf*r>-{d=525ja4d@o|&59LeG; zC7Z>5$f1^Ed_kU-(W*M4WhN78X*_O#)I#ER#23HMFWwiA(>6cfj)Ba_Z_u9 zZEuYo`P&ja=+DAj_fd6g--y|_PU~^hNsJ!~vx#5%{c8U=^=s7o@C4k9D(dfxnLiqG zKQ+8hqJFfL@CYZu(4h37fxrN~@5Z8cZ}|xLRBhY*H~hZbR0CZ)q6Jf@0`ZkXSXUh2 z-PK|R6YqzA06#>1QGON-Lc}^`#m93Kwqw=m_8y3D3?`TEJ&6iQ;!X|bHMV}EqV$Ey zr~-?d@kW_eT1i&KwPv*np;7ihN|jFSdm%6t|umO&RMi@F|?%OaHX_(;dd{ zJ)6j2-qh8kwxi3!guIhd436ueKTO#>_}hLM-tafII4(m0i)}gFb&LM@_d$<_33Lvh zBBa7LD^*Ya#;ljf9aWT)Ls?e6;l!yzf!G**ml(bzK`R_qRmXaA{8!It)xE7`Q-j*i z|DI3eItM<~Mei?6-5rxJ93?NWvg%*B%z*@S7E?`PjUsQ^>Bg0x#T097*^HI+x2SRK zo^bJNoraX$=5NJ?pXr*k^1c8kKyLf)sL1rGVk*=Wv>TRB56_Q(XXy$4gYn5XgSFd& zMF51dZj1C#O~bC&+eMD7X{L|$0n4QUjp0{rmgL>|QWpqp1YkB?aD_D;>QZlSo}9;) zc<>IEv1r!Rh3JORw#>lZupW+HrLRi6>j3E1bjppaKS6CCM`&lSJ{0=%=1?hlWB%cf ze=BtzQi6#}g-KCnMtT1Hrje1IodvD8c}iYb$GzPNT`a*8>) zcs<(~v1t-Gs+nZ^#b#Q%itl6;cA?MP+i*G_cqjAkB(=zsSUchayXI#!-?Tp_9hGb& zT^D~#kIOp$d(cZsG<`{~|0V76kxyAPvrj$~nu1RNNtLTOjOwEhhM?QHI$xqKoyRD~ z2PV{@a$P0J`g@V^?dSc)DfoQ(@~U=!@2K2EiGT7|O|p%t@c|x$1*KPCD_)Lg&28?C zmK41w*O!v0g{8VPNrsWL0vE_TsfeHogAr7MGo4I&VFfNBnRNvTr@3RCQSG@b0>$rR4i9rcRR>t_M1Mjf{9wWXZBi zou$Am30@_`H>VgY<{iIw%7~@Zg}ZNT=^${oFO{WKcef2Py|2mVx>{U5@5UwH0@@ZQ zqi<+EdGzRLAj6gLn_$e)xe{Cddyv9ubShq`U0OI<9^8(WZ%#TgsE+tMc#wMGH1roL zhQV&!PhM`0w!MI5Z%a3-dH;}j@@yx`of++*=lHGBCVJMS1$V->`7Ft$5BF#f8slMi zcm!GbD*A}I;-^Z&Z*P^HJ%eI^Z_M>+(7a?I$Ru$9=MR^UZq-=v(h0&Og8Vz@^U5)V0^OzDcn6 z=hT!fh0 z;!efKls}OE%1Z}=oklI}!QoRgkD{AMvo>X_R_N8t^tH8E?bDIkgSvlI9(XN#6XQ|@ zqQ*S4TgY3k?8!TUa-K{)>+tXYoU?3|$~)~*DL(_iw*<^CI`X}_H`r52npKJET~>Gi zN*R>IxdD(jpKL!6eO*^W0W2{2wcd7xE_`gm(_)dfk~1^5SA~x1_ok~T7eYO;=sr>m z^LNzb{$b36PVrxW;q++B1thcx!vkHwN0+^hr5^@neGl<(x9<7 z^l_X0<0riFkFMHpqD6QhI&Ua*P=LA;HvM6rDQS5)Gx-`kz=R@AUVaFSh==5s^Dye6 zhDaT_09J)S3~hROMS}%=14Q-X?4L~}4Ne?O+H$0y&;Ru`<|bGO!YnlS`L&Os#-=;B zhFd1lx)00_=+pQHm>3sCX~nZCv4$5S)7Z;R4HcFiL{TqLGg0r=#W3p3p4@Hs_}WqF$fv-{=BUymn@LuEPk z{%c!=r59{N0jsNCCsQ&2Ftows`ne`EADxf*FW{%z#h6~0e`UUMiT8Q=I#$ctvNFU; z{mwfB3!UYFTRi;k_u&3Bu{J?Vp(kCFTq->mn5&VNX%!YxB|ggn9e$dZ_@4`>J^L66gfl|AIOVbU#>t`|IN$02mR9l`T*|eFLg6mg{Q#K zf|;pK_wuMO!nF$o-nV-^5?{~v4OPE4K#`5iE#7{*T|O!!=2><`!)zu2Wt1Zn%gS3{zqrKvfpker(ySSE zTxqc_hR>9HPz(YVoMYBap9}kjY~3-u0;;*s^;u?NpYsHQra2nw%Wv}4EME}#GyGA; z{(Sb?`PacC?`^CiOO0rpddV?s>>@eCLN6qMUXlGrXYA=p@A=cw8X0$bn@ywn(fOX* z|Lv}8sHv-q@Sb)pCyM{{EbIYx@o+jzvYTh9-vq}Cz@E;FIG4D^g8)<_zq8Q0@`NGq z`{rcY6Ia5pr`;cqcaHjt*&{qvorR16$)4y_RFo$n+f?opKwGE>U8^8VZsb4?qd4AV zSoSzx@vYID@BOW{Hgf$(>N89${lmc}u!M|C$q%NkX`p}axcA_^r(rOql>8SYeikiY zb#Aky?*@mXq#_#*^{H9c3;~$;)MsyX;9xa=UQf->gQVpUse#Q?swE=CW&gqPgbxae zrzglJKiwC_#bu(| zf5T#Y5_->>Lo8#a+cB=ghYD~_9VBMDH{pi$_&qaEt>?~Vrn%I5{fK4%96Mz)MoRmhcz@2ospLe`jNqj(j z3pvY`s)G_@J;(CQL?Q@Cp|al9*3tu(eFv-Gpn1*k8zI(r3@Z6yENL z3w!%%B#dwOB^(}IN1EUM&JhbqFUv_pK1hZ<`0om0Tt44;-dM9G{0vMGGpqj_%L5V% z?zz}d$A76s6Vy4>}jN9h_UItj*r6<(elkGOfO7Ik26k97qkuDTyz!ce*Vz+ z8|b@Ivue3JqLMrY>NQ9z7kJDEv2`2usHnH6==8qL3pSPfvaMQeesnqN(;XnyTV# z7@pt(HOA*cWCGwVsAU?PAq{A1Yzip$dBw}v z(W`Dk0(B@0kHVh3yihZho^4tRbo1z_uSL}FThCzEWSkTh^5H{A-_-Ri>Ncm}>nlWy zo5#hI;$lv+V2P{PQ+5TWY2Q1bQI~S4RQK1@QcH5fcW<~Hco8< zIazXO%3)vi|@Lt?OSX~?)}N-5a| zu5o9wJ?x@0yNYB(Yb%tw$rmLx1dpm_J!CiQg#gF#``Dy zP^|qjerR|q2DU~gEp!AOzHFRt5eGZ=H+)w9Fl-LQUS1iFr%teh#~hqbc69Z8_uumz z)>Ds5KrfqPz^DqM3h_&tq0LIyL%#F5!y)5%(A0A{U^bN*ZiQHZ&y~)+&1-bxEB1GI zV8Rs*9YWlFPyqZ!}u8FiU}5e3#07 zUWzp9-A4ODtqqd5@q~1jEPR~l!6B~AejJz}1;6Yx1Z#n0VY&hFX=6@hUA_FLc`KPa zO)r+imU&7AeJ+`JmAjvE9)>-ZvL^YxnIUr}VqH(ZidAs<2OS>gvKZm3G&? z?_If4oc>CBD-id*;TdNq7M2Wm;~!gf<=3@2ndpcVKyYZo>IdndtCBpUW^*Mm zQO+sPgZ9i5nnOfNee_Lr>aD+zd~24-<;ac!IJacaw}=G39`=>HmfZtJCWLqtSOJR> zf-b`0ipo0=DT|*1#NEXiN{RXig=Ctqwevh(dL-lDk->BVwDSv0iW%JWJHqV2M}Luj>aAYFU4ig$7fd5;`CLy|vnn7SRc|&IEIwbwc!3CUHVz zhC-z5k|tJ}cXOfZ)y~{RYwZ`1vn!v0{C5>q#MO(k7SeMr765GViA{Ro$^2A1Sk@$q z%(!GHNE?;)Cn0bD!DRwX766yQ$e>f&&PTcDE z>mS0>8cYGhQ%OI#@`2{%Ww|#W>8NPkBfl|5kW|ONxTFEcrVf?#y`KW-^BWW$4m8is&`q%_!S^C37uOH5U!u(J?r)JVXc0%Q8NRHzX;sY*5%Ld#jV*Gt5c;@hC(kGSW z97+0Txv|QvKR=X=o)3n6BMgHFr=~oxFISet6S;?w7Wr&Fz!IPOs%vShyQjq;42OvD zsW6%rQ7a0as59JbPq9Uwva7D6>hm>;pR*Q*n8781dOVwR@j)7P6b=wTRiReObHW7U zWAuM(eJSvj7MCS6+X|Kzm~hY8b2ZD|WdRJ2CQ0NY%Ke*)ypn`~Z|7LJ4sgo%{DbbY zI~i)T>U##>DiGErCYWn_*8ssCnF~nYfGh^QJ#7C5dnIA^nSqb0uqTsYgRjdc zlX_wY2h&oKkGcUd5GnsQeO^eNjED$b`q4|Qf%2H7{DT0AZj z#ea*RT~9(lOXma8Hx70dazz>A^q>MB4cv{*12z*x*&tm&;Wq{rX!je*+K8uXcDDJR zi^$c@&F3FADum32GL2fu6Z(KPv@k;S5k^PfwMoO!)e<4QJ#c@Usg<{u^|&M3yYXu+ z(9V)Is0L+K;UOHGCBNq07!A~!4^v-gNp^N(_xM#pxS*%@$2D$qDle$qqKi`6HI&?Q zPPK`V+70@zIY{)syC$W?d2ZeozS)hv4TVl0m9;(H{GQ9KM)l!tGRh;U?ajG(XgL!t zGT;kMN#SGoyPZAP7m}u;(c;G42oiT?s`$BS*^8g^7tz6MH|;5O+^?l4G*x4o9Db{& z+Wus3Exdx5_wax1=eFR@Uz?jSyg4TP*|P*#>$81v`lLI&sGrd7qfeY)nH9Le<=Tzr z(f5$;i;CiDEN6`c_oyb56iy|-?~)NIL6GIF)FF&(Df`or2gzq#2l*?zQR(>E6!Ho02DSlH?ktTrj7?T4)~ z=qwL%spG$QF0pZUBnjw5NhhOxrTal`!SN2g%1f2xZkTMWn(h#uMunP#Q-WHID zMn4U)lfPo`n+%CKIo=g-g30lqULQZzLcn+ibUHQFFuafwaL-Yk{z}tdP9Jd7VWp`} z)*0~}cTN@-stZe$4;TQeUg0Az$TIqluAx1&#ct2L*vZ}JbObY#aQ8kJa143BB`>Jj zzU>dx5&g>@7VQHTKkfguoTMOjIvJhaeVg|%+>4FvA;#s7f!{+`4sl*>$siay#ww@x zQWR@rGjq5L(G?de1rCA~iJ8Ww68r*e0y(wFg!f4_eXR74%U`72B=w=Jsw(0!gG<>X zy_(JmIzLe1hl}?#tvoPxQ5$P(bxLL)_*&-P&3;&z_`8tY7zl?@3>f@Xcrhfh%SW>2 z2G(qSJ{E{X93Hhb0HUJeYEjDz{%e!B*`FXqMH7cIOQ1d`PjVpa2)LWacOICY9-NEkRZV?mqi}j433N}Vk|#0vdKV7GTDL(JlYB9!ob+6IahUDdJjzZ zS67y@eB|C*3S>GEIs&5Nhs2@AMu^!THfa1~tn+DImTaYd`(dGMMdwEVHVHxgDC6(T zHy59}4#@DY@6IhQ27nK7a>6}9D*#4x$4Oo41bMv(|9iK}NfA9$+eL59*29Lc%t-mk$%Hg+6WcntNSi(?g>D7BvQ-69C)C!V=>gCN4 z&!OIN&zOA9Es-saCMCma8ahZ)jZ^$qoVI;~NPp0%qAgpVnimZbUlt#_9}TKf5N0Y1A8)=hgKHT>1k^2F?i7NVksTAXATDH&{U8h*(!=mWkE)#C#i z9*O-~{Je^ta+Z?97wZ3X(S$Be%rxW-I16}y;6+w^0N#yb=bgrVs5zxWgacni0YjhY z2E2S#A>{Oc2#)bEKI2qBo?Dies9%N;U1jx)^AR3gS}ME5o%{M)6pi^(Gr=#E(r7Jt z>z7cF~?PoXk6h()Opem0UkeFGxK0D+=)_w>x~r`J=hf z(2w|hpz}Oz`({SHrI{L!HWL$})703S=3cK;KHg7b(nqnaLb;`5dbb^np|9F-9m6cy zspv{T9Eb*BqO%4p3Ls};omc*D?5VD$Ggp-!-fzsOz&cUa&Xq}Lv!vJR6{m&3i?Tx^ z8_I|xMTKkY>vZ(@IF(rKb9VhH%>P_KK`F}@6J{P3^m@1@lURACY5EU6#cSNL{Tdl= z{uGg*f2FTiCoLE`Z0U~Ck480nc7;=c66ZGew|-YiVT$CzS_A1*u;`DMuD*NY6zgwO zQHiN)w6p;57=|fZKpe9^^j`m|S{%=m_B>;+A9IQ-Z+x??z*Eump}-LI;dgX1$tenF zE+lsP2AUhp)BTGp>Xja)CnS=lW3mDrx!}wMd05Go54YZcr{5#ab3)2T0{Toag^PD! zrK-Jte=Zkp`}!)S?F4)Fra21$ZkT&_{y2&=*`yb*7<~ls7_B=97y8yLJtBqOipVDU zkLm&(s2uUJJ~uX8H<7*`F2b=P?dK}ok9F$nsLgp+7UdQN13!uj${CMkG5`0>pW2n8;o;?2+i@HAhgV=nYqg z{QQ;1^;5^j2Mus90kEh7Ub#QOBr>pT^Djysu~vCri1{Wghf$ta>U%4PH$c!LBF%I} z)R{Z*KRdt~qg>}ZviWGLJO3Ve7!lXC!h>fnxPEJl22a_cBua4IvS3FUCw;Ki$Gw3S zmwUI~1Y=l%i><*5J((lVucyWWb%n&s%Nk2?O#w4@6}1s8a1;;wAo$)D_V9zfV}<^A z1c#1TpM$kSQP0SebuFG!+Te=dE<g*?HI`fvGTWN?^Pi8q|7CoN$4 z7*NP!DC-UG8O``g3pooZ2B*EO*uGL1r{KH$Df%p#+$tA90{&X?y~2ITa^lAnYpJ?1 zRyAru*#rx~*8VJGJDbL3_>6O?fM~f00Ck!Ur)nwO4ch4D?2Y~U3c;1nQO!BoWpBlwX;)<-9)?cW58dMZW_RpZ1d-HYm%2Wg^r*2|GYR-q+npH1eOk4I? z?F2siZ%-^~Er#<=_PGy3HZgxUaLC#cfX?*R*4$=r*wNpN+sAvf3A9KiV_&d+Z)aQ3 zp$cDxS7r-8yji`#$E4u3A2qk1+5ZgC`eP2 zb#WPT>eplPY0Y(K0#6#tDc(k9$=n#OcpJ0R#?ayMOsUXXblY=Soh#vaXYg)Gz;n)eDnN zA{i=?chV@NjkSMUJ)$$yOpWo?gZ-J~)rd&O-saKL9BFFmEbE`$!-?jJojGT7Q74|b z=BeZMllsWrzrz(jd{^(5Rm9&c2xa z5EH%Ycf-Zo&6m}CBQ_3O4@1fOb9=|DJt+<|8za^0 zk%6N}YlkXa03Nh5PABAOZgscQU+r?1%lW|te>tu|d3)g^5ji@8*{oA79&w0}E$ty6 zs+{~QI!;p8{HKDn+PCC?oahj72TMCS*p2iGf%7bp_%znKt$AWo7afl`HaEkp*CUR| z>Ey%-CP-FL{(n5{>6XZowTkLC6!qVpzpY;t@!#Re(M;JAjn=cVesUD~oABZAkjah( z01l6iy;ZTamh0rzRU?n{m)ZASC(F>34D*r@*JOfa>4UQ|j=gUH9}DGI_HQst(IQNw7x0A<$hZ2;W3{^ z?ZT6ibtdQLXnD9|q`Jqau=I}i_Y~%@bY$e$RvEx7ZH~Q1O*|S~B6lNNZI5YQ$D5Id zhs^gMkQq(FDfB}@`5b0b#<7s~xc_AEWYql{F>Jppi%5;_iLGuwzOn@@nHX`1R7|EV zEs-{Zo7&q?ekICNm5YqCraX5O9l|P{R+_GpxdCSnagFY-FpnsrqrPikXsB~}!sg2OTHGQpxIvjI4V2`aM4U0~6(hbN{koNGCftp3=g%oA8;*%1 zJl1>^jn+Q{Yr5~j^1Ro~&<2;+5EmDcTe7idXQ*BAip_vB2)u7!JiOqqR+OR=%fXo zkr!FHaxhHJgZ&kp`S*)SzGpsM=IW44eNYg-4*08@nJJVGF3oLjE-j5xX%0sV4(fNp z{G)&mL76Cr@Xeb0X}f$q>oYC#@2buI*C|#hdvfs0l|ts|;EN^o^$|W8H{TIYCbeb! z+uMNTr}TVzyEqB6WZzrs5p_losMPV;pmp;ttH_pwE**)oy2SMJmt6goQt-6RR18L@GLaA$1(WXtxV)VsxKv$ zVk+=VM9OM<$AzG+@ePOGw zKk|5^zg=QJ-ec>aOK`UtDw`Fkh=<{~_llKgdL;lYp}WS%|5o57JcR>H#m#3Cjfk++ z7AEccWIwCg0cpsHXp0P)$+gX_T#npkx(rOrgxBBxv!kMNM>!!-Wt|U0fp%L}+ZSYQ zAF6njpxrZLeX>goC~n2xL7`UzTF2Jgj>fXG1_yoNFAql?!ikwJRSRp!$uKpd)LD?^ z{oL2CN1G`c-Czk0Z?Gy0{2uik`Ex|@ZdK*}Cn#K>8Ol#(h_)Tt8*M6ICp)_FQoy<|mY~kOTr2Srx+iio_i9XbH+q%{$owpPZzBwTE&GFa!h+-Fb=;Zwuvn#%EQBi>jU-c%^=F(h$e@`}Mj`F6|N^x>@a-yJoVDn`~JnnB(llJQ?ge2&npk_DEuT53V!&5sGS>EF&454XI7HV?Ptn#<-))-I5j z_VzZs;YIHD{XJ&a_W$i|F43AZ1p|pqnS&Fv#QdT{^|ry5k7ummk-;i8-xqwYBw41E z25B5KBH`^PbIm7o(Url|4k?Gx({iFS|DJrNq?z!o2k!S&+fSj@ucpGjY?5w!tXNgA z5ze1Fv1;GDlFEF7ksO-kJIymkkm9X58jWmKZIZR!cbIarzJC0pHza!|LM>u%lP_|o zFDr7BT+Ma8D>yXLM5lS`-wqR{*woDB$!cxmc#3`0ggKAmD@z>4&V(J+Ecmkz4sJ4= zyWW1It&xl)W-ySWmokR}+Oqqu8hO<9h(ik+g9t>c4Gl=>PN;IIcwQDfbuAz zuR{81%kOeOkJf<_-rSm8doso!vHkK${-5YSFX6SI`?3Kx9``eK;?3hNDp{n>S-!R~ za&}sdKAwB>Z9g?FM{~Mmx3DIbxPzKeIm-t0g@rbW>$&U zhv^~{BF+`3zB8H5n8tP(3&WzQPi zQ#uqb7t!O{*l-QCkdv1hhcy~S*rm)f5XtH+mc5k7Jrerlg`{As#LuakTP#_2w8fv4 z);9YahSZ0OHePhy`gcGB{}fM=NXEZr!Tuuk$Q?XY=#^Sg@<`tBvl^k(ditZDg3vC( zdU+t{g3}bSnZ}Hsm!zn9NtZ{J@{{~}NRBo3m?>3Jp)%}c)viZpk2?6&17Z8Xp0VOz z+2?(;7gOVXreA@%9NC@40IIfn>#PukEoUBBx2n-O=RaZU8qd67`--$ro@2gcmGA)N zlWAIBLWxnu2T^1BHgqQQL;Sv=47qC%;^!9-6m(x-y(ooa*;~CxaAkPMoy?()5TkFG zCj|;9xp-w3G##y#r7LYW@6Lq(onNhovwbbL_#4)6dp)Sa_^$U)tV-SjZF}ea)fA23 zzuqUm#)eD6>6?A3+j%Qz&HhgEIot_nvgc+WF{p=rU(L}FeEOd}y2-_t{Te3$JjLs! z6&07fdew}N_J7moD&#e$W+H0P3pOg`dn;Y{*4tXkhivjJG(-z77OEc}Y`(sl^9Tz& z6*RprqTUc6eSB2iZadvVh9tLoG-m(<;vUo|Ru_dgkK50zn$+y7yF3z4iq*B#XlZn> zRljUfG5jpom7+UBlzg@Qu2ncFsPygXa>dHEQ*Y6dr8&SX*to*w!`okU6bkKIAJ!5- zWgbtj9~&FT!(0h>*`cre59Rqi;fGW!UvTfRMzV#>En)JtF}Lu$Y>(B^p)W)kOHBi$U*U zW{AxWQfxD6Zef+OKd&uRYOQHsok=xx6>*o9qqEV&#sZqkvl187m^LQ+TPxK&herxq zC~_b3!sAnSUv_OoJrk^(AovxE_h}8GG(r!yT7wF0z3NTckDELuvVH3xBIP{2)~6#5 zJx&!Oo5no(XT8;^Mm5b-79ONVRR_7ap~H>&j!Zxr615U|cpG8?;cE@sm+5{utW5Wh zEYzp0xRAkBSyS&Y%jM#dih3IR^H&qQ1|CkJrvj(c`mw+pm&|;hcZ2c%caJkE-a0a` zyi=XH$L`Sy0X4t@xU+1^w^$&y0Q!=7hp%IJCZ89Tw7ZsHpDuWu#7o>UUj*wJmK&+pfp9ff+RN; zcCB+?o)(x~BmJ?S-hO#{WW_=Sn?;)YVY#i=?+~%? zVqpQhLNK4G{WeiYc(EHj|Kh%G6l1~HFPxCEMY&#e0qUCWYiC7X@C{|wd$!VFNcAGq z)2o@}SA5O8)8StAGb%<>#hU%34$Q z8T}gTO$!RiFnmQ_z%(!EOR8iqOjQD2@KWO2ePeA-edLuKovd@d9cBrdOP9b3w5PeH zO;vPe2Db);78kQy*#e>%Z9LB&89op()6x-)uYm@8VH~i)7RkSrUZ~mPje;PgYLxuas}X zLPcZI2fyF*5tK>6h272;EC1y#=3`|2D0U-IDI@0A&HAKU`~IJ~mw71~$9uj@c{i;^ z>+**C`YgO%hwu2n>?z^ROt|ON%xdc5QAGQ+y{V+fU^mW}SIe7{J$rq@dvcZgD2Qk; zn{~fi`u(3xo3%W*{=jCdysAD{{Cv+K(?7-E?rO0)tO;DHB4j>jxz&E$8u<-u1kLPa z!+#)trh*`)d~J_8?~7ic+d{WbSv<6`U?FfrT16*Hwkalze55#gD_@NdP08WluFR_Q zEm0FB8e*~e1=dlVuC6LK>Rwa1@;&5mRpoYfL&|QHO*a?$rA7V!Ld`T9^VZR#IdK5hqQGM>I%uReqt(_rm5f5)aHn5WlT4#sPK)K zG&Z+-Oq5h@ot}yGYF$n(o;m)R!VCxRpSF(Ocns_ED!kL&)>cQ2p4F%DiQf6$L~YJN zmS+S~+22C#M=MvsouQuw;*eQ0;;z`J?xSDs+vT6bu@-OU;6!wQGvcSv8MXU1jQ z2At|Vt*|S4CQ-c%HJ&3?-o(1iX#}+J&QaflS8E0`Y;V?GsPyW(+VM`FTkJ#@E`izQ zuPex-J2q6RB$9mU?WUTUo&dEZ66 zs9`!L2v__7Cm>XipBejKM|V}F5clswkqvEaYdv$^qQ$sZk0?TAN=I|MGh>xj+L5M6 zRtT)2uH1=K%nn@VsOfS>#*q3tLF?AG@NmVP3ZHI)=agJut+1Ne4zE_SYPs`gy>22Y-Ifc^n}vXO z&iC$h9Ii{QQ!A*sZ~yQ`(66=^_#Mpgsh4nOSk8nWhW)yf}zFFXmb#61NeX>X_M6brOWjzJNrNz_}WML?yOJR$)V!Gc`kq_l;2+X#yDYz$1 zJP9HzI$4~(oM7TCn%~;oTv6fr$(~L3c0Zh+S5Z+_)_(9y1r|&=56Fpu7ONWhU6EYa z`j^Mtnu+midnosM3GN1cCS_j#yD*Z`H|%;l?~@10nW@}?NP1L@=9`@M>I*fQu5%qg z2&vqcf(tSS2CKBICRd!F@M2A&97ju6H7b|CpDywpLS1yb_&#TT08~j#fd0Y&UC_y? z$jn+jcxC{F$v^9p#LweIOxzNb@Y!aD@N{8$y2N{6Yu_|7x_lsu3}Zh7Ulcw#R!e#r zM7@o{LgcuN@t_xG;HTKX z!qNEDfuK6#ZXs7UoK4cghUqKS_lyMOC{3uj$LCqtK6#cD(rn+h{X0fmT{MW?^)CH; z$UQ*4)krMqczCs{sseeR_^>w7<&yS8IYH*YUd!8Xw&3d6jbe6A!5245%q?U_{3zJB z7o3FUyD1ZWB_I6xA6QvT)Y8=09*xLib1-&?n-7N1Lv+wmsuBPyD&Z@bL6w#*^%2MQ z(}LChtf1{nfHAu|0w zx~}>f&&%cvv!q5Q5m~dp7*h&xUtzey)&AS5PqufQz&rE{@HdY&%G4 z49?%~^HU%`qAl=9wjNTPov%bmN>4qd>c2&gH&0kWpJCj1JNwu+7Y{|lQjpjV{`5Ml zy!;y6(;MefpZ2QR%30uVa3K@A-f2rlNzz-Glh7vh%Y1ak{#cD$fE~x}dOHrys;auI zHWgPnyCCGou|M}uJ1;Akj$E*DS=qIY{dcQPRd#Z>@ULKI8;;<^uFvQ47eQtl@Gh4L zagL7t&ESSDK@1jzRXq4T7hs1`G+OD?gKqxYXO#9AQW!%+eU0n&R(x6;lrcx4Z0fD< z4;exSg-CNm3=oW$(@S`W9ldCZn9Fy;W-c>c@Z4jkToh+tKSSk6XVA_F~EN~0bPgE94SNJOcC|isc-Ike<}TCk9Vt` z=uUm%14Sbbg6c-B)e8FeOvGOFeW?tQnEM}3`EGf7ULZS-&f0?M((m&jr$0Tx=->Ug zwPWLTv^dg!tH5|B;&64l1@Nb@UXm6nS2S=5@(&!}>BPJ1!y%bls!Htx9=c)Fu8AQ>VB?*j40-eJw(xp(Pb z-!{odxVpOBN0zZmnb2FI{7j1eW`?ID5ZHTOj#am3To~TdyA_el@UB z2~-Oryh~N}Nxz#MlgNu3t;}TTNZo7wBdM$P4)nAJ6><3M{JlwA2e&BC;VZ0{*NcO? zjkCxc$Y>CZjuzI#TNACnG&W@-h-tlE*cE<{h}b)C_7<-{q$b&bn#lf75iau%WVF!drcaa^L|XJTD> z0HU~jST)IGB4ph!p&R@-)lY`OQ0lW%87XyqlJ~6fAu|H1^bErA-r=po7U#Km1`UDQ z35mlpJ&&<>-|Bo%)67H(-NwjXd=GoYDnL#Rz;knIA)-c`_;eY?9j~eS9{YKgEw<&4KX!ZNSrC+5*cAA-;#%Q2iR!vYu?kTOm z3OxCrz%P;M#cC$b8xenE(L(t7EOJ-w7+4$X0?MEw${72POPE%hU>m}Pnw)N*-%TZ3 zL1!So6lycG{Ox0!UPiQQh@@=sMeNpte?4oIgmSFofE1yQoJZG#d-x^k5HKR1I@xS+ zZS4;(-7NiDS5znXI=umy5=h@W$=&1(@dWT5`G}c-Txl1?zRW1A1=6`_tOV9TQD{@ z&kjC~RJSd03{q=tY9;Gi=tk8<2=n_z!mSY8QE2e8UcDVja#xZdm^jz69&{KxkykaQ zZ@(TyQaTi7EQFaSc&codkM%M&;epqnc|DEk8M}9V$9A*8wC(M{e2R(s@#XKezb%9M z9rTMG!jH#FFb?XG>ivEcoe#P9?PLYz_R1~UKL_a<*rPoPByc8_dv7Y3l^?o&Ts=Ff zy4DG6=XJBgnw>Q@i2>!;xsbSKPxi^r-_-iuY}I^^Pqfg}3vI|0bSQ=I&7GbO#VZ|= zBDZhj9UgL;4t(u0WavF&17PnW(qO2YX2=cj%Tz6H`)BfL;vB#7wclW27(0SLkNPQj zj-t}ERN?ohWti^~(V*eS*SS6?nV1EO#*cO#hGp?=9ybYTl)fnW$b|8ZqW zby3(L85-ioJg_Yc##5*7D2wg`(%h)@lW|LE7g+XaHGcr?jJKF8;D!VqCL))8Z{>|W zA!H>oZ(>A69ll=I7;+0P1{uu7CB}Y?r zCv4iE(6l$SIOmxrucf-gDxyet{29+m6Umg<2JF;{fb39ayfWtIjmEvNivWjlhHJ2h z)1AQd@&OBc671*U_Ok-Nav2YLNTC~INy!9pO)m`=!}-7%Sb;c00mfM-hy#EWX*XP| ztuzGm0d%9QID-N$1v$L=}`B3tj<&#ghlk(XA+1 zI6c75!D9rbTP*gfeC0b_`vyAq6u&c%X520XY}P4eH({zrZlfQ#CNfhyy9sVc78#FB zNS&0ufc@0xs(W#Zq(GsG81gT2_FRi-MEi*d-J!p@Ts3Y6B%bOgynPOk4xR0Nd?C7iM10_pPfGpV-dJ~(Ej zWW66tdWT`xP8VlgYDyNfKuL&ej~4}z7m(1#-zdtyLNY_O1h?kta9++Jj{o9%>=`!H zbS0uD#xfrpM_>{?f#3+3gu zJk!>YXpq{$lhqb`0w*NnLU;CycjHASe4^^*<=p%Nn#lCqx6dR?-V=)#{H^ggLFZbU z^Me#S+eQ3W__a=Dgek%zx)0Ks?|dcA)SqHZQ_UN@t?gS_ z!rZ+86obMy^biydZfJ2jkM~a^RO+h^2A|=|IkD|tW9@bGlIYVx^TawXO1^O^j$S%J>(6IHV>lY1l}T>8n-D$D!$XxQuBWWQt_0`KWrC75Wj^ikl2!8bVF z$fn5n>N?ZjsiMb3giUiK5Ypt;9!gOSm_|s2dGCUIg#lcGPjh=fP8-jGND+nClG10U z{qE*9p=1SJmkG+a0n*&V+zn()k7|)|DQQ!cDM06;Dg3Vs!S=|Wkk!o0fSVVXM|J1m z8MD90q@DXV?m+GY@5LRboUHbPwZFl|^&!3qE=3j(5A$KhFn+D5ABPuiyok!L0ZF{a zDcWb}!rk;P>dxmYazkB=(|jI-*xjJI9cYSy6$%0GSDe4jZUTEE)mk4{jB$r@AfHZ@ z{NrMu&3C3u-^QGUF7^Lr*1U9F@jRgd_ZSOF_54{NpOxKWE?)ZPRc_z)VoAx2u=~lT z2o}vsqJ;%y_R#_OoVH{%mNu$vhi>}REyjvp03=}u(Lm}r&Ut2cWW);bEx%^=dZ}u^ znPYXWN1mc7BE!QRZjx6L?`O>EqwdpTX!}w(=;$hsIn%pOW>7eLNx3@>9?uu zr=brFWH=Uhz*2GXiq{`occ+@_NJ^bH;0NAt#xVqLod?Ul#~DEEZI%)Yq$I?hv)`pa z0NvX!HQN&DVk;S0Ex@?1JJCN*rH)xa40&hlMVMb!${ zCp|Gl2(B$Q*3f9<4~a(ZSfI|5e^Q-(PQ739OGiGvc^}H`X=fs%f9LzL>)Tm%_r^Wc zGZ@QM@komEnAkb$3gt^B6!iE202H+R1VrrrwE&1|_(WX>PK{mkg@mL56mWySwd_tR zo3(uck(=0z%F;M@zBp%Faki&}9jL}cLqyzTavXOqUhpM4pmr~ekmIjtD;BC*aR8VCv2H#oy3c?JS@ z4zS?rV2)WnRx$V8n2+n_tkUaLKTU4k%-32i`Tfz4@wnYgt#`OO5AwxpEjDtUo55-# zDPB5_h%&SYlAGGcUW2?re~k5^`gUmRPwzW|$LkjI-2`3%0UsaMzACK3{j$a7Ee1FZ zd@C0RP{vrvLnxw0Vw_8BKeR#%_8s`r3nx=6h~N^eEVIBGK!@u*;McIwi_X9@Wh!c7 zXnShaZgVdNSg8W*MqpT@VVP#V@T~3QZ?A)x?(Ry)yZ>fceT<>FY8Ky5XFzc7YftCI zt69>`{*R{f{%7lb|9B`h-^3`fM{R1W7%f#Z)QC}1rD{{PYtNc(Py|KLBGd@2QG2&h zD~h7_7JJrS-}Cw5d;Ws+IOjg+zV6rcdOa^*h3dt3B0q0OP1kwzfYf@EL58>Y5Q1sm z`g~>xY05Yus6C3wLM))C+5rl#TM^Vd6!FG3*e^^YZy9UdeS}!Q<3V5}E|Zm{Rs9lakK%uK;T~i3YA-1}ns}Gu zc@$mya#bnmnn%*U;|oz3)Jl7Sb5?07>~#mCPk}AQCQDP?oSY~EN|^j^40Fa}t8pLE zz~4gjdHAsc1I~YEV@YzoI@8E`aOkH~Wy@krhFqXxl(8K- z^(XF09s*-zR9ugv3PqMke8+!iBFB&3`N(Kg$S(^yA(LOSDQHPA9q(iQyj1V{?mYED zNT9ndjg-bGRfPdi)tH8aKuv||6Of>-T5l$AS)tR$L{kr|oh7;HdQpYXoHa7DFF|S0 zJ2Bpq=L^_!MZKniv;$`1Ad7x!ToSN@T_;kTGPMNT3Sy#PdG#`BFQSjG!ZTM$-igwu z*2VBR?0)F!{K=^1@2*hX_1n)a2s%Td-(5nMavs3GN=3Qh?ISFW37?r_Ty1p(rOlJu ztR*FJTCvGs$I3i8qf@kvU^WA5+x1R=mU$dsW&Pau5Xp4J{_|LGIhk$zUa{(|4}$G` zeWE}b(Fga&%WO4@3%FzZCS0G>5ziusqoiRJr0r;U9FYvuHJfna05P4w2T6JygLJ35 z8l^TvA&IOlFG_Kfn}MV=^G^ze{E*yRM^5wniK%lj6>`+=|@EOI_Z%)_F z%?o~^$LikaY%b8yu0g6LPNJ#097%;SIh!dRjzNins^&aWvnXDGeTZNZ%_6Q(#7W z>nVWLg&XL3fBHYnqu_y=!Q;^IJ)YBc52O@d6U#$RpGOx^v^FZ+P(N@HDyTex&G4Tl@_mkMKX@&as2h}*2ABSEiV|odWEGa z?TaKxDj@~F68lZ);$vhc>W|wfCLq@KI z*=+6Y(+)WOEq=L3{b-eFdZ|xN&Jz=A2)pA(NR=x(ZKgR~gO9H1G}ml>7`9&NH-c=Of_`Na0I69`(;#jGWim z%;r@H^kG15Wkp5viG6drVH47yt9hO}?(3MMYufUY>Up#%MuD)Bo{xU3yCPTBn2aAl zS8tNBkw_kHmETcw!4?{3jI6AW58_vwCxFS3360%S0-!|lpK{v5@1@{;Ajk(^FpRx2nE8iY#OV*Xtte+9ym;cz(qS!CTQLft$+OI~K-YMDa)U2l^ry{CAM z8?a7R1DGOL*lqif-_W-2Is(heTp;JpxfqlNh1oR2EE#AIe-FrDt((AdVr+!E>P#9? zZwu6tw=P0!5otfH>yB>op5@Z6F zEj2jZ56=#-cX0sA6raK^Z98)stzQ&bbr@NSt2WQN1wW;`U!Gti_bGe6iJW$tPeGA92- zW5!N?PlGEeEBgXoPOLn)w$8|~h#PRnt?m1Z(J6%FSZNEVWzH$oA!;!2Zbf7I^!IPx zIwi)oFh+8lHfA{nsi<5LZkEC>RwTkEti`!PPZutBL-S;gfsjkr+|9N`6c{L*FKI(h zADf5~ZeGfUxqa@I_Df59tRjhPf~ z9(WMGsGr_=9)IwL1)k& zg}Y}G%9sNqoR1DEaBQPCZOSb&()p*$Dqc5fr7Zd+p8ju zUWz#wl<6pHV}2&onrSj1w~xp}*i^LZ55(B?4v$^i9Br53c9r9cXww@R&b9fp?F}y{+Aih+8HRG#hj*A9gXWa=Nypl{7m!!*RWocXKvUnRlBo z^FWWlN+>xroIqNs!&7DD)1Qc-VNGFUDGgMU5J{yrbm2a(Eq{9oR zqa6k8u>%eBiR2M_y>LWpcMw(wpJ{mN__Xdv4)R-7kBEYT>jTU*qOaZzIO&$>DBM!_ z+O;Nr5owJ7Km_MgwcHctH+AM0wRKZy$s{x=AGi+uZLyey9Ij3Q%GT>IvKwg_KlX0m z!y6j^NQn^Z13A!Gr>xgbFHN>!o z%&hS}(%B!1-F5q3c93pk^qjkuQ@paJiqXAdmIL<-gk#XO8l4;af~{BvZWy=l_~s%!((kGv?xEkmnC8)>?QGS?FOMcGQ7Op z9i2Au_9padTPJZz;}p#ZiIzS zf$%s?QymALt+dVr&!dfT$yI3Yeyi4pDls=lVb>OTi;~i@J31434Dq22E>hjV;rr_S z-1R-|DOAsXtZN)s(>W>1@)QTv5OYkPy#O%uMLNP<23j)NQ^3_Ub?xPemry!A8w?Be z4zbN44Yz@yhT$%F3-TdS+VI)>PGTXcaH-XYU#|u4ZsppfFJ8H}^R_j~cg;H9THh43 zK-GbI&N}7VGJX|Bkz9w_)|EGD#G5M?l|MzPJ*Q}2zBF-NfBQIm?H1S$$v|u&fafDj zUx0%@nZSfw3>1=4Ua(Bhg)Ju}Dah50A_C`W0!s@qZs_ONIl1ljJU1*;W3P7nj@Lpd zRWF2y67;K{QDja_cC9%B0o2~G@={hFKH}R1sno^_SAJ4KuHe0$i{0k5_&kDc*(Tap zAc_|b+xP7^yzNfTLvpxbXN+t+{VA~;0HmOj(%@s060N5%*>4x3!1*S?@YUMf?%*qRs*`**1F3em$N5g*M+%-E&K69)}s zfZy*(rdE~2e6%EL@a&cR=+S6d^T|EGQd+f-+UVL+s-k7$8EQ}%1Dp$0T1sC0!>4V% z5?}#4eO~osdI`)Myp8te1_MTuZxC;CIw0+>YsM?M)#%bPQpuW9DY=d!O7H>h%PG%zp#xR}D`3_8!5B>)3M?X4)(0bu*uy%_>55*G&A z_zA?lkJURq53e_#-h8TQZEQqTjCFU-0Lh5|puq<{g5w6WpOYDOBkb_K|84>k_!S(5 zyxDp9l2P6wU;m6-oqJ{KSg3OoI!o;trFs1Gqzxz-?D@a&Y803cPYTZ-GSFVkNZefS z76$I+U0M}sSaz{>NWg>y)3%pi9o5HJ=f;NCv%Mv*BE8VF^$eEv z1}^U+)R08m=dLa-u)EX4k@?7_oJkj836#os|xp& z6CO=4YeRjA@9Do4ornIr2G{?%CcC)~woS+WH0U_rv0U~c%EYZn17pmWIu$B0{R+=E z3*kaMg(mWo=1PgiGc}zE#*encYDJ7f(T8U~Ezam_VayS1g?*_%+77QE;2)=Bb`to- zh7+VJA~$GU^iGD;_9e2XlwaGHPsomvrzl*Hn_DMS=8@F5m3$?R6Sb-pmv}Hs%BYJy z_Ex)d*6wS+5lQrBq?!kfvM77ZURmNi%!P}(haMk-KMg!DEwz5TZ1wbOKT-b5G#=F6SL zj!%hd64t`Gov;N!-NlfiPM?48O^OW!?Tz&2v42O$BGEK)6zG;irnxI)sen`Of7P^d z;Gr!0^jKRY*K99=*M{9O{LbpM> zr3+y(;N$h-%~f$z*ZNXY*xug4Gs5A@`bzUup4H5ZMV>kagUUE;7)voydx=7dGi`6r z?4%lm9t>%5u`a1xTvp$lxL-37y+;@SVFTR73R{UC#V}{e`Eog>$8_&at4?Ww*g0!@ znW^czCmI$p!7Cszx}uD*tp}&>(+y?D6Gz0O69usv&*VtRi6Tj0dh#q?(IB;A@3xB_ zjV5!w5rw<&Swi>UTjTFyd;6>3 ze_rWSqD2$HMb7!iV(KL*{5hQ(14SHQoC}xOJ@z%`(r@v8(_%H=+{qg9ZkDX%4mbDG zL;n)j-Md64RaIl$RrAkz?I1Br3u@sPUGbhJfT+Fi0rlCA1m-@z`z4-nIOVQFkIqe0 zN?CH50O(s(!fjba-R>w_3Yf$0kgvSZrEPO(c5Sizu1tu{WSg8|Y~Dzz zyC(TeYI=Aup{35!Nv$0+^5z}|_$5T^8OkaoYytgWLCE>mlqY(Y;>YdIgRSR-5D4ZvZ}WpdRG4Vjo?q_j8*6vWNwRI*5W2@3JSE0W=topYZpm5yfJ z8dl*DvZcbL#boDMwuKN9;cuKhL*2H~T(=vA%1QbE(;!x5VR(^apUBP0eo{`#6a21x zG^^4CG2$$Kbc(|UjU9BnnB06ap3bCM+)}9dphJuSF{SBB6X%Y(|64x#*2Y*mbAnV{ zlwm2-Ccp-%Bhv|~9&-q=L2Kx8s8;;P>q~+m5;>h|I>9Y@)*`n5D_%08&<<9KD@}7w zp^bSQDO+kHxMXLfuKh^`^0Z8DQ*!SXuD&c)sN$)pshee+%GQxSl`va^gY>{;g7&jp zG;8nKwx7wr=H%q~cixi!OMYzJI(QxPCTR8gZS2xe&FBUwrC_#%i^M{_viYRrcxlF) zjgWWt$6HD1Vk*fG{g}#5l3jsQ@e>l&gIC-mc|6bfnGS-dbKD5 z1y7YM1|=>gCYwE=Jowugn~;)1&l5#Ju1r&Pee~u@%R-JUc)UU`s})*&^Yi$-oyXKm zjT^ys2knOW`0mb=3mH@`UMG}MQN2fgu&T`Cqp*D?%_(Z?5XL5)2H@&Aq5*tMsTpg* zf|L912QQV3V~N(o7G~>!?Nrw~eKLl80hyfD)pIY%S>rF#!&GZ@(hq%cSTY zvOXr)c^?dAcgAvn3Db6pLz9YRu@|xZR7CN<+K@ULt&!zb?6ccnKQtoBDs!LJ(PR&} zU?&!**%Aa>Mr<%}t-_qlvyl$TOo!3HmZ=B)+EIH*UmTPt7}ql7FU@N3KxMD_H-sU^ zsFsP>V0e~~PwnVCQ85^^g~|-TiS

;Kyo0wm(L)R%OM7iLwT>2bN^7ta4Z>k4x!W(;;{=2z4rL%k7R<= zZ>qC7_u>4P+MoHIkp*}3ZExl8 zy?OJ-&ZczKrEbdo&oiy=mbXjBo8|=yLDL+XX+`bVms@%*GoA0O4{!at<1Z(d7G=1% z{xxi^{m9=aTrLNT`>Y1}LgF0mWv;F`DOWc`3T!QapCKcQO>;T?=d-u& zsB*YfJ!sVy`ds8`iF}%@PS*198wVzlqubD}Jept?@<=D;+(PFur_nAbei$u9i2aO` zesT!IxF9j8ryFu4->a*im5oAdPu;^#(gIsrzkhFT{4Se`RPn!oL1IKj%f|k8y|sm% zEiN96UGI#IeV2L@YBO}$Y1#X>pyCgX_}lH;A@uurb+%v(fD9Om5Vyn7JB&C>KdCY4-Z9j1<7T7Q0-ke%Sx}Zk!g2rN zhC=VDT~_c-tjfa&96*j?-_`v(q}RN1uFaP)JG4&$hL^}5sCkNiX$P9$Drc!G7H;+4 zg!>MJRpPn9>VIpeLNsa^FLmnRIR&L0{r74Xn;W$;wZLG9 zn|~c5S{BYirJsPm{i^m-WsG@Li^$Ov0DVep@ZDV1m1t2^l7G6aV3_ymsDAIk=fW&~ zfvAA(H*Qc?DYg4!@{KLIwJF{-QHg-wBD;T!iwK!&19ibfXHj$tX3oU7YUiIk|^P=o$}FDbH~wbVuwO z_m;!$MLZxe+xo5!MJ2UHA}1lae6~2DrE;Nzs&xnn5%0Q>Vj6z>qRn3rV5?~UAfKel7!;1$5m7AXh1$RH0kbkDWgQky! zJ;QKF&sNCemW(iPib(D!f}I6gGX-a7XFYa*w~v+Qie;D4Ue^Acb!I$25m-u1Y22`^ zeQeN0o%ROQC8y*EFlzY)oZKEButII*fWk%Dl>vFtX z1!n_Ja?;XjS87RNKRqku*`K_gX?&1dSQ)lGkU+(}Gt+dlU%QE{>Ud$eJj{^n`FoQ# zpIf;v!VB-Lq{xBHm(>wK)IG=>B$3&E)5U5mFnC7?@vHOj0iI+(_>)+keo|7BVLRa6;VifLkby0&)^k zsaI2FUuGd{*|K^zB*Yjj5fAFlf-ETkdUZAS4oLe1k9 z?BRD1qLGpV!)6z1Ms`|S7ilod%2A=ue3YufFNCyzQN2U+0DO>0uH9LCq@9H{D>Vm2 z^zI$lcvOan_j+dEquR~{?p$uwMA|n^Ee}7ufI=AIVXG?{Q!*SM=%8dcb3&XioJ|#k zjqqS4!I?YO=%-%g2G_~6eT7E(xxsJ|MLa5T>~=wRWi;H^)~WR4A& zGVghuXZY2bQmPG#_sUO-k#6=;Ci-1dV4BXS66rK zuhMRFTT`{){^E*6=$S5n%m1qaCGY&8_*docNb@!&IdLHgq!%G)$JbM>ro$0k+u>n} zz5m~N8g9#9@xca14dUnG;>)!&@Q)Wr+05Ipd!i{Jg;vA654TR7U5~riPMm8ebPsmc zOSJBdt|PgQ52zOvtI2JY{hPzQi|c%+UHue`0~W_cbjvbcPfxG!ypqB(F=r^|dN!Fajnqe2}=*mhXndG$43vUEMQOuOAx8#q%ab3=LPb5q3slyc}tt5 zpP1C77w1r&Ryc8#T}M2)+mFXM^Va{KRCMhC5iPbycq3T8I$?RD$OT9aiwcYwp9arK zc)Q%1qq=24E+T{>A#p?xb-sX{YGxy9kGHDxCa3>Gm6e+V4)5E(d>tw-ff^3<4Dgj? zw`}q(7zZ}t@>l3K4-b#}x;6u$3>XJjo(G=8KQ6{lwHV?VX*9^7&t_{=B-0m7V2^2a zmCZta^NM8BFam4Xb5@F0Pf^5+=8 z{!g?rW_qs0&)w1erXcCzYUh&LbfY&x*ZQnCT9a`mTfr`+Ood&|>_VK6jvOg~P$@Tx;Y*O5jC{W(%MKF4U-bAghh)Ho^BWPJD=1BoBi=Ofp6a zr+*Q`)0~iy;GA^_KG$&4&T|wz=Wc^Y9Wml;=3tX^oT& z#{e!D2@-Fba!+LXIn*Z(kr`M<=RZ5#iQc_Xxg1I&n!dcuCj==qU4;91dOF{HZE894 zkc3oJRN%$M*x5OEx!5TNy#vg&*FQMxh9f|P8nqa-H~!2YiY$Oq+ZN7yhOQ5vMYBk8 zCh2C!NUg4|eVK5H@KzP3eXLJzcsp=QjD5>5?Yov%3k(Aj4*Iv|nan&|zXOWoW~F{} z=uB+0a-0*S`gdWd2LOky)P)|8Ah5UbC2EQtN;!C{yo>3M0L7PBK&5FrUrkIVLImpa~8{W_=9oIS-wHV5^a|U z@AE?R9RpJMfP1yLucDIY$l{2UzKtU0g;JRy1yZ|{-UJv1)U;e2IJWs8O>Qfv+DAQS z)a0j%QD_d?)K9m+8tDPv`(8^A-`StyhZ@31#CgR1;u3jV+dI44_ar17M@o7Rp@|O$ zgANDeQ~4S^mo%|vZ2w~4cFdPqU%3>IC_H>9{w{xJ{oh3h?(g)pW!oH}I_Ou={0?{n z3

xI1uj;rTB&U+S~iK@J1FI%I~~-FuRGz%S{a^MryiJ*y0~av*7#t!5$;)g}=Pb z%IF;nQN_i@TMdNVCU1fj4^_$X^|j6X#rE;5EN0a2c7jfpkxzsHs2x%)2L<-;^l!fl zK>Dt3IRLav;+}y31HMJG;E~XOT`&Hs5IM5&Xx_5tzVvt#nn-tk9mMr;GVts7s>Xop zOFSOGu&>iHk(K!z3f-7#@D?n8lb(^j2($OwoiCh95H%HO4sGMoxFf#ilQrg2tENwG zL-L%3X_K8C_nucYO)H{1)y@*}0&sk|*$`FrOKKLXtE&>ny`Xmb50%4tjJ6FZN{E0~ z%(MjMfdxo}m5xB%5I@9RvuE{a*46w{cHYG$bA?sQL~f>YO%?&4LlsSa*WJU5H~41y z=OK-UG%G-3wD-YG2-SFzZ!+U$)Pgz9T>+PB(@ceFnGNK;)w*mH^dcxI2wICK z1~?`Z++XcC*D}20^C~y*_#$+E)ODc4>Cda05#>BR={%V>)`C>fCzPnF5&=}pOyc>2 zgGjRg(Zcz2Xya{0Wr#|~Z=HR>{5YFa+To5;juRy4A%)E4}#Z*jsg z@9@F?{-~=?iYy`rR(s}8n4e$%n^byusQbuVL2)COM#~d}gBqkLlfQHW2wPD`RzfiL z$FcLEav3w0oSt%-kt&?R@5qtap3nNys{x>5%jLElg?da`3`Gw(*fW?zC0qgkCF z4cifZNvkQv!fTtn5HFrW_VopHGk}vyA?w-)n&>SdR&;70U-E>GU z&fa88gO0(D$O;^)b40V3&wV~77B$HFU|6tNEX4FQQpc6WGa=9v=sVh2f(d#?pztwG za(u7krgU5Wgp*>s5cxY^d6=<>nP`j=zk>~<+_P=5K7o-MnGuRkrz}3KgJ9ZOOVbHW z(s%2t{*063MQ=3(7UNR<-+A}y8z%}Id`n9H~Z+@-xBQSNo0?Ymjmv<#~`tHohP^QjIbDH|IfPx)f(wz zrQdq_d451ep$dQJ8iFp4SAV|G-OW}m&ecjF!ao8sVlbUZ5b8i(Hq&l1TNKbvCGyVx z9X2%u;r_z+3*Wu8_f^PqP#8$*`PYg2nN%_JT6VHYiEN24Gc)_%ojlN3NvHFZUPB{S zqGScKIPt+`DOD1TNG;-M;=5edk8e9C7MPdAKGLlJ#dK*UasGGvEzm+73B9SAY6?Ds z17iFnym#{m=jm?f{+*om^>m;XONQ{_c}8nH3z2YycjA}#XgXeqhPsfiDkm9EK`{K+ z*w|Q=m~pQ(u3W14?dLsZHOpMVh+T>W4lb_$Cgy>|rt_Zym#sRbgmR&x)Cf}l-77zG z+fBA?e8UX@#T#)>0r8L4zeM}u-JbTqr>C6zWAuD$ zddoxFH>%cQ)w@K_@L3@qv5D@7^Lwj{knoe#z(K0QF8ueHTEU)c7rFC4qzKA`#?o3o z5rQPeBkJz%Zk{;AK+(5!Z_d+)`^q40q=oHAlvz(#mo14R1X*5-u6|u7N>Vvm3i4mL z)WjfKJde%+31(h$aHU5_{)_C6yF1$^I}gnZhc~y;j~?X^`-@bHtpsDLMiroCgLGnG z^_k6Y&^I{r@dfZ(1x~>D!(dz60;@{v2CtP)TwwJKO~E@<^!@u^?c8czO6q-gZPFc4 zlCb&3pHE(pP~J)mmnx5X&y$`~R(!a% z2J;hB>rDtzslmCp#iYi6d!51;_3r1&=(bkU_2uAAYHiFWH}w@a(%T+VkDq=uPZpUR zSb&E%`ChTwT2-4e=lT)w4}=fjGmAUf?mqbI^4+lj_T1vHN~p5_aCA(bgGS429FUaG zq}F^eM^(R9BNqkl(8#)j<|+7sK|8I>2tZ0qOM8AGYI4EhG(%QRZ?5L&UpP1vncyGp z_fNsAm1JZd3paRP(b2#0_vwc$S=w1J{d`n5LHvW+`m+DJ!wUS)QT$=5uv^Uo4r&SU z^4Z2Z(-Kp<61g{Tik9pmBGLShyHe#(;={l980Cpg8vdN8-cN(+$2Jat2Qc%Ufkh7IRS+;?FSvEZ?{g!rG;gkA3qDR8^SSd3j zH4aLxz4*QFKC(GF77^U^#3bwlZhpG_o3&)A)zeddSJzDdhw>z48ITJefy?urF{?q< z?aRNV8*Y&0f-To=nr4uT8{OmXBt|m~<&gh`Fl!{{+aKTrG2Tz4+y!l=Ke@7 zMX}CpZKbq#wdpcrPBj{goLn3jjZl9NLyEUQSJE6>5DB(C>R7WTua&1{WEU9{)`_CE zK_|PkIGr0G?n`nZ(L#=-bHbo?`KW7bZ1>nQ`ZwTq{UF1Kx-YV@xH$ZZ;MFwY@~89< z59A4WbCmpRe~kpWV6Gtay+A6(BVJ|C?%^M zJ01QSZJnpDr9E9e_~7w@4xD3eK@b-!ku2P%UxQBy(&j@(K}O`3)#o2&@@gf! z;rQePXi{3!MKa&dyICYyw*}pF(1z|+FL->sqxi+d(ScRo80%+{x;T}c2P2_ca79UMis5>ZuO z@3oVCvn6VT;l=d7QzqxdLm48$`Pc^v>qX9EuLpgyI!}W2g`KwPHiB}+q_5^Z`$j_BPKL|!E|$-aTx!Q$>bibsXJ(KF(NXO zr)EqXlJm$5VhCwTc!r>OC&M_!V&>Zju$quPC*0(X_n~F+NYDF7Pa@T4)zx$7c)uNh z-j;OJvzx0DcHJ90esj>R61>|YY6D^Yb7c0>z94fAAZ{hYRQuIsa~>cE}L=3^pZO=DhK=J8au;Na-&W273Zwd_4nF&b})Jf4R9$EM3WOEhl>gm5+?C5A6oTz3Vyqix5 za1N8)rWMas$xD#o95Pb0|K>Dszp=|`;AH5Lb^}aWRL-23ro`J`6hx=H$wbfFOItH` z|4H?NWVuOTi~8<_ll55dpw% zLJ*>-%%eiY(Q(AoM7`}1qZRDY;g9{{rgT~z#N1T?v-C(3i4iMArNQ`-i`(1VyEg;0 z=@}VqX9Mm#I~E^oPz-M6$SiTX8T_|M9 zVtQ)je|`3-O=TxfdgtxW9=_4u(aj^-CqTN-%u~N?^jBmA8Vrj9!pX5DHMKav$staK zHFcKDH$k@zVVL!Zoag?aZdd}oO016@)7{;T{075#d3jZR z{W>2o+Z9XuU~ma7&zdkCzlj!=_{;BDA{z!m?fCA6w9Pt&ixvJEn}Acpgofb6#KgV^ zFa^!O6U^GG>(dHOwTOcKHK5@w-@4m&w#ufZ)xFz%fnOLJ8XIH!{bghw*rQ29bM2$% zUGqYGZES2D2}5b3yE|K3PFQ*Q!H}y2c@oFstBUl1>WThC^u0J+5Kg#A2;_j; z<5{-B?25O>EU+BJ`9!ga+{ETT4p_eB3%$!<^HBtV3oUQ1&Vjw4sWET|07Dm|fI4Uf zI^|qlvkSE@l6f(F`O? zBN^s=edyOV#f7{%O}fbd6pDwlQw3R8Z2|8o!|VfWpqWnghy+A(ktcXv`C>gOjN1Bo zSa&ca!T7f7QoVk?pp0-nm|S?~k#;-6*cG4Q{&Ky8gwT$F#dQmD6T!s?8qwqtcU6U< zB(4!~&`a8;lKWzD(?2wgOeS~I)6@0!^EG^;@qQ9U!FwSDcBbeZAH_#t<=|7m62tp4-oV`4T&Sa6!wB`z;CGg! zrm!(Q?FUo1j7mU&Feypr2e4<1(ft^DkZRCs?Y*$^bfvqi0ki(f*YDd--@`Bm0CCAD zsHJW$R4FbWm|J%Q&|)(U!g>`>@M%dE%@<`7q5D+8H?)Wf*$tX&3OGbmP9@}fTupE6 zhAgpZMyVD;L>zngzU`-z{P9x#x*t+ZVi+!%pL)x(G!#w8UAD)-jP2)?5XBOUn zw!Lv#M}f;^pW`4xF(~e#h8nzSw%b?zRZ=3b(J4vAT@X<@shui?p_w#Q-}p8v@?c5r zx5Z{T|DVTdIkn;8QHpLKyg(^}BeEHumVkaynWt@mnwq1>OunwuWe6&1xbA0ik)YMOq~;EC6j9*2?|KnY`^LuRy1KIU zptnzAuF3BN=yRQAw4K{io)+I+8>n3L_Tf7KWWIrG?FONBFB&K(9G7&4clu|Wy|XWj z%!DNNmf}P$KGixs$#qEn1j5^j=`i4w9g*JL%Cwi{)SyVCUZ{o;2F;-Idp%CUW7*kH z4XVb0VqY7^>e7*eH}v8w#P;tqf$#lB6s&wO>J_<@%17}(T+uDn{{c{dI#JU|om^*x zF&SI~5SZk+%?lbnMSbP|S0Ho{k|%ka+>8y5&nY*B*;|)ABjQs{X(R1NchrM5)p_k;5JI8BEnnP_PJOH{})G6^4Ja;QBBj!pUM5H zLqhc6L1Fe)Er@610>c-+FNF{`_pTvQcox zs;#d2ken#DAvnM$G&I0@{rU<(?Cw}!U+>k1>_XI z2nrPv{PWLGvhuZb18ImArd-qQ7KRTkLi40O5+%)c_9;$kP@N}Le72o=IZ5TU(#dHs z44-F)Vk3P#T)Yof(qm^5-;g?~fP%v{L6YQ=fh}eNp?Wc?uzQ*d%p<}}(}-+)Qj8^x z!cc(sD>u0iMo>@?0FT)zqvEPkUYR$VfZCk~=vXi`?T8b;HpWOvz85S7YwVv%(09>6 zj#43vet^d0KKlTiZ zJ3eOmwCSTr12cRn$(bN9B1K@&;m7kt79=w0{N5IHt?JveufakJFs zOltK|=TiS!f*?vk01Bl-@bEC!%TdrTen_{n+N7p`hav4J`Y9OOe2GtGD`2egrOAaB za8Q-p2N=RpLe6CT1nj>{1A)|hq0&2f(lOtCtaxmryak}eNGPF3YCcXcrNiSid%t$Y zLM<0ef->WeDpE$H59$5j*EPXDcnjfgmJ4)3{P=J5FCPtg>gy8AgcFnDP_H$&Wo@P( z7yfcOV?Bf!$xk3*1btPK=ZFvuJa&XNw1WiGL zcCW9L2hxGQ)6?_}rq#_+kxxOJf+_*mX9VRWmEhBMUdu)g8z?*H*6zRnzJFjK5x3Og z(f;73a9p=Y*tXw^_(hxy3HF7$N2I=d=^aNo9!S(@_{P_%sfobg*KXK{N>o)KM{0{w7pU<==ByD`*ZWCf&24^rY2$5+=#bqj+DOSys0YFIrt$fX;wsfujf~Nc z3arKvz*DKL5Wi?0-#mIJ`>f4_CsUv11C}7&WJOsiQE%%WE3XC z4>6;RzNmj@29g|DIZupQ=I^GvT6@aypi-jkkm>Au^!~>v{C>@; zh$kOSJEWk-6o@M2)g*B63JH_ihO&dC3y~g=qPRP?_@XE~6{Hi9Jea5eZ5C;KVp392 z%l~v_$nD+c!($I<@L}B?gj#TBM+eLF%r&s(x_1~}VDuD`(7mKz2F%1b6cfD_tl5>^ z4Rs%Tm;WJP%tcO!UnAF`*ck6$u#lH1AR|+4dD?&?hGKqL^g+(mpk;j!MU{ULM_%eq zZpco_%8|E@V!)OR?$WuRj`~)T9hQk|oQbxfFwkUB4L~fulO9YQ7`YiO$OZ z-wTlO2LJm|SwEV=o+_3blusnY7CcW;Y585@u6|xfMPKJp&su-FmSQ3RUI^g{2ggF5 z6(B{!GCjqiq?^d^J+gi8EIqDkNX;V$deaKkGf~Lp;x@x$ooV;!-eRK^Sn>i3VR6b{LG z&=*K59F*`7jE4ZpYupFPE-31~Z2*hrV)?yzEnK7-B}MPF5sW=tZcLF1Zv?}@aS#he znDatBjPVZ4*;ekanzMyAOqd|HN8;&(;+eR=9uBRIro8nb72Zhm5@4wNe6CaWe$mis|rJI*?hTuiz!Mw9<9gT4#;vq zoa8e0sa5D%=Ft<_8t%P>gP`D}Y6=?A5ECT@F9Fl8m@2Y&mEx-gsfJ}RG#-rHii7nQ zpQ4LDmcp4%{V2><*Z&gM<#j!jR;E!d0zo*xViZw+H4_EVDp7b}nr}KKkvh@m3L#jv zG|0(SqgL^OLj0yL7Bvz>Rb#MUg=755LR4Z^Mr3=&LW5Mfs?XTV3x{Qup(*MAkf=4?}%uD560(_ob zI^AKyVD|hcfaPxchmpw1&Bb{J?+;^y-hGhDEV3lWnTun*z9X=AQl+3^&*gV&F5BZS z-Azqg%_p<&H^=TbTaGFhKcA^&XXmwD_RCZ@ACwXJ*8rTh@xIE@=sYQSd zds!a~$svq2(@-dBM=Tt68v1_#U_qb0SwxitL+8L4O_2OoM;D3vyGPdbM-aDd>-rYaJ|vk)>z$ytH|^*9)1RlyiL?H>R1$3J}Ol`qfF&Hv?hzWeaO z)~%bjj}K3>G!M8}>&!BX;DATfE5CX>8;$1H&oM%bENH#K0E)?^(rQJXcmLr-qtu&! z?;llajg$UhVR;1!r(VD+^>j4e-Q7Pp=}wZoD2z&{MoI)IH+aNh+4`qtkUe9BGAI*x z&X9SOl$gIV6aML)Q6vNjB~M$-gc2bLAY~jW(^-Z9hy)2egBhM(_z)>a#=u!yNTjp` zSrWpWb$ONz21BJ(k*3|lqcDoBaZW3Oh=AY#2nEi;I^^PP;Y8w;M&X_9e9n4v6rRGK z@Ci-P*|VejfBdh%kLAz`pj@4PO3*E-9fpHK6`Wk#v+pG zaCEePP^4+(2i?=tGByP{D>+L52uuKiEehv=cs_tC_s7mTspMojt=4MQTJ7-g&{&(M zsnV)euRB0w92hfNXVa-xnlXlD7#J5w85nma=y7L*dZQF!49Y%9R$JU zOP4=g#dS&qssd-u`ac-a`b=*uug*sDe-`gz!1X zCnYJArK2_JXfzrQk9*z8a9E_NHI_0;89OT@e)IlR2zT}f!04#gru!d6iJe186kv*fm1p8jo{mo7NWHde81i4jK||BihSWadxj!T({wVK zo*bVHhok9anobiV6=$3f<{Y@QMh%uEK?fy{l^~%6DY&9M7O3xi%Q;5~5^~lgCQE@(;(7UK*z4tuRx_x?2zb1= z#~5ob%pYt&T3A~{0LNH*+W+|GgGQrKtyHo+zj^a!yWQ4GX{`$>^E}r|edQ}(@gsjU z98c18X?ckd`io!wvR11R!t$b+nVH?#*worYfq&ZTm1N^E40#j-rD{0f&6Y`~`#U?$ zM$>Wf;K9RQuiI&NR@c@V&1P9o{AN)_keyAUexuOH)6@^b@pLlm_ZzL220NKdl+ukx zy#Q{v z5FTwmIvos*(Uj0yt#hjIlIHx>F(qFPr%D^Yb@v-n@VR{^vgTIVt70zx{2cRHM=Oqkr?S%1DDW z&9C3Map}@C%})F1=z?K+> zazfl_v_J^Z+G9sMvlar(4<5IS!wO(3ng3`b_s@s3|gj0T28&b-ibmkGBTt$D) zS49MbBFhM&Wl>^nIl%h$9zq+VmGXl?8%+t-S{FqDfDVc*%gSk5nx@v;JkOkSgwQAo z>-BmZMX9c7qm-0KM@OFL-MxG7(xppUYpEn>tk>%u9UuS0*I#Y5JKb(K%Zh0-U0Gf8 zgJ5re9~`MQ>RFoA>(#;qTI(#!{J?kCB9htJnX>d?VOS*TgM0UQAo9F0#x$GFG)*sF zyaRO9iqUa$LJptY{n8=NvEG)uB9%P3=% zQYo`i#m5CNFMNfRLWp*|V~rV4#+1@}v%S5&on@Kt`%=j?O*`#QwOWnixZF2;p0E}w zl`7>NoCQE$|$pCc*PEZ8 zi#ki3F{Kn^tjs32&QU_8l+IZx)o?f*j)qDqspRIy=8c;-&YwT;tc#*3ibBTt8KIl7 zGU2kUoB;uhG1h9Or7ZHiP)g;lDDt9Kt#K~$q9~)q{2=W2POY^wGqcW>fL*wK`*s*c zD=W))?%YAZX)^6}Iu|ZnoQ%hevy*OLO4;tTDIwi%w+zhs|0#Rd9^0}kJ7~Vv+K+Qi zMBKPHv$86)%kJuj>}qwpjfHL6vTV6+$ubCjhW`N)0{#HV0wHW65MKcKL;DM|5E3AS zFi&Gq#_o1icUASHGOIct_Z1Q6vG-nU%{lon_c;+avl^RF>gHA4c$~A>T64`g<``pc zx7+P@JJ0jo{hc=5?XI6ayGi{JLN~3o)rN?7cXw)%)&edjI|R-+S@3JoGVvpw(*Tlw`l( zZ?{{_-8hc!9(_~p`*J$P?197K5I5XUe)Ms)*>}6${r!E6k=^Zf*Vosab6iQK`DMGW z#@RWq%vnTmx7LWXHI@S>HGTi>xYMjZ~xLS zIl$eUH`Sr*y03lhYtuBndGqG^^XGSWcU{+w;~4mch|F`@U0u-!!MTdnR8 zgZ#9%l(Ss`6L%7E)j($gUNNnqze8bY3SD+0o2FV@68`-2&lxdMZmn&ucH_7SXa)0t zIoaVv0q-%F)7#t+u8PcUSvZNCyBiUqNKAK4!)VJwh~`eiP!ET7a*C;{5h4l$ckei_ zud_J(DQt(NZtjF@BT^Om0ZN0e0|%I95CI2ZvT1(#@{2cb-bAZvx7*&|-Ctc_f9qS{ z0&oR<^Y%?GWm!t3BTbu`bvZLjw6>nzKIg6n(-mF>a26=b!~H`DN3XB1sqc=5!;2U1 z%Y|tT9%Zqw+E#yjPqq>b&yhKNkl_(A!PRy$vAdhOqom|+#1fJzObJ?J9-!;E6l{%{ zW15xZfrvo0nIRF20+QCsiBv0`3Z?9X%*etYeEkC-2R^QtXj7AJXon+K#b(VgQ(Bfq zKt#f$SXnsUS2zVAlbeB=^c_T9Pzr4}a5^OsD}_V=i5O$_0ia%FBkP4#8w7!CMPUIL z0Uc@uR7lC)ahYAs%RG`1&|oaaD_eHfxFi7>7m` zh%Zm`u?)NHvnM$4<5na3YGK;UB?5jNHT-h0i9u0M?aU*h!2kabq^}gmmlq#tr<>=i znBs83Nvzfwg>-pSF--P6HHN;L07b6jPh8mX^4_1`r?7snT%gBjTCE^^tq!2I5@IM> zFOxWkIYs4jo;MJ>24JSAY75n`zA_I01TGQ6W0L37CG?eT4)E32jd&S82w%M$zVZ|< zk9)0czVdpAh*U4NADy4tmxd+$^OrX+t5qe2>!x>DFJ=dDbhn5gXATKcN@T`Nae*SD z+j*1-oXH7aS-BOdK0m7{2fq9@xS~&TS^vvey1=*A4MIe!LD^dWH7-b1H*@g7Ce4h` zr^%lT2YEa?=bpwR{C!!`bI}}mwX4Bd$o9w(zMP43(*EQ^arn}boF~()?>}A%5que` zfJ@Q`7dZW2WG%nCxRkz-A6+Y!Dmo$-7#>$RVQ+WgCbSEz=iMp zu4q3;MQ)Mae(6i_{J?~wm*UQ`^#YC{BBo3Xsxca3eFf%P7c)y;hvxAV0g(Zr)Br5` z^62AkfX8^Xf&&+vB))*oxBg>O3;)RAd^zY(R(Cy`|NKSx$~ky4#L5B8OfDGQlc)JF za&#Y|qjxtS0s#4!en`NLF)a(8zH)!}b^f#qB|_-=01%kLS7v5iJ|p0IhN9E~Fdm%i zImI$l1wNnJpzDAGoqs5=>u0T;r^&2Dc#Mq#VCR=hEN&Jc2_S)~nPO{CzOf?k`Nf@O zSMG7+u3Rc2QzbWu>NC*&ry(K2wi*y$U{X-~DUtcy@<1kVN1)4OjVJ5n|NPa(^JJ^X zjk|^}cLy^>e)kl{6KhZL19vCRPuClN+K&9vK8|bnr`}5LF7KR@^=F~ArfVES2N%S+ z{4Y&oSc;hwA!(4cY(?*2HJNj>l(VmH+3PKfYT6pEBYWM{!b}Pg18>vPNN^1{=$A+y zzIDOh1*Fz0OQgctjL$#Bw@aW}ux3#6stE zpP8e#x;_GtfFUoF$u6sy`!k5&)ZD-!r38fF)^wekisVS{p;N%%J+{Y6L<=BHOJio$ zhDad`h$)pJ%UF|ITF>cvFT|2X+IXg^(BmP-^Udn-MCC9(#dG>V2o(Kq2M^jYg4vpN zJ>DR3zQ3FYeD&`5sVUsMhm=E%6EpD;z-`@mQkN2&afKJO<)$l>Ht@nVM)>-+;+{~u zT!!mR&y^dF=fc!$u%$!*(5$s-;tVV zD4vN?D@(!L&4LbID_$mt1xtBN5Lar|aw%{TfSA`Q)n={MX`1GFe)jCy`k>E)2rs0` z%UOwUiSb>5h^4MG)iv+}xSDhwJmEt<77Oo0_%DT6gjT*BbibMg#2$6Lq^n404ImP< z+RO~wT0xgGr<6nzoRJ6oDWyW#P^h~r1Q?Z`&N>qzJg?JQr@NPVCL(Y`Yf)J}hQ!Np z(N&g-NSFjtg3}4kQ^F7w+KRwE33Ldm7_bQ-pd$ixJs@_>!C^C1%o*)Tgbc;NQxag* zoqF%CPzVIbN$lKg5u{vT>;J{y+1^1PF_{;0f!Poi!uD@G*r(OF5 zMUl%raUpH5kPWj38Mjuzi!x>X=^@RyeCEO|Tu0=JKoGrJn@=Zjj|ob#>%*NS&dOPR zOc#O8T#i9u2w0UxSj4p<01~Ncou-`5yudYbU~7u{$N)wFZ8hYYhzKNv4t04fzRw9Q zLa40`0I;E|Qp4CNL!XB(1LKMftN=Uk8m9aSst_C_0>lKe64;(r^JZ#~SxcP!xGCN} zyZoiI`7cs`2l~CLF>uY{O;yb#X4Qp+X1)1xc6V=0FCW&v+5rrPH(tIMk!eMA-OOV^ zan3Q)+f`4;V@wH-?^QE$kKc8952hLtj9BT~TC~~2Ij!Gp8EKi9rjJP@G9q9aM^JNZ zE0{ponms;EGgDp7g#zS@@DP;V&-gpD_bWKmPpOg4w_#v^?v7nT2LeF_xaRuxn!yBE*36c$-?(VW}q1n>5P8Sp98=kD!NAcg!i ze#LpUBr+{@;>q<)Gbva)>f$r-v_mhR3eX(=50X__eKAfb7f%Fww_ zaK1Po@~o~4{>i1u?akK&;%RPM>zZ~jgPSgGg|bBuahd0s-S|%Wo%oTf41|cPCS4Ci zP))TVAPvcuRnZj1E{ebz;a}OcrLL}br^6veo4b1{C1$|}rwh)L# zB4;KuWDzT+mJ;YsQNSvq$XF?Vv0>LAJoJgcqg40f(w@zZp< zgU;HYGst>jDV+r@!NoK)mJEm#pJDF%e%(ZJlI6`kV@#iaMH>y~ASGxR7=J0GUVUWrg^AtpC(;o>23JRZ$l=~4S~ zI?w!$4|kUoX__;yP6=Roh6C5UJ!*`#z3ArPOyVp1Hr9sB8&ViIIS~ROXSv*|5wu0p zWhikx{TNw%sbw#ktx{_a^RCv+Jms_&hiVx9i)${sD)P=5>6vL|z$j1JLGEUh5=F2H z(3>eL{#vW5hQ#!pwh90^a2EFqScZrMDS2&A+Cd0roN}0QtbV(}EIxC=0*@%iZ;BO;6u1J4keR#j!^%amtlEX%~qO+8X1HwRZDP7+JfYBNeXxDZjC%=0{s zqn^RuNbB?~HQ!-6#Vd(y%g<57Fm^gUris2bl7Pr-1LW8`F>sj)2t9=Pfy0ye&}SlI zA#JVIC3js6a*Zj>=Q7aF;35zjMvu~0*vQso{;@x1?x?yJ0_QwdTya%rBBzAR81M9H z(!SPo^oX9Rf+?QEzP18{k(GpHZ%T<%a@7lnM9i1VTS_U27_#&5%?eA!wFnNN$EumS zuQ#8Xb~(8jqO+`n=XaZth$5cHvFOL)QV*D^x8^Y$7M#EUAv1zCI?ueermpW`@$omo zn5GP&3UG%qHx%dCt>g%3wR*%DW=IK$Q;zou0ZL%7V^*~fBVxcfsq5ezhl3yDs-WuU zYu=w+)t4UJV+$6M5cP~02XO__OQ|Aq?(V_L`}c+Yit_ukOO##5It$P#< zQm7f$rVEpRVEEiiXosRPV;b#YN<;~Nwp#olAwcwX;VEvsebHF`@>lpyCC8)_Tf$OL?4Y$vYvpbm4(d|z|5Li zOb!7zaP+h5vJ#=0MqNa$A|YMfL|TOI;TVEB*Jbh5ZUI^;;ZoqLF>YM?-b-0Zi5!zs z0wO#|3pj%qYw!rHGy;RJh1t11i-^&7MN>65A|jF~ds!F&TPZ7;^Ca=4Ucdl}Ryz7w z)}XCwt<1t*7imh=i{P%y%sHWuNM4uL3~0rH(7*d%#AV>_%d$+<6n3Cb^lq(Vy;P-_ zn@n4aOIL(N((C)XF6X#TBA&Kdy5HQyoyM#o{;tT}qz zPb}p$OG>$q5XGey&WTbYLf{l$L#?*x!x2)1g_y9520#Lg3fSz+^_>u1yqN-vHaF51 zLcCb)85jZ5nU#P@+FEQoJh!|g=gWgx%br?e+2XJU@mQa92$`=S zK+I#`12eQ1l?k~6#U*M;6ej>mxfBQS(}PA(Ypus)QwR5(XV1==bwHYKcIg|g6h2J( zqO%1sFwim+Gcrqep5wBrbwzBRDJ4>kt3ClpCO2C_Z!^bGmErmJAwXp605L?);9#oF zodB5$NDQ$#lvY?33GvL2@B2RXN(5RWL}4>4s>Eanr+FEM;c3K5;)IeQAUl-C9NkWn9x`Q=Pa5rQIH@xIX5e5SzYb|sl0MWa{=+Aho z5hNpt#W-QAk9N(G6G?({z84}PsP&z0JQ6^oESX461&ukaJp!k-Pe5qRp2V$lqHgPK zuN7BFYdcvWP{T{T&m6xVV|9sAYrwR2^@+$m#Xr3ZnlSj9LZ3f#^{kKQ5|v3tvZfbS z*jc7S?s|Dy4s@DMkw51=TtdiKfZh3gJx6cU0`XpdETNfmERHraYpurFP}Sl~U2WRO z*Dt^5x-QHbqm2>b=yS%j23qD75_R_rdrWI}GZ8UUb_>p>sKB8f&FoDjPXmWr3C5Iypz%v6Aj7(~z20l24e2tA+FhX-Irk&8oz z#O{l56htQpvT?XAf+HeR&>SDz)G2YTd`98H9dZT$E+sClKEhNueZ<~3Yo&-Vu6Qvb zQX2ZTh65mGk1Oy?KzhXsE#aa+kBj_LN4l+-2NOX`%#1|wWf!l`&34PoOcd8al!X^~ z^(i+Im{(z3Y-=J?b)tZVmuZT99MD^5D=Xmwo77r2o9$}Nu`Gc{t_EgjuX_g&LW7jYhm4-X^1Jnj)5GO!Y26@~Ks^E^0Xy~a{ zs}&?6Cw$t`3{ZjFiZDb2+&uxhwv}pcmTRT)3@@T)001BWNklCp%olK?F{x)n%gV)_}nd?vOgj8Ps8#pdW)I1wasfkV$-* z5galD04xin1VDi7Al2Cr60B8R{B~81r_)*sR-mMmY@V0J-BL=3q}pn2UDutve&7Hw zzoRq>56}o&muz(AcUpvji-o}C(Ue0MFo#yo-Num9I}m^yXf+si@?YpHMZnB! zMa0H%zIP*uqgE>uAt%7Ki;8aEnwp8F#E4GO*(4OvWoflW&yP~>MPNEbJycbvXwM8w!Ryr>+4k0umy*5nvezD8v?gx%Uq_}&BkZXG?22gaLP#Z z`tIeLouW{?|%tF9jt5vAYo>QDRjBcUvniMlh>LoD!Kj zAP^WP=?Q}%7Pmv!bt^5)A;yp9F0Cm~c#t}PH%EU;HDz3Ba(MPPU`Afsau$tsuLiLO z=I-iEjd<;{umfi>v!ajIX+fCmJDnF_mLM=vO5x4Z)GlWgF^pvaAY8hfL6KQSKW6F7=6m_cSiIa8z20ii5}(B*_gt<`y1cH51rYEyS3bP?L$zwNp{rCdsxPJ2ds zb+{k8zRO7v01b(bhbi|1Np3)fEV&n)peeco$7r~)X-+`Q=wJ%ozzK!e0ZhT238Bwk zD>{%PnjduIxfrO#X*%XK5 z{CWrwfdM74J6Mfw4Zitb{{87R9Z$zDcel5I7HE~XXjb~|8l zgxvRDXICQ;rx*^1V5-$3Z(o)=O|!6^jOEZWxYjjHAz z0YID8R#g=Nm!+hXhJGMqV(vF1?yjKBU=Bji>T)`zt|Jsk8Bzv!Fj($B^)unY-L$Es zju63D$pwonwz|xPlLZs)_j@ARY&PazmxcOX*w6jPp!qRAR+1|3|)qtpw)6HhyC76-L$r5X1nX_yEkv{ z@ArM*x2oIS?$ygzo6VM)@2Yf5&` z0^HnOUk?M+hTvYCwyM)X@>xo;7`S&iL5%z(gM^?C^C|7F&Ik{BQ(TH9(dMl+aLk;b zAKLw!d0EVC7<#5o9g|?ri8`tK{oVcBP1{Uu5=p_p3YNyQlr>djS~;CKoF$a2VF9ho z9&>YIVo9g+aE2hr%*~)5HgWus2q*y{bO`{_ou7(GRaXcT9T9w4)m!0eba8vb zD7xn3s+=PN_f{*P31SyAiAr8=JkPC#P@SL6BOSnjS7bAsv6$05&&*j^5Z7*xW&VGWLxp1n6gaj9UU1-}p3WR{QTU+;^IN{sZucm1#r6NKMzqYXPKKH9R$+9egjF00; zgni%7%M^EWKt+T!mzj`~ByfzK5mp$rR_*e3x5J#nS-389e2XMG91hE}bU6nrVH`Ja z-@X-*tE;P0%1?gslg&8J%bauCjHB5w5>Bmkxs#MZ1Vtc;o{l&$s6lNw zW{xbH2^|QHbGmQ@B8+b~n~UVjOlzsbK%lEn6nX=wePZ_pT7d*O0kbMxcqz2%z}8K( zwmQyyJwJ1*-5N>^g?I2 zkb8v1`6AC$pzwiSyNzJ&EIN#ylHy9+1o3$;rEYgU1{n;XsRq*u!-PPj?T>l00cI`5 z3A?US)lAW(t7f)LQ!A%hi<#Zu->>~qqKAhEX5MVZyktBav}u3tYYxLeL@!>v2$(lI zhKP7`^L*HBxbHy~tO3FC?yl=Q=`t__7=WK1CS(F6Kt^Hagi^^(`VJTqc_;Gx=l&$z zy@i`wS1X78xVvu0lb6{6O%)JxO1?^25dhKA0l~l_uF`PPG}w+%>qV%JG3NE6x;JZ9 znJ{!(AdG;La0!pl6ja8NE?rPd9rW`aYKp+-e-BL8dtf|&oxfOSSvzw@yrj%7%@H$kr z)Uw&WICn}6+}s@*-Hn7v1We03b1iC;^;F=-^cvrlu{}6VgG;?yRLKh*6M0N!Nfw*8y<#^nQew(@TltX zxCeln=g;r&@4$WNdqQHSNKkW1f#~$Z0Pe_KOF=>+KJ6b$z8U&1C2?0F#GJgGP-HnB zOD(DE0+x{6A!i40py(H1ofA8>xg7Uy>Y(N*DK*ul*1qef{b3x&F*qA)FbowpK`mb8 z(m~ujJM7j{RQ0a3?Pk*teIQYIcHST&a45C3)<$+v4LKc{X>HthmqDrPO)4)C0MMd8 z3T~bl>VoK;5~6Dg&2;Td5HVm|5z+lW{98G9 ztvLes!-#-)cX#{!0}*z8s&#IyG)=1Q^~+a8wB2s+?(WPiXNkh8@B3w$m+H5-x1W6S zN!O)c`ITP=a6lV}zSPoc>4zcbeE-?!+v}UtGy^~bLgr>bOmi(Hve|BtK&F!_V#-sg zdE7X2KlBIy*sPXiI?VHF+g+7XBAz2+-w#z=cYXcxODW6Z-}=_KhGA$e zC{9w!l5@vrqfU2sR2qh|-)n2bb_-_T`|fwY`OR;B^2sM6^3g{hnOWMVQc7J46BEF4 znr?Pi4-XH&_k$nioPY7>ex5~s|A)VOeRK2d<_4H*DU`&_m?fe$i(t;s7O2%~MZ~zM z!J*7k&KaYeGkewFj9oYMJv!<q@P|I4Y32b4sZnoRAneCvDo5 zstDYVwV8B-A+d;A1#N@|1VFe}J2``?ACImYsv$3>ww7~-uABDDF!W)9yT&?qK#Dsp z`cd<|?>C!^VsRzG41?|Ww))}>4Qp8nAtn}UO>4{J7PX!3?$Nv-$F>x0Eu}Rw^X{PbGODW|zeeltTiFsa@ei%~Umt{Ha4<28GC?covsuM{$6QC~DYDKl2G6S|! z-7S$UTmmsnEC^m}Nhz9H;O^Q~wMm!VY!yNw3l8Ikv#RyO_}OQlZ8l?=X|0`2hMefx z`_J9s{$YQ6d%N519`;8f%qbB|x83@(ym)rg4ZXWfr(?GrdAosDur}t19*UeOWeZQ8 zm0!LZZv<~hvO^XIgxsa2f)G1Gs}$UZs2{4X_SPks-w4FutsPH}$RhK6tcUwyxIWy! z$vI0(S}PNUa@@_d)mE#jee%g4^nJhCZJDLDhT!8cB4O9{lG1!Sa^Fi5ttE+o`7)ir z0q);wYuZ$6E6dExOtO@PO;wecC8c2)h{@c?VQ_%c@i6?{w~w!0_Pd*__qV<**md0% zbvI?b@6AAMc%)AgNCtNZ&*xeN!J0avbRDOJ7>rM+loZJlAt0(sQXy<}H3xLUyZ`;) z+g@Gs^>a{PUcEjXPr-u<(vlYx5EYg@J0d z+7dlca0iGH44#WSD-nP`YW++>Rmplo(KknF8_ ztF;zMjAH^-R|O(J9_M)mBm=C~r0epq0p@Z#P18(uHnXnl0PT1@)>`|aV`gpb(@#JB z@lSqyJRbk_ul$+aZcB`+dYTT`SJ&>!Onu+G!Rd5ts@P^E&coQ6<$eQ5OHp($rPf*# z^Dy-H$2a3JhP!3}VO`g4HX9`5et@CZ{hlSSU{)z!N)S-Z41GEG_z!dCYbZgC2S=ehcYeHm*O*MC& zw+cw8zx|)VX1lz)1B3qg3z!!ta>MVdt91M^J0L<)0F$Mw$=<qC z{l?8uNV$7Jm22l#&bu+QVJ7Ii}hWKvhDui?a<}xo2%(`B18Zd=IyvWO>g^Q zyt#R%&4^R2I*was(WdpZG}V+6r117^DWz_=bz)x@P^FwrwUp9tHaZEv!HcOYzPZ8l~;JUk%LIF13l#L$AAb6nv|f}tNC?(fUIbX`6jPG&lEy@+ti zruy*qjcjgWa7Wj5{Wy+SyLvov7H0Of)q^u#fmz;dA6~tB`}XaJ-})AoB3*CMEJ0kB z<@&6b@k@(>SKbdGx@pr!Ae;pxt+p1{Aa_1ZVCu~6bb5IE=J^L-w^D!z?R)y;IB&kZD=oym>p9Dk;C%@_v6POAY^McfY&4 zi!APu-VC|R(j{@MrOdS~@4x>(I+%LyH|2CnH_tsS?d~CSg1!gy!`r*WnTS&EA$6dj z%fh4ESy&NThqrIvo=&F#0!8F&U;7$&8ERe0!y=f{+m|mdvO5t;!*F|n>s1@ASb#(^`g}b|%6B3bCBZ@vmMF-AorBX^3 z*-76+Yb&4G49jo*9hQWtN9k5}%ju-8nzdTzQfCp`Zm)e=$5NYvT5Ia&22NF5bQ+#* z`p_Jl^B0@uIY~BEBT-Jd#cM)hk(_%VR<)ZK8}P-Wk8iiTX*zxK#i#GT|Gt?G!`P)nOvF5$CNmv}F(x>I zvYM7sB&U8D9Gr>F9abZKE42>KZq_Rx@Tp;dQgohyh|gB2)&vnky-z7o-{IMs%*;@j zQ_{Jvf;mKRGi}6Nrjym$k0THs@9$o|d>ziPc=*nL=mvQZB6~C55Sa7IZG>x&GR@6ZU6)h@ej0#?+SJ`slHB!3XsQo)bFj{cNmydsNoe4V}jU0Q7K($9>9F4u;?-^^eFs`wt=3v;#>~uw`~UktP_-o8 zFl2~85Lkc}`fM~eoU zg1jS>U5|p>>bnX6eFsx}^clSP35FPC)wuvd7lh|tHkb7oVd@nde$72@}!bbjmq*T?Z5-ddmPTaylLXVBBu4HQH=oo-gjE z#01Yjg#F%@f{8=D@8~FJo%q_x@a1@HXIJt0bh^L4fByVtGmNDaMDMrTn2*$2L;b;j zj!1|CC}$Mg4*NXjz1SvyYHce%@n2pE9`Ly!ipFdcnf%o^s-R7+{CwU`Sw3>yGE z9uCtqkHaR9gSvQ`l%0nx1Z|ns$*KSDZ~gFhfA7c3;sCPn^w^x83 zhEhz8L=1ow0l-_uVQ_7L1lk-h=o1JIMBHl72W(k_?>)~`UJKK;qhFVrQYvN9rYUD6 z4EuIfn@`8Gl&;I;?Th;_ULKBzaTs@3SCpiw%2~0fW)8qsOD)XQjT=Y_v_e@<`#pg7 zLr;<*$;xG@O^6VJZBz`JMkTeox&mRa;>I}k=?I)NivSpu$?J?spvHdBgx1YcK;=AAb&Fg2oU;fj-a<||A!N))O{PVkR zc=pMUKl_DW{Py?0`-6Y*kN?-rZu{59zm%YLl0v(d-LaKsW`xA7Q_VR!Vyj00NGZ2g zxv3-p5pTNe_c-jrlah!;5;Do=8d^0oNdpk78V3mm6Cebn)k>Az8Hu?fQd<^skp)#tlEUro^fh1^Uv)w_GRXYR#%sCC9+NExlbjAQMCT8Ar{iqGQ z9#nVFZ^!4i9Dsv)?gvoQWo}j1#!xS!8NdLn9rw#TwYp^Hq3@fv)8W9vt(2fKNl707 zI6F|sF3nBLvh+zXF<6hTpq_UH$^^~8p)SjGIvpM!hJJ9fI#&lllzGlz_TGE9Ksan@ zd{6eTz9{GmT7Ua&7{_@jo?hI2arlSd`|)4>^I+FpThm&F zb}+Y9S04zX8FW2}U~ucTQwH$(Sf7`ogc;%2)O5p<{i#%smx0A+EjraIg_ z$FAF$=~9%q-`%|V^zfq}eR4QVFJ8R&cmDQ&@(aK4i!Wck{)>P4*I&MT{q+xj=GB|m zpS^nX3tt)v-UaQAwb2Lx~7A*^pBe?tNRBKJbtGkK;Fo9%rc=5-7&W?Lx zgxYd9;AZQG2 zS_m;oa+n!chFI=~wAo?wGsFDN+)k%I_`M%8!8nfCsu0B6F0~;`Y5w-^VVWo7+kfRn;*hb?*H&7?V;Vic#e{yZt~V@t&Z4X>NfrH{$2o$ zKph%7LI~N*f^LEc%`iX+eg>RHJH2&&~Y5vZH%GS#|dmcU?_rBUir>iTZb zhJX0x!PGzc=$rrUzy0t0D}UkF4#)Y|{?ecRo$vqd+lQm1{`WumL+OUw?O3PN^=3m> zzxc_=nORNeki-L~+~wRM)&MSsVMrpF5a-efsRqlU`xEzrH?vj&sjxjaSC*wL%Y3X0wt1fR52tawTCK+# zYxBSVU;j7%`oHn7efKxN$GN+`eeX|x`EQ<&BMI>OIn=Q*ct z(|1IvRZqhJp0EIr07|#pfrFP?m&FlN$`5buqTcEI!A%LFzrKcAR|uLprnGR^ZMIt= z@={tUUP|tV7yYn~k#I}lL_G{+&})XFyn1P-^>lQFWtrPD1K{;;2dRhJ?05tO&K?uI z{Om%@BH#pQ=4P|DrknAq)aq!$420SmI3XvrN+^`5?-A>Rd%Jq?xvV7wop~pqa28aB zSu7BP!g4xj)7)jrnTOF!QPpK$h)KF`b9=krzqF>UR0H2`HYs)QzxQ>->?q^r>Ysi0 zxBu?n{f%#b`&WMTPyOoO{G0#gU-|W4yScfMl$z;hpMLfi{_?l~yZ`BbW+ImSPrvh> zU;5==9=dco?X6W%KF$*n33Ea=bS?F?ysfq7-0!yA`-cY+nWy<1-}uHjjv_hd?q+w@ zY#jUHa6D95`sZ6$&$$D4bmJip001BWNklVEIh{^@t1&^E_lX3?JZNi9KK6Yp zHE%ZaeqY*Mmy$?wGoMc&Oc!TL6z(%jlUplgDa+F3Zh2U`n;Vd%(`2=hWILXy>us)g z$2(#|B&_r6*RS6D=pzAiKmb$FRp0>bZJtld9JEM47-iF<-ZDU79tQVD%;WZPR!{2s zZkw_QAYoH&)|xTpSFi7X_xFGNTmS5PoaJx)jeqrj_#glKepo*I@S~fXYjFSlAO2oH zbgy2${QmF!cJ8;e%pZU9$)El6e@+mV<&-4#UBBDyYHLO9a5yUX&CQFS`Dpj>a4>Ub z-i#ZOba!{R%(W~JS@#^YIL|Uzf zTB9;S#Hmd8_xI0l-wRKm`}@6!+}zyEr|E8<-Tl26FZTOSuCK2kC1Q4f>25N!?XzdN z-SkcRngJ1NhUU-;tFt34)mxh$UiJNOy}QZmu+HsGw-OXkX zoJ2~LL`jydfp#PZFdzg15+pz#Y(EHoa1y|8Nq|g9f;?GB>_~Qy7=j!K8cd6_*lab~ zO|j>$dAPUkc;>UGHLpDEQ`Id}2?87xF6!Z2+_TSKd#&&Lf4|>g%>m1@judjfY=rWZ zlF{SoY<_2Ly*C*47K=HhD2PZ|EVR~?5*`FbYvus+&<7E)08@rN;6?xqNXFH2&WqC2 zwQg)#mc??Zlp>UdVc2#@s;Xj)t*x!4X-cfo+8~5Dh%j0Ic~$~<`MfCUj{Y-KpI-Z@Kx0O6!-`b z=;(02m`~1cum9<{zSU1cWF2st_43hd7H|^Aq4G{BLkXT1#dJEKEarz!_V@X4d?osA?( zmQ|^g@&LUcM??j&$J`53)wLYWX2|ImuRUjkDW|iL($jJthhY@Qagwy%%$A-JMNt$* zS(XVYW15hr0YE_z7^91#G@bW9oA29)Q6owT^&v%#qNv>}L9EoCMeV(6ui9IQR*6x& z1g#XUStIryQ7cM_RiXB%y(yl2e|YjAB)MPLeV*6(K92(|4S9p1Aoh}C!Mp&$3h9>9 zmbozqlzNUG+I0*}0jqXx5}*Cb|6D`Ht%sYS6Q_)Beo|4*23&@VC5gR3Q`q*Va{uCg zLqqXfGZqvBCPyxd$8uzk>e4fYDCu4I+`9M&ago}_0|ID(=XVXa~S)a_or&jJnm--1N6SNlmo6+uhA(nh<$a(vZsso4>Bb?UM5kNx&f0bh}b zMf2tQLh!SJX6T%b)Bd5aEYU`wuZCDZQ2CGdue4cG9|q;`HT9moXtNINCLNTPHkszM z0AydP@v7)sX^su^J7P}vFbVuDKIkMPEAX@)VF3SAXhzWgFlO*Ee-M>Ov|yNYsE0uE z=0odlHx_?|>Wckt47u=?tcuu;Wf}NEQS5_GR~|bp-HiXVf$$|De4(#vKTV-Q6{u=L zfmD?4;g;&V+jbvXKS5LE0EF3s@GXJz)ID7`8|7XL6!x>*Cl&m&1I%z2mIeX<#-5l< z@#8A zab!$PGav0)vUKnsCa-0Atef*GtEW}d;qToG3(Zg5A1ejeUb4M}exG=v8_!VStkF~$w zug=;(XvX>69uf!Lqbp|BTav9Y!&>d;qD>R{U!+GJvDqu^Gtdz$e8-|?5-1gKy7q`v z7V{sKzbsqqF0=9~V?A7R$)qqt7rVKXfvR49So~-UVujL3-NA(XI4X3=(_vI?ri-H> z?c6|v*;T4@Mq>ELDWX38_m10A#yaG)_yBRH8Gk0aJsT-bUKXX#wG~RsmdDCYpaZC0 zT@^~;F9e6Mf&`@#BfjHy<9s7s|8@BSe)b|Xa!*o6r`=6V%&{V|vUF`@K(D$SMS`J^ zZo+i}3UXaQC6LYM_g*-ez#<#s+2))Wn!`AAQ$FO^ z-n~MPhM+tf@Tlr`YRvO`tY54=Bi5x)4K=7Zzn20uDOe64fd*Q z@XLV2swUHV`qK{hO-zlCtau-Pj=Y{~4_-ZNis`47lx$eTCci$S7rpv(#m;RimnYPA zrl_zS6?t(oB}`a233z7LB`pARHeQS9SfJmT9q8u(bcfrpPY^rAaxt{Ss%> z09|pw&Q}b|*^V}FPi83J8mrP~&$s-MWcFQoDY<;6zJY`De|-srd-`8|@kQf=o~e;9 z{vzxTV(3tOsHU`ZR$MW2eO~81MHlY1e7v2)SH3MrJ-1~Rm)3roxRlGW`EB_BY)l>%s*Knlb~s7@g;#b@fe-#CO&PFS)fluDx#4^$Q*E=zK43 z_WKhiIYP_3CDv;X2l zjYd3Vd-(7nB`4c&c{OaE$Lo9Vw~Z?;nta!FER9TYKA6OQd(6d-uADb!XLr}C;|4$9c=(a1Mm=taz`0*Q^+AldHgEa%qvTVwRrhPuq;~@CkQc`N0I$-gL z9+o5Be-hKwP<)B&*9Op`Q7s!r9XuH|XkW}S7!8Xn=8uo=vjuwZ0nuOp*Y9WxfJfRK z&WwMu#(a`1-DX{h#SLH2CPusvp)-{PH_A1ml z38hI1_QASdrX#!>>v+)C(0+S!6KIU1GkXlpx~tir=nC)B>6 z03?Nm_KB&5gX3}BoX)G|j~l7ria4KIsoj&j+C~>v4qzhi9&OBWOuxCzx(kD81<>)Q z4D3hw6E@Nc-IgLX*E&9-*#2bpAv2=r zC<@b@%6spf9FZ_;DT;mG{{Gqln4JUMbdNT;^DEE=aXN(+974p@Vh6(H0vVm6~3-nwTkD-B>Vg&bvxsaVCD|dMo-k29Q9zq%tWuc-(U)6p$IWXdj_<6 z!Z-J4|DnVYzR*u+09z>N&frV84o{>O{sq;Z9}V5_$F^N&pGnJI@?<|hPYIHi_Q%E7 z$HIMZd$B&$6{j_Mp+>RBcM+Kg0KCb<%uNUyuV%TXnzy&@JFo%_{;ek&2VOC zwg*GEkULfkqX)VyzMx~mml&UYAD!iol{zC7mZl7f4A7DO_Y__o*93V@GLVE&Q}7rX z`k*h{+EK3|%*&A7laSo4~mSdZvTn=>)yrdaFKC>Qk>EywD0}w_aR-}YVK03Cm9YaZHjW5*4F|&nyv>{ zW{&@U_d5THV1BhAtcI9YbUBI$NQUkV=_CaNulb`V?9rXsxKJv8vY#^iiRlRXy?H1Q=M9bs6*$icpoGu@gnX2j*a9JfW-H$+e210oP0+# z|DaSbrV6C?pOpk`+q`JYHPnZZlLu@>$dY<6-2BQ)lMX9zzy?wNXWM%BZ^bkgKtx6f zrV?nqXW{{%&6k~T?PqI6r9N_pEB-WqBaE_vmcrYG3V>Gs+6!*XJAdp;=mUE*mc1{f zrHoik*gjh{2Q|o5UibK7UI-0{dPrDXtxa36vL-xm`|@pJJJBv0KttTlBcX=@?W^K0 zi2y~##n|(Yp-hPD^8Jy}$gq&h{hMoAxpSK)6K)q~7;$!;VJJ+`Awzhm=DRD}#4vNY z_9IF{8{3617Hj4koN-6Pf-)??oLg6l!~pA=o@`bDEx$` ztEBk_=^zMB*O$#fi$|&HD*+x46W0@Aq!~#UubpBogk0|ul12gTe1I>gANNTX+X6PK zf{5mH^`*AIx+BCGxZ+cQiHwQ#sq|VYSgPC64@eb2#hFkzVP>yIG8oOl zP@`zw6yexzaeR5*XUC>Y2PV_GS-+j}x;_*Mzmvi+#6M1A(BJ*d!$|(9{a>V|rG37S zA%HV`d^0HQY}b+p0bE*K{G2T^E`F~k&fs!mjLTG#g(X*=e#-D?P zDV`q1|EG3_py)79X4{p7`ZwnV6mn~)2@WAe#c|KIxr!#txL|4AWyPv`tm#9J9R=4D zx;MQEB9O?0nt_KPhG9roZpVvO#w$ zococ6vBaDT44pTIsT}3Mt46amg$SJfeGDp*YtXpS7e+KYChy?yQI}cx5gM`%cGG^H zWYa6^5{&irpnROo0XGpyuPSY>WZ@)LnE-JWi4Lv@7o!xXBtjL_)m(ysTuP#cq@PX{ z|4FtVpw@-G{{0<|OzdNDSpDi=Qe(z-nk?h2ClDQ8pAdk#PXgz(uM>j<-|r9HTnFNV z`(0}5+u6MHdkI~-O0HcKL|R%}PR?rB$)^Upxgto-!s%3dQS%LroY{1)vtYz?vBkQi zy53O)I+zYhO_@#k8yaSyXgGi1o)iJmEMF$k7Omd9CsWolb903gL&fn1jAYcd?>p~D zf9*2O`N5%eL^JqiFY4u%o(y8ok|kD!IO>j9LHj?03F1Sc4}fQ0PhiHLsi$%K#%*?h z&QQ0Por>mq!g+yuKocYYEhNOCHKgi=@WpKuyVSiRUia9^qFX|w=>uVpXO(T$`0dl5 zGMlVt=pW7Kv)q6G!PnDNP^>rok<-KGc1g7w&-{v!;*9Dzz4$f>yLgL_{acceM^lko zW!9~c>oLYw;T0V%+p(fheRM%_=Rj#WAS&PRQNd1W9Xa~P8Cpuf5oO}s;GmdI#`#z& zbhI@PB*R&fb}-}YkoWGFxAb69yxMm-0S9s-?uNP_`MTgZqZU* zU%Ay>`88GJdq6WfW)(q5pw9nV$%Cjrf=2oD^2gh$#L3LJoy%VdIQo!Wq8QPHg|jbyBp+z-fdM zaef7s3O*?kFZ%6lz%azkiZfIcRS~h8rhYJopZz(0em?NFArr_KUz(W6sZk?NP+$Kx zvAz{M!^ z3sS(??UMq7NO6ill1DllrJ4B6epNTG>%QED3rs-)BL59KMpO;!SCn|<5lbpmS(ePn7Wevo z?45=n2(HO>i28ku2^UY56YNbl$s)1M-6)|#wr9Hh-K@X^BDVBE8U)o6h%dJI0L`J& zbw_Qpi*t~O)x;_N*C0JQ2y}$Bq!zhMqQP{xdSoN8bn-DA>*&AxY$kX+W+)R+E_5$(55#g_!&suTsD$3iE$(F&Y z*-xG2YU3b6i*Tdh7aDrj6+_W+-Erf~RzoP3c2oS&Im%r}#9yp*G0v>E@p&jCeuXG8 zP6W|(#yWE+ic}Li>x(szWhTKm@{2wi(umj-!4pEn?I$-v%KWbp>v=y=G_OvEQSo=| z>u@;c_jWK@BJX#my?1n$`8t2zhXcu-9Q1w?G9D{!cN{WQFfIyoMbn`wd}PiKX$jUh z6=zOVinb{#FBnc|-`e-av6tYFrek}jm}#7_e#n9EEvI?ARKgo@~8V1bJ*r$1A9Kmv=7 zqK}V`BE-WXpBbDG^-(SL&Vy2J+igPf)eq&0)k2BTF?{MVqU6Jc;;VYmK^twVQE!1l zgAy-?@9cIl4LLHz;y`Y5mY|?LE&^$ih(~t>2nr&?3^mtlNF-)sie-C=A;}~PR?`2G z*15tMq2{8qBTPemZw5L=G839hYXv3I9Psd55zW*-JD(5tYG;r51BglyRP;$`H|d}ZXf+a9SwERN8Fk<1il0GLuneXzoiaFrKQD9vdUU> zX@mVXEpj|@4R2~+aYnAF2_&Pg)b0%RCd%KSi3*Q*IgUy(U@Vcc*&3V$e^%d`5JQtv zbZr^jCjr%J8!#x(DfEG~c`+uFPt_79F??D8j>V}o?7rIT;lQZ` zN3~8RUd|g1q|}$jXCn3#o+gV2rz&2ao^STP>O%p3 z!A{9~rH&6(-(trfC<7>?6`t(S?FL*WzQS>$)jAvX^(FjSDAWo+3&sm-t{u&chFv>Z zhkoy9LWQ4P`p%iNq|+(XqreX)!6kDXVT*V60~|x6GRm3L57>ymM17D*8Y7|GYO|jC zAoXJ3o#c9^N#5*%mMHl?#zd=2+AZ;P^!IX7q0w&T*III0f{x*0+PSY%*$RC8Drur! zva8Wwu&376dRXBuZ>+wnTyByI(yqxJ)(PmdRpXm9Q+JF74y``Lq-I&){NBwNBBB8V zsIUl8@^HtuevPy9UW5~Ei}IX=U9JoegSmwg`v2 zBgmM*F^@CD`Hz5}s}!5Y1J%a-nu8H48l@a7;qPE6FyQgt?M~z#hPNPn_0e6hi8e8_ z9*@sp_e;PeKs|=Y7B7B+MyoKuczJpCCGnhLUN;riChVsy zda;W8;|Y?RpWS%xYkjCt5nm)CvWlq{KzuywY@AeizyhNSSnB_~D4+T8-2Hv25E+<4 z;IGRzPr#zxgv!1#EqQI}J=v=wB86zfst`jciNRF1*l@P-U9sZacT3-Lv}F%#4;uOI z!W95?LtSV_v3sYCz3=B_+2gyiOiq>8?r&YveOfs1I}Z1Crd{DBL&5j zzXhT!;JQhw$l42c>Az_&J4*^Ws%U$u`=PxQ(6?5^z!RhbT$zuIt1#> z@7Fnl$2W=Ib*hyL5@$;;Fda4baa3Ve0+NecZ1)=u3;ll4gb$!ZQv@7R@b7(mN8A}Mw*+8e&_t~r8K>q?yXAv=FCimt|!4I7gufLX747Q&miZT6AvuUSR|i?d0c3T%%8^;w(8T_O4hk?>Q(`#ktxd#Od@D6S;YE61i{Q zkpXj$2$+~>-MGju68lD;LIFe6qM4JV3s5)V8rIjxPlG=%8Kg0C?0c}EY}T~ADOTEn zY`GG9J&0Z`FiL}vr2M3Z({;0%9Rh*yis#utS>k-2>$VI027wpJv>dmBk1~SFpXm zrHqp-(JLpB)^j_M%Elpj?ew%bWAIyXS`p|fVI9L{I0GWP5!>voH>*=j zs>wB0@m*0Fi*xx*pbAQzh`(DTApvjm&KBv!Mq9aw8XXNFW$^k(eB;a!`2o9TZ8No@ zXPa2lJ6{S0x6BjMeTiOX6^Xrud~yK%=SWBT#q7V_u!cEe1^p+4s}%|A=o+8vh(J@p zS-Q7^npp8Xt$sZQ6+Q6+j8m;oWVn5}eN148KtoA-N-;+)Es4AcL6b3CWw#YdIX&s^gZB ze)yQDNQ;=#08*2_Ffi1Y_<6-0Uw))*$$~t2a&8Mo`p}E1u;|@66f(eb?R8eCQ1Uzs z>Qv)HnF!(}B}_UWtfu>}+tgdwbr(TzulD}2C$mE$R?Flc*llHWD|7~vlJfPJweBRh zvVN}b*&uX4kQxljdWAJ5#ViQ)D9U@N&nIf&uE|@vZoscC>NT+-(*0v=Jr4UkU@4!U zrFjt}FYnMnP*_-4U5E9^`lOv=N*=r)v%z@U1J2bhK^%$R?FRQUyx05LqoyI2I|6co z#{IFesTfIZ6pVLmw_$it6gp2+^3`;hW*6UdHlP~lxe&ZP7bzjfvg5uZ?e>r(=e2D^ zR-c}i=!99sKEA~(e6J#+Bl2S05$bG!Q_snCoOuwi4A#OrgDto~_uls?c8r3`N~N{w zHNF`&V<}aU18+=IA`q_piAH!=FZeT8sREI;+EPI}g-j>&T=OUw;5$NSz`qFPl3pj2 z@30E40R%ge*$}PF0-5YB1UO?EHHd5!OaLDPFIQ{WsBvt+Th8+2FC=|f=ff@>l>l$> zd@+bX9M_gC7-riCQjOqARUw)V-GhMtiq@4$J<8Vaz2h|>zX+HPC5<}JVCr}pCN+2X zeGJP)^hhnkrlNAI)TFdo1^u&mjW7=HoDq!2f|IzS#Y~WN?<#h71hpTmyX0^{!OLt> zPnnAzb_e5kG82?b(qe7Zfc0kgq}y&TiALFyn4<#Yz{y=+lL>y2S{uL)(V!uS=r=>X$=kay0=Pcf>PG= zeAh9S&8)Cx=XD@$*qhh56iPs?EiDqa!Ytv~vK`9ILJK86e+b>1th*Ec|1jqLr@M%6=P5dkdlgOr`HG{XO+#}1l-A3~atw!tYf#d1z5hKn_JJ%8 zsb<=bbVhdQ06m!X7$^G@vdCi;8whUJmiM}+Y1zub)HWP8HbvIL31hH(4Z`^?ex7c$ zz^3r^0HL9)NYH<$-Q$^M_09<9%yP%M01+jBRpBM-UQ9pIL z&SccDt@j<{DAGN#Wd(qZlKB}C&!X%h8(nWt%FC@htYa*Z9!8Xe%dy%*@NrskrIZR@ zYD`>TgS7}?!daFgCtN z|6-3a#}CqYG1*v(vz$KtyHcNqC9Yyh`MP&1#HmFP3oU+|(YR7R#%1+t0x9K_R)tS` z&8E9t?g+&-XZJDT6S@$2)fMx;<8-^@a=YVz{kSHZ`p|@B*;v|`HtN9_sQ9AgPboA< z5m+Xy+&_b-T5@f?&o@iN^7q1mJOr|4`z`$upw2y?toq}b(6jcb9ZYfB$)DNNwE!&N zI)X`ADLl%eBddtcYm1&U^0q6ciy*UI!+tyEGZ4GL;&I4>`tF`v0!V(|$iDufoz)Wy*3dMw6{ah8odWOuXpD$U|G#Fq+sgO)* z=#Qo8c&@BBHR<>CznU(P_S!RkM?FU9K&fDc*p7X8V{IZo<)i$s{2UGmJe&S}#fvs# zxdRuhke7pXhU*0#5eI{-JBf*SOu(<5RF2QKL1)*r#U&+(YF)XT#;E~16@s@EWUHZ; z?r5U^dw{PC6Nfr^j$CO4qJaKfrKQzu{2s;FV8rV@Psw-#+8_1pH%IbkiM$k7{@4^- zUY(`m?mo?R5sSNEV<t6IM6e2Z+Ay#9kcgN5zL%Ouv8QNqlChA z{m}KnJ`z>2=m4AVi5y@0+%q4W_990{b~KqPd)1AT&hi-XgAw_3@iOxmJGm+rpWCjU zn^~NVeapmYPoU>~Jhwo#|w$3oV zgc|x2cd-K9k_ON4Fu!w$#`b`KV{Q&U3+sLyhsX8>1W?EkUs!iPpjl97oO=a<<**4} zi?{O&0%#=C(6;$rTZw4ut2kp{5mfM{gD4cLxstzIh2NZU+I-#PM?0o9q1Ltpo0vv@ zpE;ItT@lnLBNIxHWp18OK<|1StFlLenYDFPxtBL|x^J@2owgsle-g}(^KEzJ~q<3$n*=eAwOLL_EPztY8Wf~2S`6f zTQ-I43>sxVcF7L}DiXF1In(PF&K#>-Zx?Oz1Cw+yKO}I?R#j(R`1O?LKbkS*!uMwrda&fZm{8tIbHZ&W$|en>(DP$5dE0Q5YI z2PKi9`H~8`M@dwq2w%+I-Tk;ZI1WjXM$~ zr>uk*G^xL7_%S}z&{Q}}lT=h%Rz^yC*2J9M(_`J`Wu{|(AiTP#OTkS@>4;zqwu2u+ zxQuLn5G2l?rbdh5ZU`9}7EUduguXnPK>3^V`-2<4#s!+G!(4T@D=F?PpZwV%fkc2()Ly2c>7<>TTWGb(&BE>FSu)KTVb%7dSLM_YnabCD*-JRA_Qux6#BOC5 z*EZ0;LTl6JjYv8vl6Q=>2U@IwVN;};+b6n-zD7=pp<&&U; z8xBtiI9Y9M+fiuglV!n@bRv7AIAyWq`!G=(5}fIGQOBs~uc@nw<_wob~k9$r85nvd{1cmBl=vd@P|eTM7u8t||7l^IWs?mfi}_WeU9%Y}K1 zs$;p~OBaRTlzYH|_>y2YUa{irrxH1E*aTsRAdkj$`|J8>o_yp%A*xca3>-hsovHuG zDdXGMRP~>a>7_oSUcz{Ebug|w0xpr~cvW2;bT+A5Zi`?XV?wayWN1qZQ$T~Gfs0gF~%h%nQq zmzQm4@?bf_s0R|UgPMA0{sOj-Mz47{60_AyNS>XZf{G@?*zR2xv|jbRPiA|Hr}LNn z1Xn3Rj+~sNA(5UXG6gzysfFJ=dp7>Ue%MIo!YP5~Cpw zU`x1oI?z0Q&awT|(Y8E!ne>8P;B5tI{C@hK?O(2Kk+WQd44n;TU^Z{&KF$(Mwy1V! znnS=!7WU*idrL<@Y6-ABA<$#oSQY?;p+snE<4-r3nTtssy_ zl;@q-Q~4rRsj75~)64@sec1>BRFTn^A>~i^-swf!WJm}7Ycv~%OITkV>b}gE(0*1h zG9~#=Uz{L{id6(Eb3&a(H}1U;ho-y<5Zx$o(XeU?M!Mu|`U%(BrAx2Z(Jzn9cuz;} z4i?!pfoMWAd9*Fxdc_oc{#t{%(+%^+G@hve=*SK}Oa9VEDn0-EdLBHDXKmmA?Rd9X zm~mbZcXC+XrJnP%_z%2K@0@8N`~_}OQ?jaH4&NT;6SQ|aUtIaTlK5My3d6u;F z73n{15Zxeg?AH;Ki z{_VrrNdfcRZE8Y%4eKgL#YbEw^(7rhh(j5P8(JFrTR!*aajkOLsiTpS>u9-{9g@6K z|E1j{z!ccxGMu7VKTt0`;Oc_%QvoC2UEl1Ov&%Q05#2vTvv^sy`njC_E2>G+Sej|Q z+3zv8{$GflU{1F~$Buv`PJhMZw#%+Y|PiKkSiL z3)dHck+-xr`y&nb0W7A7Ma0ID2!KW?9S~Mmfl9DnCA>DyC5hqYsi&SOyR;HBYp1za z^1grp!QFIv5 zgLlEamY-qW__vHk4Ab%AU#U0amObg+%TIRkm%!@3O;j>%g5+ggR|C!yJG4MsbD7ZP z;Dh~vfx6TziTn=#?Wj~(&A$1 z4>p+*GcSjo;E17%6%CmNPxG|u^qU1sOa1hpYRUdd_sx0C@X`>c|FgJqiaaKw;r>f% z{|my)a^z&h*gD+9!~O-8I(FD*g)Y7pZgALgduR>XYq=IyhNvGBX3+jJ*P`%l4swOy ze3|w5!fdUQp~7zog(ku&XBzQVysC?+RMHF79~*lQA?n74)2?Mzqq21`h-$=AMpjnB zqxhxPG(x!S(A+$iN#w7xt@m~s?HYE#=9l^CBl6QrU*phVw*D{9q+BpM4KYQGX+_&J z@)J7n%JVxTA8{l9_QxK&EBA_>1~~cl$XxW$?t)CKXs<#;LqYb-VS2dP?n0p3Tqvxc zx7M+(3Spnj&K|Kt;7n{5TwiX^{E2zr(35h~?7~1cYT5SYO$g$vrNVI+GW^L3@n{@=~(lVL`_PFwzw|5YLx6v~XqI(SREvjQ!ri5Ak=voiwd?RwyaWVvt^4 z|Mubhi=Dx4T37()hdb;el=4f)8lu>gIrgnPGhENoP(5{6;J@`IPf{L-_3l2;%!JbB zpac3KCsb15%ZXJ#GXVb-Zi>HX)uFKT$p@|&A0RnlF{JIyi3Uj7=2s5|ZD4&MrEOo$ zM}0!A(ZxKsMibNMnN{n= z<`Hu#_Lm{X%^_1?fT_47TFYG`FwgwGa>bow3fZPNpaZ@okw#|<;$e$10a8RW+Vt33}C-o_~ z8~)=|dyC^xk7kk4-0H$|8ruQl%9)uWClRJw?;mgXJ-KvI52W5|ThfM#a^fc*8 zK7IOhH_;apd3(EZ-Qne)wR4>{s;hJ6uG-QF_7Hb<9A!DaR8=j>oiaLvRMu@Z8Y$WW z&$|L||H+8{;h_6oVEa~C@H9EuuZIZuQY9(r%&NsFF~N@+PL(h4nK3Ifz`n*#`WpPJk+ zdoO0Sj}ht}{QbG4WZ*VV2!2pz9GX*HVQn@y+nnC3w|-1Y{8`Cof`vH;4LAzF{d>H! zIC-;V;p-91pGY$5&)ScvW!?VaV8|b^i@$K)Yb{W^IX$iY)xl?anH%Uy9~%$;>lpjw zE>AGhOi1SRh*$UKc;Vl{%`RpQoiqQ9lk`Uy(TkVrJdJ{27?G&ZNy$WAykHucDu_#a zbP1-HNawmiF5wpstQmHP&86DH^G`+1wmBrFrzX@04n&u7?8((0z=(zK*G_ebz%bc_pRR35(f>s{%Wx-#zB) zJ&d1ZblHG=EGsH2TRi!-(>xoP-r2Uz5D~2?=R00p^aTT93jD7(k$?7AHUN>=_zI(1 zyz``go*nm)W5dE8E_br1=mO!Cz67Qe<76P_QM6id$yD``KIQ6$YqY(kybqn+)*i(Q zCIGn?uGXeTM`eOP9zGJIjwfbMY`#_$(XvZZ#MYPGC+s%;-C-way^U&T-6z)8nNywS zkM2jqI4XZOEh3=9RAP-ARtU@AjRD~io*2|ap04hHchfKY7z`Y`NP<1pk*@yV!`4rR z%(toPRS3WHjqQ~K6LUpkQIQc!ZufM_#rq1y(m-h6Lc)SV&bKyWbPETRTSAWy4~|w7 z?+uH8V-Oem#2qIJ_>dZvBix-s|AUXa2wm=EZ~)OH;kf%ju_QcRRKnvuNmO1uAWZ9% zV^00%aY&3=Q)cfBc9cyv^jzt`!2%`Ve}qwd&yO|6tbgAN9AlT$T~XxrmwsD>Ivx9t z0YShD)jm7G+akfxym9hg8SS?(SAc)8(tY5U(*~9ETleq zv}tvG?Gotdl0+xE@J)DlGF}|K?Zogx6YGKP1{SH0w{3WM$(Hh0UmRt=lipMol_@N>Zue@}!}O6R09f_+xUR|PeMy{rPX66~g3artsr zgdGoFB;KW|2FylNUm%;M5K>RY@i>QvSS9Yjf*h+B_=Ld)vK*YFXsW>TCjTj+!Uy*G z_(ov9;pn)ZXnfoz5&lsb7!V-C>k<9kBU7WO#<-HD*w@dmoTI>P#Jsq|?b2asbYvt@ zgq(?K9;~yrzAoZr`6XQ&`62aiWB*r8c}N8#dDjiz3P`F>KPrf=#qaA6IBH@2wE=9( zwiQMv5D{%G-`#sJtSu~9lR^_UnykwHD37{|Z~1_f^ql7~hf9x`y3a~_#+(V0M=}ud z^CE%R9Mxmt{qx(OH&IlSbK3R7N%iQ_xa+*ou5Cctgtk*oNf&W4E{n#7EyvFCK?_Q0oO}` zj;&j#o4{T&^;drhlh)p(owncMhGjV3<+tWF8axJZg!y0X|6=;=C(k;~5?zsa(dRAU z<5Cdt16tlmGfT-t4w|ZtfRn@qe523N4*Si9>Y02CJ1L>}m?^lGCse-zjS$&gBIUA# zq2gT!J;e_^@lXw7F>YzQO1ewr)tS6yhu^>_bHR7^e=};WXzdwJgcWl9W<81~W@mHs zzJFScPxAS`A|z7903k>lum2s193j@FybAI^70+znvzU4d)Gjtai&Hi_nrdn$RGM%3 z%@RC-<*%OP>4auZ6YvGJ?|TLu1eL_tXln)UC@?BpJ!wi*k&XXnRV9;`MEsI;u(QAw zZfmdx`20xVuv{&*ZpM8xXUwxe*>*|Vk?f_O+QS!c7tW6gTDG#oWW8EXlqvIdRMW<$ z>+>={kt&iB<>Z>1na{0Q{1*$-g`%mkhZa=2CPw-QKUTe9PqYX-qhUO(z0~-Z`&cV(W1hA4-$I$ zZ^lLxVQn#E((#n}{ZLt!wt@Tu>Qr$n^SKR#KYwjJ37KAf+tm||nh^nG7GKRzy(0N? zD8bs9>=ys40Q&X3)4q~|aP{w}e=ic8XbYUa+YIv==4Nk!zHuMa3KlYm4IL7g#kOL) zZ(e6%PHA<{@=LIEoTBk0@Q)0wD;Y04E>Y?_g|WS`($znJVrPOyT;bTaWYrf71?82(Q`?3^;#!(jMuR>KIwi|_c^>>hNWDOd zA%P}1{ggi=@R)VhaF9F$(u{S})>i?ew*i+T$f@CRk zw(1Kd@W#SeJ4qHTpp@163fSzQ^xO%M54yAc!dh*E2>q41`AAe`Yuk0aOQwpDyqZ;O z&~uQMrq#~QRAyFI7I}}=S)u*Lgs(RLvwn&6;ac&h_dF){DlN>6IqpBodika+%ZRbc zvPGKp?5mpFa}nzIs;U$@A4IG>j64n7%vRAAxh}$llV;QUCbEDn-_IClJp`7O-nkw3 zYL^GmChO>+BN%wdvi8SUw_~G1-}7Fnmdwdq>5^~xjuN1=OD5!-rkeKl>Y5rT|Gk(LG3NdQKWPtCPL_yQ zqAD_^mi`?l?DLHKFk%tJMaro{IAgxLn3WkC8ymZ#%qon`&CR)B)D$rTe)^{xBdd;6 zPc8dDAo)hqhRn^&+}wg{t%_h9kG}LtB<`u3*kAg7D zIjdqSslddDgSWD6 z{#DR+ocy6+|J~|OrT9wN_+AfqS|(LvQ%g5Bo$k=iY5k_8KrYC;Lmu=>lWlUCo2dra zMC1c{Flkn1A>jx4ba`ULrB1b#8PIW_`tqrye7~P`K74(R#fRSHUEuYlKU}Xv9xNqg zWoG6aIE!-C#%9}Y=rJ_&P$*bcrW+eÙ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 W7|13d^pzw40000ceBSD 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 Zzv7Wk*7HAcleYbR^L_~q|EMv)`~TJ9S)~8~ literal 0 HcmV?d00001 diff --git a/code/ryzom/tools/client/ryzom_installer/res/modern-wizard.bmp b/code/ryzom/tools/client/ryzom_installer/res/modern-wizard.bmp new file mode 100644 index 0000000000000000000000000000000000000000..9bc88799edcf710bbc3461f623913fe73f7d20b5 GIT binary patch literal 134446 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 ON2{P{**UxCp8p5$zz)Iy literal 0 HcmV?d00001 diff --git a/code/ryzom/tools/client/ryzom_installer/res/resources.qrc b/code/ryzom/tools/client/ryzom_installer/res/resources.qrc new file mode 100644 index 000000000..0e7224f4e --- /dev/null +++ b/code/ryzom/tools/client/ryzom_installer/res/resources.qrc @@ -0,0 +1,8 @@ + + + background.png + + + ryzom.ico + + diff --git a/code/ryzom/tools/client/ryzom_installer/res/resources.rc b/code/ryzom/tools/client/ryzom_installer/res/resources.rc new file mode 100644 index 000000000..b58619308 --- /dev/null +++ b/code/ryzom/tools/client/ryzom_installer/res/resources.rc @@ -0,0 +1,39 @@ +#include +#include "config.h" + +IDI_MAIN_ICON ICON DISCARDABLE "ryzom.ico" + +VS_VERSION_INFO VERSIONINFO +FILEVERSION RYZOM_VERSION_RC +PRODUCTVERSION NL_VERSION_RC +FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG +FILEFLAGS VS_FF_DEBUG +#else +FILEFLAGS 0x0L +#endif +FILEOS VOS__WINDOWS32 +FILETYPE VFT_APP +FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "FileDescription", "Ryzom Installer" + VALUE "FileVersion", RYZOM_VERSION + VALUE "LegalCopyright", COPYRIGHT +#ifdef _DEBUG + VALUE "OriginalFilename", "ryzom_installer_d.exe" +#else + VALUE "OriginalFilename", "ryzom_installer_r.exe" +#endif + VALUE "ProductName", "Ryzom Core" + VALUE "ProductVersion", NL_VERSION + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END diff --git a/code/ryzom/tools/client/ryzom_installer/res/ryzom.ico b/code/ryzom/tools/client/ryzom_installer/res/ryzom.ico new file mode 100644 index 0000000000000000000000000000000000000000..f2c7e8424be521bf1743ae13926337695688838d GIT binary patch literal 160871 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℘#_q|NI;d9zF&ylR*-XLV(rlSFv*K z3Y1DkSb`5&FFWWFnCy2^cgoo{M}w2Pq}e zI2w5*TmVE+NdnF}KtRUJf^m+qv2h$fc?QLti~ApZ0CAimNHlggu43uzWAJ9qgu_&% zYv&)&MN+HC?;H6Hhi#dt=5rVX>}ty&`(vXxE5{cP9i)%h zWE?%)-B|y69QbXk2ty=E5Ne&oS}UdxX z1P;Ifz?4!fi4u#5n3PhdNg~oDW?+^6*B<}%AIr$ry&D??(aS7+=;+AlKU>Hu0ax0Y7;=H~Sxw9I4*6}X-YVgiHV0G5}- zU@!offNh%aybOjzAJ7VxWnrv33EOti*xiEdns8kg<*_+*TTKu&41+_HMs<7)55MD4 z96Edy*=!a`oFIuKq%uXCgy^&zC>F}7P1aDz7ce2=4hfjR$lepuK6BzcJ=yjXe+-adS{IXkEyh9ALLm6#O{tjnFY}VV3R{qSz`067Q z6UBqGmG~E)U%UQTyMFD3@!4bE^vP3?2;089Fg-Kb-`#q$I5qodb@AZA&c^!3dz%|; z%rJ<72wJ3yNg6a5o7(4KZmx*m_~-u$|KJxs@;xU4-xD6do2|pYHl-pi9uL43WYK{ z?KUji0cRXDGc#!H?xIvIqSFLHmqEEYfxsW4zOw-^AXhBofd}7# zd+&ee$l`&JCJ6uyQYrj?4=XDxSX;gVJCi{%mq#v}gGv)*b436WY1GBQAEH((K_z`` zU4IE-yNOFzH?g!SaqPDJXa)>^=%Z0@V&F%}XFbf%PT=l)9)Qr>c<$0wTwB_~Zqz|_ z&V_eaVX<-wrw+XXhxgq9L%A@xf!+2Nu3Wx|p5MTg^B1tMauALB7N(~6;kk29B1t6_ z2$} z)AQI^U4=|FEW<(^hB$iU7{)xn;vu?&HtIo)sp>eg`%6f}Q2M+1Kd86j7nsP^)393` z`WyFJZv5VA&JkG5tRbHGhM6p^P2P6LNp71aW4sd&2hSB}X77+u{8D>;^|OQe&K5Kx zt`SSlC6}=p_8fickgIDGMO4Sf>3x6m1Gs7Xv$s9o_5$$6x%;n7;k|i0zm1VM!{RU& zN^3z{Ynf^u$ElXNYRuL?_QM-XPyA9QlRfz5ubqGDgMat``Iosu;r_ngy1ldZ_=6S4 zbBAGs7q7J7R+NK%O)N}xE0o6TWi{~h)dRcKZq3b@zRE?zyM1xweYoV1Qhyg7u>XAqr#rF*h|0 zMTF-1GB&Qgj7wM6ae2+h;c)~1^^e?+!sH@&CWla_kYu8m_aJ+l=(HmU)q(4nxZI8L z$D1x(#p#+jKP#%%cj#`E(qxdE+Hw0BnU#V5|9+iT(4)l2X^7q*+hgO9ui zPks3@czlSLxB5ua6uuuIjn!y`BIig$3Abc`6N714;3kkMbErU|G@e1eYJw{ZeZP<9 zQjBu71f~g57$NclL_-Os2wD-sAcSjh>@Qicasolk#Pye>_E^Cv9l3XojmHW-(GNSR z`GxvM;WL>MFAUf0p{XzYsyCj0&KjS%X6nUeta|R`{3FFU@>}`I=~*r1x}D9<4w{XR zc2-xOR!P!HeLo-=33pj)ORfffhUTX{eQYs{>A87&=cDfdM*ARw_I6@{ZKG${d^pU`wz~HgoWtX<}nzU2d#g4%^L*tdx?4u>uzYQIsG| z62wY_G{N8|l#&pP!^z~JH6f7-)v+-=^64jeg-r=R>hu3Wkd zE^I6;9>hD|`6!B|5=J|Ajb`bQR!EZ+qs2HPwA)Pt{Vv+O>u7WX>};=~RIm`H8k@}y z27yAcIEHuLcMS8j9G0&xVfo5cTwK~gz1>CDHIRe}CW$?MJ2ox(BROT2Oae&PRVd|Jd zu4JOU&5_TS;1mSD@#$5x)a8!vH5B9EOPf7;%_FOM+4aDS?b2%+w4p0A15c z!oa+ook{mq$628wT!?9nB(hN7jGq%_YiIY1)>53`{`o^^CYSv%s2F-~XXV_^#__}N zdfLk7cy;mMDZkx02hRF_efR5Hs-{koUgYzjBokK)XF`t`1ikOQXXt_Z zZ@bZi-_}C#_N&nzUcW52esvQVCcwZ(d)H-%KuStPQEVE9g@62S-~aHDyX<>d$KGFT zRvvwE>00CdGj~08=i!N;{PNeXUO)M+|M`E&&b%5=p`AWYz$|;=+JsIZ%vaWpxca3256Urm7{}b?Qz$@}3_; zI2@wa@4&JJDq|B!(-`&L9qewd!_Ad&&jas5rCfzp3bx}Q3PQwDfWY@5B@l!W!Z1V- z2IzJ>a9jt&eg~@;pMg*TdPxdHIEa~vTzLZfCTln{U%<|li|7q~oPFVCY_+;5RXrGv zg;Z)Z8Zm5Ac;D$7PR`oU&LV#QQ%_-FRx#)`P%326YIl&c74AJejU$I=uvCxm_}L}6 z&KPDErr=W@mdo&8Jn~Zzsey9NhGl#3ybLVc1Q!;1ojN}K$=^pYpM`ENVPS3(zw_tM zVxt#A5d#c~!h{8n3kd1pzP~dC;aS*hTtz9nA90+b9dvN@*#>wl5bgp=MCjK0$Q3+z zwu3ZEMj{!cNTU>SA_1l0aE(Jp$B}pv^*)7O&R#!wI(H}zQb;?2zbnzJ$GG><6kU8a z^Xoyn{L@oER4i>>^C|9n=c<|enz|J{nXQhwhU;msQZ4PAf9VV5`2}~dvwg+RW&@d| z{m35-gyE%bsUVGltvthkCGxt{cIf0?chNum@x!k++P7&~@U|0xH?GyUw_p5dSzg9D z0|*uYp5ROlgNP}qtU@l6{>s1odvh;be)_#1{=maO7i@?7uWVd@tTI=U?Gi%Sh{o>pa1G5Y}9@DelIEJ7)hz^VZVzg(MU95 z;75pK2_*?iYcK$=WkPTuoAGeq;9=}vIE<;<7z(8l49;o zvw(XadKAa*ID;gKA(IqIoS@xqquc2qN@6%}2L8|ov_!YHi|w^*@CPj^>YT+Hm+gn1aSOaGx)~i8_1~&hS?VSS2QM%TK`A;?4m3NQr&Q-mOh zaN_tqc;~x6fPeN+ehF#p<6tcV$I;l_iqOwl>DVHt1E;E1kVHsgj`e3c*v}n2bmuX= zw9Si$eu@^RM!EL4qX@jM1mKOyzSojp@Vzmv z3|R(}{b9g0kxc}(TK~N98^8a*{QII+KJT^4l@vL1oibXWsG)fLVx^VFS zC+|IjG|SF9&)>QB^)9`w%*ygrUDegD+E#bd-I7`c2@rxEVV1!&v14`?mWi2x4HiD^ z1QW9}YbIs}v%_cw0)ZqDLP)f$_O4u3RaT|$ z@WO+mxsoI;9n;XT1k4_usacvbZbXZ$iV`FoRUbl-dG^*t~XJ-ri{rijD+;e!R zk)+dYlgkt-4V2j3Ttx~&rkKU;MNCd)8OoRtg!pbqR1Aqb3Bqp3+}TGtbKm3G=?qa6 zVHyTO;L-2*`SU;fBQn_>wR(ls49cc;M~< z60zcydV|bF5;LpQ>N;2{%^RFdnLE$9JKmn0{J_Ooty1dNUwL6abt0d2_0zUzYiSub zO0Ncq>bXqv!FF%6^(EQYwuR6;ljrXrX>M)IYr5XiExW55hGQjCK{B6^EI{P7gb05=cqc@p0XQ9X|?VOx?gTG%~h@rX;0eiF+S@l9Ts6 zNTXK6t#1%}P2AQRTWih-BLDhOPNI@o%EVcFPriq)lM-gxarTCENW!QS2;eh?8x0l9=t6icKs ziIu^~*aWF$4#)LCX+!}O3ME{}XL7Q{J!eKJcNJcgKqOKGPCy)l_L1)ir^|cQAScL2fVpCGO+r;k#boau8xG%v0IVg(I zO@Uzonht2x8oTW6Y!XEwdNM*USWt*DhlUwDqLZ9RvAx=2{A32N6YIlMDa{F@M0Q%+ zsk*I9-}N#h>8zQaN!ST9%Asq4naZWjTz0JEF6Cvmc`Rp~Uh>-QzT0ZHhK`ip2-Z+>eXyAoi&=gzS6+F)&pUTqPy+*%NqwfTS5d=Yu zVOz*Z(rL9Z3>}0)zBGuQ$e}Ped-f~?qak^rh$923=csoaOe0CG6hRQMv%N*H-(h=e z8PkwF{op7W*`l`J$9HvF$q72W9^F=xFz`uca#YG?RHQRFo#D)*Cz!vsPGw<-iHTzz znwjIVr$0=q)gqnBFfl!YZW_n~y#Gu(c@!N~CpCm$@(*z38OM5esIU!P8T}=`Xi_A8cP?7S)uD4r`dQr^8K!6 z*^>BRG#N@w>Y*P8HBDW-%T)J2`mmawJ@j{`75F<*0RGy6-+SPyzO*iQGe9k`f0cH-LWO8B>)vqu)HI7s|VI=9c z`|Q=1L z0)fKJz_ABLNi=4?giM#eoqTyTr!UrJM`Zb-JU)2nF}BOk@KzQu9P+7tJU>8 zTSACFLbL{FkJv`S?v9>1_cX|L&9q0oPV0|?UcZj6g({AtC~)J*iz2@j%JD)@PL%>~ zUY%F}`ZxZax_Ig#F5PvK@h8gCPcc|?Y4er{=0wB z?aIvlYVFx%-uimKRcuehS!ezF?gKw~@yLts+`jX<`E~KK8q6Gh^2FVfrzQsJi$QK) zzf7y`#%nuOx^c4CsQ145gC9P5?!hD4iTQW#aBZ#2l|7#z22Hmxtpw?GhC(*Y#m6sl zMhJ<}Qxp#6(fvNjNT=J6SX^yz{_c}xG6i%~Cy^|m>lQ)}@w_tq&Mxi74o=6V z-SIIKCSj!GI3ALS!Jz@7sLS9`o@~BE%Cs39Jxt*GoPOYwym{#bwl}vqa{eTfhmO&z z_0e?2p_!v7pkA+VZT>Y()57sPgrSdSTdc3$p}O3mwztK^uuZX$q}}q_sJUdbHt}Fg zQa9+`i3sBZEdVk`Y6>(RGhq@-2qT4_RA7N&83bN{iUkM_x8-A)2OJ7JsbQBC>9is@ zWK-=$Xi9SEc#`FJ`wUL$9z zE6XpoTTAuNi}vV(VQNkshs}v|_ZQNGLso8d>`8={l}c^)Yn9K2ymu{D~qe@&6nT!=1}lESp>eLRN!yj`M(wX+Fyb{Yw5z=Z!dN}cj;&E+1amu zYM?OmqSK{Z-HmTN^X#v`bnA0J^_9%D~p$T=Z!B@Y3h_l&hqRhK1Q$Cr%~OdlJ(5F+0f8CM(CY001BWNklEr2fLO+ijZLBHih6sS(dij--VF0^yo}>Y%H=j&n+xQ#33NkX*-0!j zMZMV|3|egL^k{Zt0$+l#P%^-A`e?e2RKVgsnyxW4kR+KXlh1ezls0+x<3EMt^>Kn- zj-44}^6(6|wqHe)8p$X}+bNOI(*&W~FZo4YEUv>!M=O%DHXd<<{vYjZxF0W#416(Jl}{Pg&~Hr-4XK>h`}|j5+t#Eu z_W%6olcN{@@Y3o@Ph=Oe*?~7aKZy7Db_M%1SSt754Axs7e zMS4y^rLxcB@&dc<7JUzr2@_LS=w=$j5C{Y)gKpqrDieXi_dP7jz|h_k6uoDuB~?fi zMIbbk1k*A}+6LKN4t>z183iPE97Le8wAo{)9#G6{3>R%8348UBz8e#S2MohpHbvHy zcd78p8b&p$w?RUw@zas1RV4&61#jfIyf zW~M23cgWc#GS(>XUVoL7hwtb4SH4VfC{4B6BtI-TKXQ`sbcU>H5)yK)cALFupJY3W z>e$p;ZQ`_~q|MMOx9Ie{G#f4YUVv>#jHE_F*Rj$TLOU=!i6w-wA_`)pkjVI8SQv#d zK^PP35@jedC7r>PN%4fq#;SvE23rz*ZS(|9#Dt}KQ{ZGICdR04j-^c#> z)2DcBV{Q9nacFAw{)-=-_gv@D;LzaFYHerY_=z)`Y1$w|l(4vU^<{qV_diF?l|->5 zk;?M1@A-aa=Z;XV?9y}E1inWc2kcZfky28r&!Y)R==wZ<*J+MUlxSDB(Lx6^J3_Nw zq2H>rwZ6svc8glq$BSd4tWMwy!l;ky#khWq=f_B;Fb#1)DiQ(#be#xO2h-GW1Bs@A zA1HMFz`lfH!tC96D;rUqLLb` z`zzR_sfSgruH3@3;kB!OPTz0S-EJVl2yZ>)lG$RQU=wQ|y-7$V(`3gjW+o<3eT!6D zv9-~qFzmu>MOuwA=bjj$zTYQogEiw}`5M*5KFPF&rfc}Fhp7~?7K5e;;|NXD(X&Zh zPZ7p~b|)lzGD%d5(6a_7E@Zj+Y7gB2E2)t#3aWb{V?%j17COR@b^YGQo)iNEcX8{+ zZ;;r@3Quktnrk)!kJvRrXK8NWrgsNP#)xcnhOfI8b#}yY(J@g%G1-`8X;BUy}`zEJf6GAgIVa)G* zb@bY;SARhyyAKaenM=!8yz!CQ)bHN8)%^L9xpe=DN5<}1TCJPDVP6>6##hrr#+dY? z`pIJXxKiUE+VsbRzxNa0*Gbrkdld0#5c-*k$q8K7qupo`yA=ZIbL;Y-Qz}~Qce_j- zKFXsPpQ79AvbwrNGLgXZ`}BHk7FVy+bGz8O;J)L>8OfXE6E@0osBCZG^;!t!Q{5}G zveV_(T7!jc2QP>K$PG;~kRGQvQX&k2AVdoyY_E-MEaUb)Vj(beh+~P+H6j554K#rg zifmRVk+eY<*p>+jdaW4Gk0|7-9MQ78Rdz{_8qCgQa2v3@)IlnRVSpxMa)(SJJD{>A z(PFTZHbzbow(>9eAc{LXk{l*xTA9Wv4lH=59{ib1yfqy~|W_1YF6+c7wal z-^Y_rUO?9cZqR3MW)rs;u(h+sZS8HUjeUfvXz32Enj#Wup83@G(ehiUfi?VYie&a~ zMhe3`kV{cXUm`A!vfph{92usQTjtU49$@!YjorRSX)sISaGtfb3RXkVuSO)4#4;r^ zl7xNmR7k>#NEX3RI-#Q|U+*yUL;>FjDR1~3JC`CmWKrMk5rmR-I!UwPGIJ(Rwp5_i zUeqe}MKphuGpEi{X|1WMs2>k7(*v`^%H19LQhIozF@NJ~?v(NFKPpZgc9dK^5ruI@ zH^h-dVdTl?>eBB6BJ@3>qDaM2s5B9)X4_TSL_r=saZJ1P7ca^}sVFYpdi^^x6#TaF z0RBesA3PeM8-}L6v-+8_KYZ?A|DYeW|A*pm;aa2SK9(7=H|k~cMI&pTj;e{zH(Ty$ z$I$e1wk6Sn9 znV-MLSb85pih5&(AU24s49#|n<<%uBm3<~Arr0m6cZ zV@eaJ)}pq%!T#PR%InkXyX;kZym6z({EkniG{))sE^_~e{vKx@eUh=6qiE?OW;&0R zN|TaxwDu}dtT9lsNZAK%fngg6(1>vVzUyh4%?{m8oBJMooYUv-<>F({a(Lz>*-Q%E)X}6yI+@25Ha8cp zva)f7UMHeh%2C~SIC`W6X%HDj?3)yZ)65K?!^~)GZESFB??pPD8vFHi%-H9?C(e?c zF0oZvBus@AiY0b8JBW_qA~`))Y$1L_Iyb_0uh2CG(;+fAVwGpS2}dO zfZ{|FJqP1c8B&P?wH+5bEto!dlU$zgkLJAKz=NifnN%S=)cHn9j}`hEW(BhaQdXvk4PmgbS>s5zHg4TjVhO~HE@b1ULZ+NXpD|#S-a68 zn=z@}a+tn1$HdVB+w(2r8q9tqOVGB!(`j#eNJC;sgR%5sTD2-NhL3#m(}a=3(!v$S zCg*tcnU9l9rLhx9lv0FIfYa~OYPG1=t2F9$cB;!Pth`O5Qlm6B&f>LAl6jLO=Svia zbs`xsSe!!HK8;qBk?|7kYL}6*EcKl}$L<-SS+0{ENN{^$kJ4z8%{vZD@3tunr?7@2 z#`F(dEiQVGyBaGN2Tt(bc-k2V;R)>5=@M-0pw z6lYA_J)0PpTrtJ=n$PsHaf(w(0xt_y9l{=ZCMKvUbUR=&HN(Ch;55g7r&sB}Vj24P z9X@~jnIMsO1Gk>;b$Z18^yllh?tCHgy&94_$d-bxE`%HTelS!j#iN5c#wN$*AAaT! zRHNQdS9flHJ0SVZ^=)eb{?ftcJ@CJ`^DCvaq^Sc~ude_4!|g`zUyh8HMly+!O2uD) z$2QWbgeo=;C-40Si}Nd=5=Q4o?-@(et(1(;@JVC3aF}=Bev22L|LvuVPkv;%-RV%R zR#JDCt~|1|x=7FQDdtPqhM?E2a%6l2r&YrZB({RIa5z0;aWxQhx*fJw78om6tu*xfs`p8JjZ+^^oC`&hSy2Ak|{{LXw0Qlg;Jm_yO%^ zKxwjoVUKe(KTCbDh5<~?O>^?xSt@(mj8D%pbL7;4ZI`a$_8gjxCcW-KVQV()+?aoh zrQK!XV4qBA5~8S7w-}pDl1dFQV2$9`9j0d|ICNx;TCK(Os78-HMg~Xdd3EMa9%gf~ zOuf-(9G&LEFe5`Lj*jIhJXj+Nx=iNA88I@1euL&v4L|47S%`S@k@HLqk6`GMbfHMQ z-KM->#|dIuO_ye~$)dl{%3=d8>4Q+LUki~!(QCMbeFF{$4320t%MFI-hFD#^iW!YD zFf0+PHlf}_7b)%-H_3O#IdS2U?_IgRRO>YA^PZphfSqttkzNRWuWV|1^np-yZU}54 zbcGCM2qKWW7TMXfY`C71btSX;ocQDqe~MnGr>_35|LofZN#NU10Q^XPi%0)oa{D@+}z)R2lNcFN%L(l%2fg*`HbAGpko4|3tiVV=>7)OPnNBn>jz1S`aEjqn` z{dT~1Dw27e z%0jMXXiS78Je#8*GZ?HzBnA{p3L4jZMrSh!J3(Z3FwxNxHs#%bsS}eJ7S!5((i1jf zC&^S|gw}2iEp9V0ImXan38&j;?)W(jGl5bOUgT5Rt5MnC=lbQhS(v|yZU`EUI<;1l ze!q{dX-t(AfhFnJV;+6v0)_M-_#qEHeVlADN2^;Wmolidm(f&`#d3v_@jQF&I_XjZ z;hy5;z#y zLA_o>8Zn(l8>by$`hunTHcHb;XDqfa`D70p-2G&pFo^ISXf-_A`vGI)14uvS+RF`2 zp1F%+;$BvE{upmpAZ!p}D>HrK)OVM+cVG7cv8NinahXaPww)!6>ZTOlxF6MQE6!gt z?UXOW&{t@olt!G&n6l@4Z0uIzyYe}aE9Nvn{T^w?zj$+gw))!FHX>x&!L@0=Wc^!WXUKk|BI{q_68=JJQyj?dSh ze@UHxzy|TUbytKbN%|YKMFm^RYt1Y>UT!kLF3a%NN56-L^CvWQ=nNI zv64JBX>H1%o&p(P}flP-beBzz-shJvqa`i45b1eNrMOkFjwg&Xv!udcAOb(58wD{O3RQ|b6A>VLZat$Ej?21a6(-BN@(&Kg{UVv(&or zjq3L9okZ6Dz9@ExMA9#>uNY42+M*ZiX+~@EH;O~EJASvjtZ7D7Gp(MHNcqXE9dyc7 zIWsXL&m5mqx~{7q{^|ez+r|xe-%kMV1=apl@M}t;5$o34&TEH1dhaiFO~X3<%9TI- zH$U>suN?ovOaI|B7mj}7UtBoylqF*KiJQGpm%W%Te(7sG_{0yqbn%hL?pnP3`oO}S zHLlNJ|AXmVc>Zdwx9&%t>}$PeG$c9)S9`i)5D^nb65@mA586V9vbSRHfIzh{3cs9??)i-$fkq={MbUHf~GMNO9+oN4;P_OK< zy?Prfm1k$^I<@sBjvX1M)$S4m5s5^GvkyGN(&8eSRFd7@eX@xpVXw);o#zlEJxbXe z=3bHMq4OL$I>*@XEGiNZ>x>*12wmXz0#qdF2Yu|MiRbymQj#$9_)(YC$Pi|#h|}-U zaNFplPY>p~2`e9nDbO zdZW$c35(G=i@Tmka{c8BCr*AZd){mG+5xeONaZuMwrg~{j%i9c(U(!O?(> z5uFT2Myx1s8!1DTdZExqCdZuJ&PLC#%LPp{a=K+Qe)gXH-i<3S3kc9;6o-aBG(M&_ z_R8Ay_(+^drv#w>uYdLL_y@o658rQizP~4cze4f-)?7Z_b$tD`h5wQ(l=5G&w8Ys? zulc)klaD>}_U*4W2aGtwIK5VdS6}!F zwr%tHBNzDYXFtsO`yV2XLT+4sk$QcXH!r`;n>W^IJ0VuWAR#1a%fNWgrB`V&Iy%L? z#b7B(t?sf{?vgeI$&^92C$Mq`xl)FcPZ!9Hc)a=AK4XOf6k~!QKv!w@YkLe2457sa zYwaqdr4sX(cR2IVI4O!uj~?gf?A@3$gCz&)xK-jxNH&>eySc>Cf%}MsK~Hw5?=Q0P z;>)BaW@v4!v$MX0(oEbSBwHHgt_PnWj3Sm7Z?m<%&55)3((60iym_6_3pw-H5a~ja zd(S_^)XW)j#WaR)qGKOeda00Bw@JV4V?-9oL=G(pW_-<9ghc&-sn+%T_A09rSw}R=UvBZPPcCnLxN5+3$Ac z(+}J&V=4LR|MBPKx8qvyeZ21 z)FZE!>pPzs$sL;*DU2N2+u83`HkX%I7v3Cs>E$b4Dmn1SGXts9U-`eXYIW<2)f{I2cC~sT8qNNFfh)kED%K5mLs)z9bG6h9T$%irtFOiBS_v zkI}=Bddnj`xPiwmS`?>DbVDO>6q+#5g$YvU-g_VB@BhRj=s`bU43p^-yWYfV;{7P;~2@36IUnE@-}{B(gQ&y8?)wm_rRMVgQ(6p2HJLO#J* zF~P~BMRs=EJo@-K{`o)tF#4EIbEt)=#|&T8py-mB5-hLQ7(X?^Myo$Hs;b)91;PqVaejq>gm zsbqrDiAg^E{oljP+zAXVPvl9G8H*^4+1c4)e*P+Z)h$9d#xg9NzDpEDblobwx{vS0 z_<>Kq=g@W9I6aqkvrDVlpt^;fago1C}wZdp)jg*=tPQ6r%!dSM-;@2Ocp2(rbwkBWsTDFdPoA$Bm?6a zb~n4p{J5bi3;KF>d#5lqJEz4_sQI-G!^)2k9@wB;{a#Z`WVQ!%p(UL{d*6H*@e*A|&#Dfoq-K{E=7kv#>ThFaC5uu5l(=wSgm1(53DBK~Hj7jE(^eDE>_Il+~Y3A_Vx}7LH^;$#dsK}S3vq@DgSIJo> zlXHh@+}`By)DdQeCfTTNzb_bl-&z1aiUGtobp7wWOEhW?%d%}-*Ny4d7e4>#x2`<* zr(tgM=i_?zBcJ-<{~SB*_{P??KT~(^EZ$x1*!!RSfgcZVU%52(2cP?029K5QWJX*^ zrTRy@P32wrYP)f4s&Ks9>akqwBZNXK`JQ8_Les@TIHs-<`;ba$SehVd>%>8X5YTf% z5P~RF6tf0>Pa&mbGH>v}u|e*d9Uy=75#DUAAXJh{tva*D#$bMwfx!V9 z?J|RdW2~;O0FcQPh$5e4GC{ZBBJf>A6q7C(G`G8q%w!lDOp!oQt9!)66f!nPric0O zKK>xJMvu*%Cf$C(^r2zes*D^=bEm$Ra|+pBbYE<=NZ?A+NVVJK>qfc?#wxrfsX z6cdaN40Gw_>-f@S=ujFWiH1dSIL9l$Rbl3_ByrQk+lq-J?I2RT5-~lOXJF7KKbB{J z6lpU>*A>V(&DGamItV_I0-*_n&@mEuWaMC(3f(d>?IdO@%iNj!Id=AbI-NF7)T7&L z5_%Cmnrv;Z<0ab++eeA@HoI3=X_otVaeUwbU__j`>l9j)WMzAvD2h08_Y~DyjkVX? z*k%SRp))Wz#P0Sgp66j%U|Kmk%@#tz)Z_#?JLc@^ljO33JB!PVP97$cGBKkD^>Uf# zZf?_xUHa{qUQgoLx&Qzm07*naRL^7jRDx7NaQx8}jqM1hDH)q7;(9uL*``$vh>-*} zlirrqP7gBT+lGgg>BCoCe<}s0No@}k8o*hV?_@3Bz|Bpi%`@XfcwTDuLGi%92wsPmK zFWvXj8`pH-iYsw4Oq{)cJh`&4BifBvHTNR($atQTp}4eCM-vCVe;g{Tq>fMuQ`4|5 z4cpY28Y_`W*i_5AgaL%HL}~&l1VJQlyaOePZWxS@P2;&81`p{RJv&7AR)fdxnWC(D ztlU&wo?pjK85p{S*OvHh$lyqUTp>r&O0v7VgYWw2h9Hy8&}ub_`~XcVEF02koqE+} zWFm*1(8y*DYGsErM@IR%Pd~!SjT?Oa`BmPz*&vJ+)8jc#p3XBgmtjk^X{t7zdO%ze zq(*IaZo8DG42GwYEWXnrbWFzQY>wVL$fdWJuyTUlO&x702G3+*~wv2NrO^p5UHi8*PAr!EeyjXkvPy-1ip)9=nPNI zaM#1nkWA+>5=pi;)_CD-e@xfup<9Zc^uPlcEjCCeEwnOdzC>jMGz_}^ z08dDoO`mi@BQ>fB;)q@+ps^?k`-)^T!NA}Up4+Eht)uB+ycf*k_IsF?Ad}7U;KPqI zH9PxWT${$_*Pq95w>fht!;;%)wH;9?j-wUU3HLng0TbW!7#=E+EhXqU0j;*ryI-s_ zb}CI{Eg)zshNcs};$Ws1c&RX*$ftXqMkysuaQex|Rj1{NFx;oxx-Vyi3|YK5KLw)HtT)Dhy!trdC+|X#c(SJ*j0w6Ewd`uMTL zv=Sg=a<;}`+QbwF^?uAo&E@Es3uMv>W>1`9_UI8VJn;-C&Yfj+avW5|;S*;#{@7F8 zec=f*xjg8wwSSAN?|u!>?PG8-u}=t%pywdQ747vdW>paM9a>?R%3>EogX*5c_D&Zm zBm6L+-S&{?f#ApUBD#A4S|X-j35eo=cB_f+_le^eDJ4otq9`JYA~XYn$Y*VJk-&9u zye6wFw^-j?BQUa*D*?9GM?vVTl-<%5X!1;ZkXj^Le~vdtdx{WhMF#g@2SuY)Zcn5@V8z7e(hI&g@67_zx)k# z9~}635=Eizd%md|Mxs%vxx2wv3ODvX`vs?IJlL*Ow4wZ=^kesZX#Dz()!N4L#--)? zuYS-Eob+zPCyo*evu8?)!08UFoYVqWYGI&=WXuz1CNZSX;&zWnDRiM>=o+Rt-~s&L z_dLN*e(F)KUAe>R)($91VJJl=XOJJwpeHPZX<#HR;!q)UMc_*u$D`4#6Z8%$Sm>J! zj~(Xvt!3sH*Le2v`+4~E32v@#6Gss#Q^S-H^t)8cZT6}?svVDR-y@7eEM1TQEM1|L zLaLa|;fS~qA*3XglRW$5M`^ls-Yl=P(QWbYOdjiyh9)AWvlazYu)6AVYpuifUVv7Y zjEv~CBgy~A+L)gbI)Vp#=zLW!Vm1@rp4B zOyU(U5{OL_lGwo}7?5HR5=ekFGaAi|MpMt3GpCom_u2K``|a)N&wb~^9!UHRTfIhp0J7)FwU8Xf1GpkdXoM0Suk!y z&Tuuxv@(=xlk|Fh&x>3N;y8h>A{s~{FCrX8m}3Zn zh_nT6oM06UtcqZF*8@u;LrJ&elgNCA`|+9E}ij+jk|sBZuYS&20QB=+U*hBD<1iZ&d`m~vmt|aOt&*e z(ZC#$i1U!KSl%pER^(zIPLazbuh?2Ak`){4amB`q^1DF=hyt!@}M^;#`w z)~7Sm{qXL|RJzz0HHHmM2&GUlNSTC6i6m4?fGn1fmoF>fIO1;u0{pEk0sqlI{-wX- z`2z@n5OJJT5t=^e^}-ozG5q?q-#p}c;d5ipooWnXKK=9$iEqB}O7zP@vtG6jQ0na^tA9q%^8TVcML6^?nh?PNzA14STmZf8pP)!?{DV@H;GK=_d594Cr5uh(`i7(&=wRBqI%UlWw=m ziL>`ot4}f-4LSS3!z>&)44Ot|Vv32W8B&(UcsS(p)oYBUMrH8?+0qo{`W#B=^jdp# zwpK_+15%O4YU3&!ogr_&^ffwrd#G@qnB{p9fgds&_>9I8x}oipq7p^g)R72!{SXpK zrmSJb=6>jCEXm{z%rq1YlZm{;IFOi@z;<+$0$qnx&IFmD>7Z!>p(&(NXokS|L!^|% zNlY9|l#oaRP!RbFJELJ_A@&uG+djnuI?db5W=h?qQ<#z=vaGCBB2iEFQQ5T=f= z`SjXjTI>Ft8@oNku&$+`2zk+(JF%T<}Q>MM5M zF+$%9l$J<^ri>tvk!7JxPos8sS*Xv64|)pt+kgQ7-d;f|M4Tjs(6sYW819aTqv-#9 z{RfVDf%|>Kpf_XL`MLY2?`1A`fPeG*zt6FHGU>SkBZ(z5)5{z?Fl#SOXJ(dWD|Vyj zqH7ut9-HAaPb{FxF>hUMb8TmYCLre+m?ZqffATb+dh8hg{HuS!)%Uij%x9T7@1VtL zA|Q|&{eefEi1!I05xyVcx*_?ZiEikamc^+v_u&O2q9nqw_X9~y#{j7qxPrB{ZOU1b zBL^mEwt5tuEHa&8X`#woJ%^KVDCG@~&K8J(ksDw-Iw?cv@RJTX-9qo{ocqi?OIDf2 z3#+szLgtTFk-DH4_$(~VbK}xBo|n+ujM&+YnSRP4HIb$#BW9kha^>?C{hGIgrOpc!0#z^X`vYgnyxc7J zwef(QZ{nn!_hFjJuot2dlLU!j>ImIH2!*B;X-8)eDF`FNC`J%trv>SXj^9=!Lya&I z=rK%{9JH*1lp58EJWkp~(+q6eKq-Zj)zNf;CIp#M8sC#dQG}BFJ0pJ-qZot29zK*rBLgF!HtNqmQY$UxbxD8=I#L9(U2lxv{7|JGWQ@XSvMScIxufG;%;tB#&LksVx@#cLnfe9 zDurq`6;kretFQ6+@$(;85%|Chz(4xMU;2LoJomT%S|VjS41#XA)gCo>8**p##^U;B zV=V(<2Q6!a0zMq5OY0!`BD4Y+k{6}Q)T-2Ox8iSOoVAtsZTIHJx8avM-cb~qmWV0XWaJ48xHp@&tYpO-+t>lrx$bd2P0B> zn|L(jKrO>`-Qn1DmWR$9=J-;c<>h4xr8K%0Qkaa%C_(K=o_oIgQMz%T-L($4t0Qu` z0=wn>-cV=85+HIR|G*2>}+f$v5fU=cP={Gc*&TZU3_NA zDb+5&_Zt0HgMMd>I~wDL5zoKd;=;`i+ij1Xp2zjI7B64g;N9&3m)3^#MlptIa{j}U zJpSnlwPKCIri%|75~37BaEJ8h9JIHZax zQpFU0ERn!)JYcW6O%QsNGFk3Fb%akpbssbg{vmZXnAa>cTro}IE1F6@L-PqMISM(vvy|>djd|TmWl7WSO*o`*9KgF zZ2%rP{{AgCO!n=y69s5G*-VyNwZ=!E`vl7e4x$Ui&iV?jw@0y*VQR5Xdt-~)#U=9j zJm;TznxiN0pk2v$?^l{~C7M;l%u5wzjspe7i|7b}3C9=6gQz zGb}8fLZq@BIdqgH3@Mee6bb^8gsItSl#EHD{f<9dOR=yp!Hta;#i~ORnQ=Tai0O_1XZ90W%nB)o$hA=QK4b9dlmK?%xzstv=#2-eOwt-=22%+sm z2SYteAk(moKg~~L#QA-*Z8-E^&y`p6n?}pu=jgm1!B_h$J zkfBtXuGHkD!uP@VS*Xo@U<<(yR11(Y^}i|t04UG{KNRk0oHTcvDA40D5td^u{?JGN z!S#{r{)?AB|6l&k2Wlp*NTO?sMnB{)-f3YVnJZe%Rx?Z#1y|Qc^pY4EDWW*yz7t3I zk&>3>!`4OwPTq=_`ynnmWwV*>iY}0nK)g-q7RN`KPH(PZP%A zq#Vva@-)}p{T9Vag=)2iJ8X03))hR@BWFv&?@O6U}{kb;#Wa0SJRLF~!>HJP^G zsq=XgL(@@-#Iy_)I!b5=Lr|YB5DXNtYqEUoFt=APk;py5K#;O5j-5YEzu!S6f_u+B zNvpj}v$;pBy}|102AZzp_CrvRj3vRIU~(}<;)i6{4H~Z}ocdm!tfsT0x7qkgz>%38 z`mSK4#dOyal%#Utv2%OdcP{i67H5k|s1vkmD@J8&snZG>4PS7V4wv-AzCBec#bzAj ze_r#oUmdr06J3{y5Q$QzCGGJ@x~3__(W6Sf^b*3dJ}@)U4;}*i<6nAT_m3`wP+RMp zgkhu&x;@JIDg8@V|J#X~@~O`81Lx;o{PRoC_qM)%ppZ=;)6ERmZ>{hbZ#B_?nSza` z3FappCTltRZp>EKXa1xeDRXB^4V?WDVRqF{5pX zY)c$z^Qp&9;f{UYy|%}|_vx;Bq&H&lKqc&Z>nHnDnPnK9IC+Az_ufZ;ZxciLtXz4W z^n^~fVo*GmN9~zZM487v@+oGPk5H~nq8S=O-_IuSeV^`VmqDjPkc?>$*Qr#d$fqWm znK(cKbXq+g{m{oSEuHGb9NqRVjXT%bTe-^XZ@+;zXwqz~G8$~+I2xuc(2|&(V^AsP z7>A0bsT{R>nXKiIBo^gr3DY)7ITjKWf{+gfer zW~h#v;XuTxmX#@H$}{&Jx&8LnR#ZS!Bn(v&M+gyVDoHR+X-i~ennFK$0(JW~3lsDI z|C<2*zfXaXNumb59&wz=a=n@w4}0p7mGQLXc%$! z_)-4hPkn~L+6MpiZ~hxrSNm8JoJ3=;l48^!(eC=x7K)rZGRsR}c@q%{+M6zwf=$2S zqa_KGc?UbI)9(i)T0-$yk(K5)sZ^iF3M`zk84m*%=9Z{0XxzHeVt23f(Xhp=r%SeRp%*-gspy;OiIJl zG>XL{x~5~#AEMvvv3+L;Q_~nHHqShElAU3my}cgkyhWI3coAeXWs)QzNfNfUZgTp* zhtQ1_gF%zi=gtxPF6Htxx{*fH1&y5zzVyex!^(|S7M3TOtRKg)bWYuKKeumg-~~PI zuCC*3r7$d;$QhC$RBqnEO ztX`)h<0vMUp?3PflbPTDonP%Y+IK(O>9lsH&!qmL3L|zqU6f_BIBDvWwLFv69Nx&M zHj}2;XfRdOm@Ye98YZk>-sASSeKJ*xieKXH#xC8SWcAidOi#`*9D3B|O~OdAv*t1? z8%!+hM_9l6MhmBC@b1^w$V^#;DW5^h!8QbmNch-e=lOd-^a*ZWev4oJAO0^oofsz; ztPW#pC5s#pfr?4hG)_EOrzSlbUP4CG(2)#>K7pHn5)8Uy4lHCSPU?)8OjhhZrGYdOV~Vq94l(j2=n0yoHnbS<)$s?X6w(lpzTDLI`lZgXGET%{= zcDVBD787}cQ*#b}TG3w*34?@02pU~UUKex{g=T21-@JsT8?{tv@`bUlK2k{ssg!O7 zUO#GTDQld|DZRI~>J^se%lWCpCqwtrD4{bllZvmRcpM{r6GD;1iD9Rd)9p&(WCRma z>Jk48dMf=LQG`FR0+6D=to>qw3tFHx$cdGpm3ZoM;N^^(iepFG6tf40rZ#}%UulYyC# ztxE2EV@#%K6B;gw7x4WbeuAHV?r|=>`iFe(e|-%(2nhQLE901v5*#ulTk9R{;|`CV zTjn#z9^$LN_jRsphny{>2sDM~#`K1YSO{trlh{_EOj3=QGIj3W{WimnKW+aDxKq3K}i8fi;V&ZTg4jho;4QwpUb)!H=WnI#S$d6ImwPDFxb zXVHYlaM0!E)+H7uj*)XJ1d&G&$9RJwJ9~Fo+q%t}lMi8A7Q_As847lHceuNLi;LG@ zVSaX*L&s0>jaR=!ce6#goaW5oMKbvk51l*1mD}so7nYe@SirJObWKtyE6NiB&yC3z zv*@LHMl#RLLJeUEy1g;&?M()|ud&Nw-J%Xri$ zjD`${U6N=-ECbS(fu`G-d5e%fvF{N^9#&qbH0jW9yTn0EDsAFa%akisJbz4nsz@$d zAU67Rh6yP`PJC>dSN>=f5$u0VgGjPmG>F1|wP~(cq0`ziUAI4_oYHQ4{mqGLUe&g? zv}@9lB&o>I=DW4Lzc`qfdAhoLaa9OksKks4nr@Ud)JUQHSZSeQXwnW76}ltgOiYuE zN4)Un*S;g2zy}Qh6dQmdYnp%w1LU%V$t{ORHYPBVWucib@q zN(HQJcgWh#l}aW3(8_9q;lO3188P%FrY@+KbP6*T?SbGEzfk4tztiE;wX2NVF?!Y{ z>?dUM8f#m((RH0_t;owa2P84P_tqNuib?Zs!pv#dxDwIZO0e<<@gTuW>pXPwBtP-# zkMWH!e}P~7SKlC;X!x#V#g~jF=$ay*(Mjhr;2S(sJchfo%NO5ka&{_%jbc1VP)Z(^*07*naR1gvQAy=+nVl*1?>hL8VJogwfmaN^q#p=d&?yRhm%@oL+MXug@gIp?w zIaAr65TXdv8*}vJ8R`e_Bc02WNgL=II4K*`v`|VXjudey8M$Ku-{tb$N&8-AID_K5X=EBQ;s$~nq7EH`lvGWFn zuFcpAC{?m7%^slbZt?hY%jg-O2fwFEX(CO#;jwbTC)NU_s}Z}3qz`FL;}?GVBQ)<` z<5&L0SIH)Vt!~0fD3JnmL$FY`S(+&jrBZzFvk!A{y2Nk%+m~rHeHQWtG6Fw_P85;P z=v4D2MGLByz_JA=o;}U2yIWjudmKJ~m`dglD;w7cg9s%A>6}G>5YfKl6Kuy!&lTxz z4ai3V(U62A&~$;68uxzpQBKayP){u0_{Meau8z5X(IJ!yKUVmW!jB~ife>IDfIDut=p^C7Ught4%R=@E{Y5hcF!n!?ZDVlPHc*NrG)<(3Fnnd$gJ@{IL%buHU}G zjXT$vn_J}Br#{Z*%a`d7+H7pCaChe>wQ8MmxyE47#gi^xFd$#6GgX`C(6M`2I(mk5 zu87cddc6VZOqz0Kl3byNX{FGFB922s-y;kXHdk+Q?efcHGHG6X>1F!6Yb?#x7!7^$ zX@j}NY4Rz7i~}?R2Eze{l2~br{8Wax=d-&vCI}PaFv7_Qys^))>yxQ!7-@k!gohq^ z3L_g(S%&VGBAv-_{^2JHf{+u(AL813m)N|%Ly{yIDS^;*f?h#{R=^tF0snyqZM%Off(uF4D!1dF`YAREj$Ym$yCUq;N z{6VJy!WEkCp&6lWn6W~ax{yjq1)5MsDlMkXj9PDR|3?voKJX==z8`KQ-hJb3g-pbu zV~365UeJDZ^Y0gr9Z&t-=Bn*@*7(`A8{u3^Cgv;gRW85XWujCZ<;zy(jT;??ZiJic zQ#$W?HV;a$bECuTa)FQiOopA66mv@zwzv9hu65CcO?fuU_>O>yJW>gK4_>)=6Mvkr zc5}e1f6$||8S(IE%k&!xrwpr?BM!{x_}L$RmaFevGEamO(jdka9FuI{^hdL9=zzMT@jya(eL$ z7hn7W&A82$c9FZSF%wY%q9I#50fmV)(MH1boWsC?-Q59&11XYWNa{{P$af~H{Rbb;orgkitMcyEM~fD()Y$-s{}IAL?8;bW&ACTHd-l&f}P7QV5$ zyLSBiTw!La#BysWueqaPpb6!Un%kD0DJ$ z?cXxWPSoy3lRF!M-f()PQzq|yt2_Fh`woPSRwuu{J-`nnZYW9T1q;WsH133)d?JU^ zLw2_V?2N{+|#c`7D^Zp^VGi~QvG-OoSypT5A` zH=2Cvo*KQTj~__#mO+0M6Dq|ZNLZ?;sGYPaV^FD=(Xu-4ymo_Ar}Ch(=(l&#eHb|= z#W|Z{Fk<{pM6ebS1u^r_<|vfY_?JAQ97NkO$Il(3T&dC6+~$Q>Z%_ym7E(GXN9=E2 zEJ3pq6DyDjn7SbCXcW^rm7GnrltuY%rVmduu~G!%xNW&Y4f&OY)i=bw3w znT10%8#{DceG~yn9MTHIM!(IjS#U$yW64X1&or2VIa|^A`JI+ z2Z5I$eMx^95R7ADU*eQt6!w^%oyI5zRLhD%a+A@hMYaImI7XNTNU$uEaWkP~0VF)af!=U7$xA+ue{L5ex$fX+d#H z=jbCvWKLq_VCm#hdV6h#{UJ+AFmNO6lgAhoJB)iSgPoZ1odB%^Y0IE!8+4-(L&eO_ z7tt4lUB&-sX>+qld z=-*>|yTMn!_D4MX)OS-V)u`7e$>nm`PMUJ5LM~S#o5`c;0)OP=xh{iFm-Uq!xMPo8 zv9=#u8lLqrmj zDNV9`@?lOq^bro-^CVLTPb0O2#`Y?qOu#lU%>C5k7y*eS^b>}|2w@3owG_hK*9s^P z%1ai~3Q3g(Q8cFC4w;@QP@5{#Z;x>MisSdqVmL0nmWPl!XCHi=`pg`fVY0iq!Pxci zB~;RaYAM6+NRfmdh1#^Ue&gmJ2BF?piCdeh*$YJeT-P;UGfitOWYRM&%QI40&B_%Q zg{BE7Us>>aovSLA9%zYD#43rUl2QqwNhK435NM{UX0zr0V4Cm;3IT)=e_aGNZ?*K9 zyOrc0{Px2KM5+C=L(e@Qdm3$Tz}?k8I5C#8>1{-}o;q>x*j}^CXz0=R66HryES=0U zf3Jq7ERG(V#Z_HgFXYCh0okg}D=+Nv;qyy$JjwPI55v^yttIp}5_D6rw-GYgEHe`+ znzgdh9qpJP0B+1}aY$;Upz-L-Az=H?KZf$gNQ zEr%co@I#-w>nkL&BAd!0svX4%yij*oGjNaS$>hYefuV3ti7(I4QpV z`QM`5X`u{35cw3#OSJZO&@>%)9AKvnY|Ccz?j0J9Rgj8IzQp0v4{+?vgDf9A%le(` zTzun2`olh^f&@wZK#^W!fD$25q;OS2brH&yA{Sq1V+e!YJ01rrRWh|aZr`Qvy3o(@ zV?X_CWU@70f90#xXKRdGMP7OSD^w?E@xv7|*({5P4{_`A1!N+5Z*#qK%wc83VifTPSDy_j~_n|{LO<#|27~1pZnbBuzu{n z*Kfsd|C&`Df3ny1KGx}tRO4dkZFh&}?6CsTj*F7Ed;5#Q-+l6-nV)Jjdd}9~h_0(- zwybOSe^-%o!6Xc0+S}lcV_yG@4%L|~{y=ByfME5OOD7%^jiGTZX55$LW;HYejy|4Z zZ#SeObo%|6=H@;X&eU{1`sgwG%^hC7w8N&{SW4O^o#c8k)vfHzmIsOzP>QO9J z7ld?@V8(XxuZnJS~8z*IBSQh=xfPSw@7$sC|bxg~oRH-7RB93EpVPd9ilm`8F z2hZ!{yDpRS$Hhn5&6yjvSbzygH z#F;bmjNKlk>5#^Dhw16F=qVlL7nz+u%F^M}eDkY+L}PQ6TbJJ^U#wzUI)OjhHy#Z= z9=K1yO9PyyX%hzB38$*E)ya0UebDsm? z;K_-nYICXMZ$IDoP<7cUx-C=H7Yi@6#+`%o6FN;v<1POi^?XLm)zeR0zrCw9yCH)> zYMQ=Ja_ToF^@9fEW<;oB8n;92yrkD1lL{O<;{aho>rR4|5o9VFZdZ~|84Pa<@`oYl z8B}ePowmXQz}ITK6`-O!0O@Ph=`PuSV=s7@Eqguy7g zN1p*7``#ZWGo2wG37n#ZZ0@rN(-{j>Dog>hrx);DkBe7!dFFVYnW+qh&Yx zQ;XE67pYIpqm*LM>0sD`$@!z)_xLBNOw8eq+6WP$D~+6EG4?`EKDC6N6C_wzrj221 zB+PXpIxBW z+(HP!Zad`2OqOn(L-+QG$Gg09wL>+R&Llx>8>Y#qKQP;aO_{@|SAM)j9jeWFlV6qnqz$Vf5lMJS*)d=6cCOixxgcWRz@FI?lw#sJGSaIz+~oXJ8pO-|E@<9&P>1(GmGICOA^ z@B8=z{Hw2C;Lfc+A6d?m&sYReLZ~FMp;0}QA;>_~mDmOt_v%C$ft?a;tc=lhjqYy9 z=Jk;AKoS$;`!V%}DlK=&g}1LWjCPo;o#Bc5zL%AEUL#|IzGxsfJPw|}m)WJ$eC_$y zNy~`r`$fUDbVAdl?FIxw@z4|Z@WkUsNZc-de@ttm#m#ru`NnHoTZt(W=AUw)AThY$1CYp=4| z-J-v>&EoP=mJXg|)bG*nw3t6|lDWlYX6Ba(VuLqc{8PrQO&VQ~+4)(PkDunqsRyV` zPBSqv$EepKNx&WV@Vo&Uf?T0YzEGxAu2G+yqf(tBlgU%AOj4hoVS4T`^~rhcOpfy8 zEGHlOD6$YR>UFUZsG?3_hP1YZoIHG-ot*)t(!E%=!jEEfETRC!Ks&!kVCV+Dc0j&t z5QYh%Quxi7+>(b%Ji@*tjy>Wq#I!8D@d!wWBMC{!Vm-~9m)58_2{&31YrAeTmAAC{ z=?uO8n9bd=A##VEG_Uo@aYDZD@>A@iQ`Mu`gft^ciVatEEvC{sH z*pE3dRV3^YRZgW>2gC89v}TA`{6OoE=2O6fMvv$Sj^dl9v)&@*W6&Q{^^RAf7_fiXfeqVnkbsuth!jP!Id(UDs;=sqhp)bQyz_MCvv+>nCP7J*Y#_P7 z!To>ldDpw%z1O=|C*+N{TO9OL<{J*rUww*Cec?aHDK22U4tpE7*;%_uCLJE$d5>;C z;NzeBD-_E$zVRP^hjO*f`HN4nw7g0X1pLt-e1lK?;-_h~ju;I`1fkE));8U4mxKKs zKK~bgmDOX%L2GVae~;$j5f2~T<<{f3$$}Wew5hozbQ+O&9ZZ9X=qO*J+Z$lJMJ%^K zrBY?SnnNP_@f+X8Gb2o)84d!J)>Nuhyu3xeRA%fC5K>Z~Sz>F<9(ErWI}Opw@1BLqT3yl?pTak0dbaL*d|V~2Et(H@m#I5hB)Ul1baq;m(FaN`7$XjsK`h@c%Od z_>4z>qxwKFn7noWqQ8|a9b4gv>THN`+`}97z zzS0^jOQ151_sXYTkDl-v@(^ZVc8!ko!7r%Ol# zzMmosE-41ul>F9z_7bjXQgIBLJ565w@e$oX^QmX2INtEM^6}rGHh&UnnDn|WHrF0- z?e*8_^$w7BiN%vo(d%`&_Rd?(%q>u>O>yDL53#?u%eVjFoBZOZKg-7Y+Qc?3OSymd z7T4cleOc>N9&YPzm08M zH21d|_B$LM?y)fZ~)xzEL1o zDzV>bQ7~3GcJ?Dw$}`MPe}vUjA0wA9bGX++WMhgAiJwHo4?!jdj%6S>G$(pidGD2a5~*Q?XZ|Itz4 z|Ccj>KRE`x{8#Jo(y_UJa_xIZ&&}0K_wz$*QRJMnZa)>O8M7O7+1VQ4<{a9sfWwW5 zaVJB5;MQw9a$*rBG+7M8BZdCJyRWq#PxibO^anA`?KYOqkSZYOSVVEgcpR{^v(M&M zlfzbzwe2p4y@Xaj;`CAt=}J_RVkQ~OFITV|FuO3tufKAUr!LI0(LCZG{=3_pbR0gl zQle3_!GK!X;`UxbRCbZY1iciKIf~hZDuZ^4kOIRta6EzSNoE(T9IS`riWa$&gVq_% z-2h()NMZBvdK0&v;@S>wUEq_#tcx+uh({@{<(R|uE$+O#$48f4q9jGTAWQ?JXpsoX zum9G|%uN-E4)*!_@899ox7sW>3OsYFK(XLr<~+_^{R`N+GD^Wy9 zzw~R|y>$bn1g2?o^~IMtaq0~FyE}a6oBt2bJbRUPt4Wrngnq!AuYH@Htqm?e^DLkJ z{9k0yA9A>Nz@3{nczEw7@85cjhZ{EtMj=_8fJz9%7_CxN7Ne4goabN|5Qk$d+d-=g zAtgc>WJ2SJgeb}A^?gp9UZ7OYAxH^^U6cqhG)M^$k;WlO5MvsGaOBfESZ8Pb78?(4 za=5+5cpMo+i@#rE1w`rQGjkSvwd=awkW&M@5GWx6O}umJ94+_cR)3IW=o zTs+Uh!X@?|KO!1OOwS%CjC+g@hiILWj>e=xfMuD4-4;5FNt0;e9g#?$Jy+-PPLE*< z4NEadG-0ZRN+YH#R<#o*}i_OTAQD6sBYF!`x$Lma6*Mb9Hg^_8Nw#5%$E+TL>l&Epj4Z6^6ht zC0PoY(xhoJIeiZ^q$$XhV4O;Bu8lZsMOa1`grq%87>5}`Ob*~I35BT~VVZG#(c_s5 zC&)Cob1sD?ff-8(H=J*ws?1~#caXi#giW0e#*29S)$n;Ksaa6Z1$MBWHV}N zw5yqFOe35wPpqEj{o8lxH5Iv>&DL6r=U#e(hYxl+fA$n_|M&)@4m8Rh8~3{$JxWmF z5T~S>TCk~?PjF@RH1FU1Hr+*=vWmHNuf>@emx5~(lVW=g-b#t1-9BIZkoMR=q~M}eWcOk z%fJ5DDc7p>yItP=(W?x*d$bPskpga9`w>za40=t<#WI(#Jj>43CawD(92@`uAOJ~3 zK~$p_H?F zlqd$6*ppY3;U@!aP=&uA^dSsmaM z0*a+6OhZsA&-0lt{v&?pU;P8(utj8O)U3hiUV`-F4`j?8tXz&X8sQpXxHiGqC(K-) zdm_iT-tG}tCIv&$i87*8GgffAEIvOv%r;NHkei=5CSKcaJ@^Z{SWd&?^#>|-Ln~jX znr^NbHV+O8$4)Or?PgEuppP)3iKDPk2%%J#YBN`mX}_n0`RAATe-Z%vImJK8;L%$A zEC^_AoIU$e%^%;0-_#A(&tF+OaTNBK%b`uL7vklGy8imMv2oj{e$pk0^+a2(1ePO7 zQcbyRB25D!0YjiOg+wAUkix(a3Zx`enoJ0G+wr6;C`e6#Yfr*9l0=hBGfvN!xSzLq zwC^)N*W%*YRnj=*&Np`{84gG70k_th?6niVa;nT+!yJkpJ6;2^7{Awh_y%8 z5!sl2e~h6MbdZw7DW<2%<#V{XDuyW;58LQ8AzyHC91v+r|8NVU3>87nvzR-vNH2t` z8JlW-iTmrDbUGcOwS?o6a4lA z-uTu-Ov_?^rpS%gcd>1Q2OWtvO)9lIUfv~tYKq*zr*rp!r=Gn;IvUdK-a!^pD&`_u zndAy(lyb3b!Kc6ScX|KLzoFgg(AkLT=M?(TpkT&$HV}tcQd4#BnXkAwGxI2LS)YLtccr9fIssr;LjZaenRd4tj<5d--#)~ z*S_{O@e>BH@|3qcD0Tn!=NlLP#y7rk<8S}ki=X+Qy{2!y$tJsZLfvYP<+&H@jK?vG zjQ&x|+}Rw1-2@>grj1U)Bp79gOrukc?OGT}#0R#|rjVG1fm9h;l#ylXgMbe~C1)@_ z3VHkP5q2tgu9%}g)YNMgVij}d z$~4_}NPiF!x(?^hRam!u=AJ0yc?H_N4*8OpB?x9R4Ul7?8z+T*1M#4Ypd> z*}mVSP{>oP8F&jZZqddMLS`=53=cHblY;fbJE*NHD~)L$z13mL%DD1GjZ{Gb_!?U6 zkWYQ_3bm5U;e)&Ui+_8AO2Om9$Lbt36OLLjz9Fg1mKa2W^G|;a-w$|n=LVhTAt%mW z!+3aQ(+T^TI0}KXrl7_Zbhyyz%X?<9QzQ^9$%Kp?$E0&?%K#otHoPd7>!h z-Pitzy`9@gHDq(I!(bTD_fx`HFb*>uOENuEp;#*7ITj0x^GwgL5DkwgcqTKmH7uP{ zEO=O^$<%BSH#fO%8+AvR$q`B=2)Brr_b62g7)FL=2n=JAGn<47hUt+n7Enr4ug_9# zOp!zpSq6C%qCg=f*tR6i5-eLX7;7Hybs3L`_~RkZT{=hHUFV%QA27Xif_`h8=H3p& zZWq%t(N=~O9_UFzv7T7{u()uQWGs1b_cn15V+INp3sfpF4TWQy*w*BoO_K=EF1?DJuD~u${E%%+;(j3Ibn_+>f#+C|DO}UU$TC!tl4TQ_r|lXP zat7s!jp>;<1q-1CrX^|hGm1vSxsv2`&7@wixv-qaAE(^i8S?Z@o^w@?f}S8+mPV*J zWzZX=J(DC&h=hUa4Jp-ZhPgZ&kN3FyUYj%#L}AS0X&2KJ_@e~ZwXv-Nxx9rxjM;lU zq_ZEBtEU`aImhFz$BdLuza7)xPMALF5G4Uw0xFA0nhE_*fsbCQflld-A`W{Iy>ZG5 zpL~iZpO`1?wfX+54|whFkY9ePL8J^y3(JhMF?P*mkWGY2ZmvS>V4Lx%OD>nEbFf7& zm#4kI&PPA}RV*p6Y>PC>c=-Ms?B2glp;)6ax5T~cKO&1qoWA%pmtS}V)6VhMYkx@C zdrZ4?M626p|0tx{PnjuLm_lMmi$b+Q8joqz9L}Eq5cPVQB-LDg?!z2EeGUVkLILKN z8qCa?tRDuPJJBHTm~?!BCZ2?i=4?vk8jdp|t|C(yrp9r=^IXEQ4?vbs`bh#mAd3RN_|?D0_S$3Ke(O3J87D5iNNIYN z`)|L-{@NoB_IFsnw?Vbhz_jd%!HOZt(u_)Zk)4M-Y(Ll_Rst;~y(FV(2`s6wEMu~m zWk`nOfaB9P?RFDC)a=bt^0 z+kb6&rtvQijLqUvIKV5qY(E$n$Dgbr+>CtLV)uQ;(OSU#84s;AIx+e9->Q=)8h&Ll_A{P8USzgnq0KhvzTc~hmr9l1EMMlY(&@ku) ziYU<(Ds}d^?_*1a=h)8&kNpB3~#_uGMk!B}_xGc;X_@f9x}8;j*+)BlO4gf)v{_u?!Q>^GJOk zX=sErF-;TegEeW(E^+MKlhkKsIQitWT>kjyFr6y4YeLZFpc}K-kLdd$Yme^o#QF2Q z@~Kbb$duOMAy##n!LY@>cV8z>G{gRc9g18`ZFLqyxEO|wR*=RSS(1<@30bPSf8$-^ zG$k_(q>uzD)IM0$5<)T>MkJZy)bT0az0>5>f=9EL&>zQ4S0|wp`GzHOiw1_$)5&M*D$~MpMAGYM zVHiL6M&RcHz)xoR4>tS$Sn+EhNY#n5W$ffX_Ua2-gkPAit^VN4Uz=ID_jdb#|JdJ| ze)YrQrN7kmhg;ife5n<+KehPe9H-Bm;=!X$+B+dr$4yp_%^?kmQB1k~sW~3~cz|Qu z)Rr8MHWNB)Ay#No$h(-PMgmC?k_0Jwj|ABX5J9w@_h5s5C~^Lf8->i84(TDZ(J7;2GFbPUxfw?aac; z!LjotntLPezdJx=6RR$1We7v@<~NT>vY1>6{87fkw+HBa$Y|`7iwuf}Nxp4TpDJL^ z8l;<=a6Q8wNv1Csa4nNzq_CvPV4Pw&Hdn7q({CQ~t#91p&Zf_=eEKvP&^Y~3Hh1^g zKWLK*6QwdLORS{nF|b#c^F~)AJm=@G`=3F-?Qx=buM~ZFG__?sb`3JVkwO2Cq2H z@r%!K_PI}S>V?nJ@3&Dwha}4|Op{h?i-&jK$M*N}gOtN|pPj93q9|t2IlwSo;xJ&) z3VD2YgU2`Dr(hdcP625eq;W(r8sj*59^AY}=#TO8MU)UkfRm;Kq*!le>z#{=K{b7)cAV8o#0?}>mM72_`QGlSKhk!^k?dSa})+&-g_gteR=N0KYBFo{%;qaUin{i zE#RPi#M?i5$kbvLTjt2u4eW{{O9c0R+~&p~wwO90S-M<6mlJX*PR`dE?4(GMl0^xl zAi>Q$G-mR6wxm&aF&qJwL@14=1)gh>w+&oNB9)*dp(r#Zpdi7Kax%whiDODio_I~Wti9y7;nQeP3- zn#8e5hnhH3)Mv^(d485*%jfO4w{a^4&YvjL8)tNU!NZ4->5l@G0#jsorp7fUr@h^S zO%_jGX7l#j#Hm85g!yA<5PGsF;dvh8ewT862Bihv<{tIgWg7E~tgYRl=MU&~+U)KR zNmDp`YMDzc!dq}OZ$l{5o zsm+{12m{+Iv2g4Xh7c^By^7!4#jqL#VFH#$VPM)GS6=uymtXoME5}aa2OV~I_NkXW z>LrK6y*&<(dMungiIfIWnBtEHs5C_-DT8rJ9BHD~9x@G(b`g}o9}L-ec$d4^-$JR3 zG>$3P8(4-x)sd{6Jw@Z}^EB7*fDm+s5$8`;LCBzt-Mf>whi@j2q8=CI98Ewf^5*Sv?-S zQa^w5?!&E_r=NSWyO*wg#I5HE2ZHMK6n0UveyhoaCr@(JJ0wvt1}?K_UHl+n=tqP{ z3Da?&D}@Fp=1RPC=MZTbEX;d&jzGx_!!$4~!FU*B$C9EHRC5+ZOH!~UMN3jMB{R09 zY)Y!GNyV|SEeqS2Z2cAT7KUr$<}3_X;)j}87$iaxsf>QBhe`}A6LOBEk~1kgk~oue z>YBT^`*<~*N7nOF;%hI z+8;7~@+EG+|1JoV(`TN9WW-FpilG7wp{dU<)7)NT?Dr{`JPgZY*lnVcm}g%30!bY4 z_|`jIeEG9n|L*Srnly=c;+dD|?XT1A4cR@2ICpl5FMa8kdGgXTEFQZ+ZDyGu2sqkr zvcGo-(#5iK%*-$G;g7wDsS_5?zQDri=cvsc=lF%^Nt2W$9uoR}Hg3Mk=G|}8T>Cyk zM_9H=s$%Zk-=|_5h$J8Z}<1+FBD6|p3ln4 zEJi*mtK^t)a?{r6V0$Y!xB3K9np!~8?`@z|0%Sl&mMN{37D_2$NS*m(09G1He{2-^ zQvmSWzx~@kkNW@Qp&x`f54U}N@Xt?Z__f6|&ed)+`4y{R7v6ny{oxl+KKH}sp!ciy zj<#+(we0ztH_hV*hxCF0?RJ+_c8&g)&(`jc#!?=)WODCXi(V%LCk1V=^RCYc*JF9M zKzAGviwt2xYC;@?PA4%pnV(^q2DO4g!4#BD$yCmuC^R#!NkK|#hNSE|RErj+ib1*V zar(*gq}`55 zV}a5c;Ycy;r3^b6&8>vFQ!Zhk7&KE%Q?hw8WahYon>Q%dY*sIn@gu>h^A|BHc@B>b zaZ5D{<^tW#2XrGS;W1o`h$4j^XPi7S#Z28pqyg`}yUTVTe&v@=aMTNF_fy_~?_H8q zAkkd9{49-n1C@+0m5*&G{Lzr{u!|M~%gduO#p<~a@zjgI1OkH50MjtBoE%y!hP@7z zdV_Jd#lhwS$}XH-oZ%Cn|0<4~(n3-E9?-m*Mj=1;W8k<`! z7M4~>lZ4^0&F;<`1$=~;=fahbVn$u&syQ+ZgHc2wZ{Zk%*{Vl#7~_W#1y>@HF^A1f z4i6s_jxvsR_XzwELKs}(>x8V%PthBX+1&KO5L9MtTKgfnih=2fNr+|5 z#4rWYPPq`~WLn#IrmGyM0EICCm$dNDBDORe?EzB`ByTJLUp62W`A7^IeESNcz)`Vfm;qDgKfAlTt zGqW6j;zitimGFz=lUAg?rf7K35N$q zSayjhOi98BzuzKDW5QvNC>T(x&f^stOieFRFXg#^>jpo3^}8&suCTatlBceGm}~d% zvhjG6?Z*#j%&kzZdG zTB|7hDFFN|2>dYs_!Hg#CxtHo5u}+I_+c(hQ+4v>Qu5FKkN@#Y&iuhgAHFjbdOT7A<>HY^Da(7;tw-S)1WqQ6Zsh@ zt`;a1ORT-!CttFu&DtbMjOkhQdNFCHCJ8=?jad@(4+cb5hF$QOKQqg;QDW=bo z(DD^a#~Pei&eLkgv$V#UHt@73UZ|i z`C^sB?MEDKuQ9c-iZCoTZeK$RL1%BBXFu^5@jJVu{($P-3J>4@K36{V71A{0t?&OK zeh|^=bQzCFJp0neC|9doe(qz~PLAT#6t-IcLz2buvAN6J@7*C$Hd=agI(;^GT4-$& zW(EfbJq~uaux*nyj0*D}d6&Cxhvrc%I`&}xqvbTXnaMXL<0)lcipAM`MKJA|Emws(#w z)EeyE`Y}l~Vrr_w?*0*4NU}^J42eP!Y3Q#76i*rST7tM2=El3`t%Xb8{CF6P!N4a< zw5RsDUR*e}h-unNX+tSx#7FxN(I|ALw8~PH%Cy!KAP{159VmnbemVyHDF*N-y1tAw zqMd2k?u=Z=b!SH7Vf;_N`?=3k+WW*CUq9UHw8LkGA^ufyS$e;5?m6F1g1`LDyYJ(b zJyi@c@vXPlDV;Vc&suCe-ovy_;xwYRgtIxbbX6w-|@4dgnAhUSp(hQ3Ym*F_2-H&PaW5z*- z&~WzrMKZrj!Lx8Jf#cX9Aw%NkatPDn(nr5UrvvsXWXQ>@|SO1%HpYk2t@yIWhV96!m^AO1Ke&OV7( zC{n4H$uy*KLKcVUECrpSwIa(jDoa05j%ElXm8liFtpmEPLn1#Wj#E-?kYxh2g+Ce- z1u=q*nk&(M$h@NnyBkDa@6b?*X6xOMipFHDYx(&!e3INtU3s`k(+mh%1j3 zt4leY)9Bs%V==fHj?A*-%pET~oz@uPTP-IyZx(9Da`B+IEnIIk4g6N-j}BE7Mofx6 z(@Ldioq|@Bw?IQ@K` zYp=Fhe0GeT7sR1r6e>(9kj8}GFU(BwV7EERx6=YC3@WyXEhSU49*$7tErTKkkB&IIR+X<&XV&J46N4sODtsM7nG+DaX zWiX5xj8lRzowxxDLAg>R?mWUWC5F`4 zwnRw-TUba#lJ}|{ZC+>8@9_L*{|gMuB8wBE@qj|DK@!ItY}}_Zy};RLK8oYGNXx}= z3q(OcXMdYmTT~WK(%Rc39(PGyi>IIY5Klhy5iBWDSwid&=pJqG@ZS3r=APihsWaG? zK^pm(0&KTTluZ)-wN zOf^=?mFj%?*Zyn%$^ZJ_lP?x&wpvtbC;9L%{|#RI_V4oGL7S7${SpfoKSKUn|CWQ@ zLkvT+v{+>5@3PSjCu|!5LW7=2+f4^rofylsXdUh|JDtN1B~k^XN@3d$Ns=MNB+o%g zfkrdBHl}&x3|249%Jo)v%5{a`?+!{)lricA#ge~$5YLQOFs-82N=YFLDvm9*Dxh_u zwT^*ED-{WdkqA9ekpFD0>z^`!KiT~wjch55%nxK5rxq&1Fx`4c>qCF|?XRESeKhVy zp?r3X}Ux`FL-q1Pt79)TdUa~utG zT)1?ak9^@bdFo@IqC{EAOJ~}`;1q@44Y%J66Zqn|IFbJ4E|02=O4QwO8$ude7 z6dfp9lIoO$BTXU)Zg^uxMwzOrusiDTaIFhtgF%$i>qhMQO>#m|+{?!+AIj1j(O^KOsSi17!C@nMY06r(tz5V)K> zUg5-|ham)Ul(Dxz#*bheWTe_e2m{xaSbEIdOaoJd1u8G!xH5taHZj^iMZD3#&n z3i!hT3nwnpm^;bl-Rp#-5z%l=mTGLR(ONNz6j3HnGRHU*EG#XPX@wtW1UUTY&3ABZ zmvc`(h2`eS(hSqG5mGR_xWc`A_wjoN)El$Z8w=dH@k4ID{|4h=i0hVk^ynUYyBn-O zdd!C|Um;&8P@g}EU7W^rJ=%wxT>Rv(GkfYH53ap|(gBUKjS)wn1(pd#*Cf%Cbn8Oi zVK~wg$+{`99Rt%biNk=S?wInz34DKqRw;%d5kinEiD#G$Mr5N-uyy+RscM$uYSXZ# z6e{jZUCfvEGvDkW455;wD|5x;N$2RHj-yB`ouO5N)(Kk2Xi`9<)lcRV{Sy}OQvmpx zt3Rng>}5!qW^&y1%{bCJUoQMcrn2GfYj@sh8E^g7%CxhkOW`YK;QUV?U7G(N%F=ef z_4*n|~2HySW^tjgZ4n6-EM?B7h7yWkNY#B6-O&$zG2 z5`oqrBwRi-jnWwhqZBDDvP2_gM&7n5xfZtPVA?Lla)rC=V@C1h%)swLd&4v2Q%#mN?W47;nAl+>%QcuwuN9^tebRH(uryb@mSX5UG=8u<9N;3>&L?tCw z0o|Q|sbw3>HE?qV4}NFB^ck1RaT7bI8MhQOGdaB2qF%RgGXqNsM#F?3ywxPepfyY| znEYUdgmT_xwoxEwr4&3t!834N18GVY&b&y_d4v!K)%mk5oVrS>zKD?;AS^IUo7t%XB2C%t z#`uFSwp$=g0t8~R32R7>&lcD_OpP=$uKVMt+I}1+wWlbQ8o6ZLO$OSxyGbW(=`0(& zwYfQAI;F%PHM7y6hgO-^d{F#4Mw0+3N>7qYe&+MD0Ps`lA8AA&kb{H1jFln1f<0$g z)_;>JHTU@L-S4drzh547;%%>P{_5G~>d}%cUA=K*n;*Q>ieLWC#x2LOnJv|4AMXy$ zkuzd&nDN%vTO7Yq<#+z!Bc{#@u6|*WNADeC_>$KBkSLUsYHWws7_B+|mP zp-?a=cow-rnPaCf62?AHTz;7>bNPSydaqy2()7CT_r5#t{pE0StaB=MS5EW=4!(kF7+5{;uG8Lnn^ez;Cm|*hvmnuJiCwmcM=7I0OjW z$PXC~6P#JdC$QXfNg{zKb(UXoxLhi5{;L;x_4Tjv;PZzZJ`GsATw>4;*?&BuUiZlF zr4&4qq!BaP$!X5oBu9#@oHKY&QCbq@QO@L6Lgyf&QMK9G8IlXZ@`8tBNjjq#$C#c# zmR*fTNy9U9Dn*UOl^3bbZ6Y)csp-?<)NNsVWo);I zVHgyxi zNrI(P@-$}FQLo{69;R-RW;w-Dnd-~}2T$*@ z|M+uqVe{tKe+k2Kn44W;Z|5m%7dC0PPpMUE1pTL!YO5%rBTa`e3K6PDoFfpY$PJ6B|(riFZtQl4=onkqQlO*l74p3w$ z6_cwNr6N!`V;OQwh($wMQbzMzE7?U7GM2KoEZk+|Lh$K zS`MU?$`0{3gB*_Q22Uy`y0*x#y!qd=H~JLKx9Ipo1}7<~47aM|g*w6)n6}Q`Oqu0N z*XZwe82b}iev7#a(`&Y);|_(AhwkTifl0}%vwUfdaS$U_Mi3?>nZnN!(o;D5EXMIn z97|)^50R!pt!xk_iYQhjiK5`@6kMHJ!JtvKsMky?1)XBa!L$UHB@og;H*Mz6e+_@w z#VIe~mYN97Aj=eKlF~ZZ!7JCuvYaf-ahw9<@tD@Z9#Yfko*XbZJ-{%hYgEg22nIcx zs~4#+ERrM3t!ela{TAxKq zNRt%PcJK;iqG-zbPxkL(n2_cQGtH@(ia5?ONT)mW)-eZryNt(UUV7zAtgf%|+Lyn= zm%s7`^YaV1u8Wicq3JlTN0P*NUXjM^5-7#PJD(t(63Z*+I6G@`dUC|v!XmC)z_4A^ zpQEEQx`QF*`YhefDU->VQNK%lX@gud=nThH7cbF1K44>Wk*imhx%j1DV5MQO z+BCVmS?2kx&oS2!6xP2)YRobW$1Gobk>$&;aI$q9sY!ArP(q_#b_p|`Nu*R`(>_r> zIa6>wt#_>Ya~t)7Zi-lB)zB_B9kX073&Sado+Gk4x2IF&?xf+i)0TLab>D`cIVVLu_u z!O%6bJmcC%6+@-$_X5yDY*S->?xJ&H|(`;YeMsSdNx6%l%h zbWCpjsDq)Iq;ZO8YSb=T47)yq(THMAqy1paa66;2Y~W9FqBN(hEKEyGg**hFr;|QA z&a_RPOhKchQ?fJ!@cOH(%r+eyS0hsjO-zGSip3%`o8Mq~e2?D2=VVbpZS5xNS!|Zd z6`hlP8gr|dwvCX2@o-2IC3HJ&;&6iFI#{mDsMn@Cvw&Nw)7sxgH%#1O3Ab1xNn>=) zK(|aHe~eNJuUuz-{UZC@Px$=957@Z!JTr4k+`scV^NTB#N>xse_7M4$FpEQ`Hes0xG9bH#i++WVS(;O@%b4;Bj(#LKua3j>q!K3YvgMvw`atu^ks7 z1%_!6gg%yKF&OsAf$MpkoSbmw$~7W?gj-v|t<;!|15_@^ z5eVI3G8yChV=B#MD$AP)uffAF?qQdg*!$!U2}dJJt1lt#3J15}Mwd2P;T-Pj4UV_( z(K&bkDj^tm$ufaq=xC;nu1VZt39Yo`mfiVJ&cF7{Yo^vLE?>KzIdg|a%P#Aqqwuu6 z^!$=us5g=%m8m}&=26rkipD?!DpX2^C?b?fgb>-khXDTH0q}SK?tdqQKq!GY7zHBi zhB8fKVVAtGXK7mRcRJ&bzxTs`IT?&)@1z&B{N1k&-Z%fCEb|Sa<*z^98y_scC01gU zHN8Ty=qX1#J4^9aBypH?b~q-E6Dso#xCt|tOx%ITWM2`);QJ9-rYILozWRKVgP}o~ zCFmMBmdE<~1zgKOldyUID(jmU=$y8BeCIa9&K_Yf*XOx_ggk zR0|v(ZZjFhv?gaf^6zqdyiZz}JWkH=9Gj=dBdiQQ{G!WCjTyXhiBGkR5gD1#xc%S@ z?(MzHhu?dL`pgAhzVKz1(-z&cE_$elmBdPQ+J=u~7}P6OCe8?d95V?b^a8}a46h;( zY0kn*0i&6bij@6(0mVg~?m@)xQHmxZ7-m?OhL($GIf)V>C7LF29g~aeb9DP-7Mm8H zt20wEk-Ear1dd~%2@RDhl2n4q$dyCy=pH75@o++O^9{lvAPfVtEWs(3aSJ7M(#91GU+CY}Z3-Q=YFB1inv} zBpmKMW$VFh(j?=>*S^N$@(L^GHc@#FC`y$YlhF{Kao{LQh=K{hc*xV+AK?!I;&Ftd zXGn7H?gq@14UR*o&NT5SK8PGeP7=jzY@B0tWsPF7gzryqJr60xw0RiN44o)Wuq_)Q z;Njid7+#4|xytBS4O*T{*3ND6;FEV)TwbHtTp>g9-jDwn(D>q`w~R(mFI6RgyTUsiDMbH z4nJ3E>Px3!DSzBk)7L+oA^{Z(A<}09;3q>s`}{28@BilCX55Pz=MYJKO)=XVZ*@z~6E%W>Dc_f!6!c7JWge6i*hU+S8Xz4WS?NvW0Et+Bb{ z%%=b1A3o5|y;x@FqX8n(nZ0hZ_1%D(3k4=$WK>0oek&nL1e1wRmMdO=zR78Oz;@fm z^CYFBi)(v?!I<&*jLi#InVp#@$s#(P6Lxp+(mg&Qn1l$CG3<{Rwg#N`M(iA&q6ZVA z<`{p@=Zi0%GAQ;)n+6Bl0hL0DUOS?jPdGao;pir(L!US5lF6jc9WBQ3s&v{hM_WE; zry;SQuzT`8*@JhvpSOudkn}SAEW@8<*dv2$=U&CWk>ZX8y{7@Q&lNd)7;x~>n3a_| zPFq7NbCR8VW5x$1s?`!lM`u_LxQ+%BiCvJGv4$q)Gfq8C!LcpLwMz^9>+e0`#+547 zqE5jx@H_)sm+0gqVS+9MQd*Fws7yiFCypa>6j2ZorwU#(rSr&$6p-DlW=TmV~n_6{s z!y?Ty(j?~G=0(Pnh+eM;2%bE;N3m4GvTc^nT|v_%mT5BbV}=u-2e&`rcz=)X*$GnD zxb^NI6HG=JrpvqE{->PmJZAguM?~a|;*fT?!}0!OF24RP?!Wt8x=+5qA9SfQBvU4S zqR29V?Kmu+e*w!YGw2Ultl1o%4Jc_U#Hs8~`rEC-Y-?U+i+Ucf89{q|WLD;9{Laa- zQo0+T9e$xEen&6Y8u_r_0wVYc0*D17QTe|&0RH}O{yjc94aGu12N3t;TpVq;q>7YC z^GNo)y|hp&{)O$kcmMIx&TbZtN9NA()?)u~JkZB(Yi-qjb$r%-;rKN6t4r0pp$a{x zpar3?3~kdl2knUc?}XGZ*!aB!Z$`sN70b~)$uMKZd_&M(h?Ug-Xt1L_~f16XLNFe5hVyR{N99c5>hDZq`E{S zWB!_paY=CSbi^>raH=-@k1}eGM;L0Ho+$FkfU;wv8!10Czd%tc3`3*TOSpB~LeuKJ zeBm-u>v7VWP;@lL!<5}1q|nz;w!t{gn61^=TwTC7Pf*o}pj=js8(zmH3vfocK3UH<&}8~ z1(Q6>5P+dcEM3EKYz(2Wo(+vGR~WiU5+|5enZOU}o*ZJjHB6_7<&|)}(llOP(`lcb zQ7o7F^qn7Y>BW~3Lg9L4++vYha~|C^aSA1pB$~D@nHGg|6|Y>G)}3e5sQNTb5K{8w z-feW#Wc|WrP7ik2efosejf?mbAAdYyd+RC1QkmxL48eH7pxb8a$zz(cbC{;Z?xP3z zK}en}hDnYv1&&m7dI`E~AXT3vh8M2ipxZxXZfTj#^H(rT6U#7=QXn;Lx(QR$oWd;6 zKx!x?druycW*P5)@D9~#4cl?~!S{ZjI7&Ge>azv2>o3?R(5@+~o9dm&KJ0tFnCAkSP_--qeIqsAY*3O}f3)No)Hs4T8RA zm)xX%a$gEk75F`sWm5rAF+~81Xu1i=vwvp*0Q|-O?7tSv#pwWe&>xBM*;uMD6LIEC z$1N1|G-=;{@4ekoyQjxd;N%mP>ApFLqeNR;P%r3a_TqrVu3Z zkh{O@Gj6AZNd&&mcoMUi%~BQ~$ERJ|-Hb4d5t`z~d51}+=%fm12u9-(j#oekgR3`Q zqqZa|+j9&C1C9^233@$}pihLxcpTI43^ZGD7EJlCB@~r8lhhShnZ<>fDvzJ^s4QEk zdce3fATEpvqJ(+f=IgU%3R;HpQ-V&+PCwvj5nkT7!evWgk7xMtcX#-*<&gDq^jRJ0 zn}lh`a%~RJ6byS^o<2Dv^$Z%9H9X71bUdzqX_j&1bL|@*jRhCYl05!!LYn9F20cPw zkZk45m=0ynq~w`6hDNDq;5pOI!r6wy=5mvdZyhqf;IY2s;y5OrYd%Y!0Zl4`QAC<5 zq|_04j*vQ84wh45IOr0G8BvncIy^+T%FJ(EC0Em$Eg>X@VgadXq|pQrEUs=)s?TtC zw8zZCGMb@NtTyPjj?i_3yC1zxvC<&VGSW1m*FMFx-DkNH8VJFikKbkc;a$G0urmykjN0whQ&k#b6-5Rl}GI8HGwo71CR zS|`UWox4Q)tj+V+Z_@8|_~G~efKI!Q<(ByPy&thKKgY`EB|zupuYHsCOV@but3OM9 zVTEzO!*j2Gjr$*bmvXg+X<5WkNEn2uJVlp+H1gU0;>YNw$)nHTCRPGJP9e>?^1=;< zlaMqCId}aH{85*|WXvmH|5Xgf6Pd8KEyr0(F`_scI-~Y*k|aA?I;O4CG|;_jB|1BP zByv)%Qm?DYsP_y31k)ivgb>NIql4+;0VwU=L!UHPVzp!dh}%a48FdpO^NGx5uA8P+ zNYli+_x?xyB=qgcXlR9@Z>4c;2%$9F7@k|2bzXhc8d81Eb>cufI@;}Mt1o)Zll=f0 z!|~Gq=|~2<2^ShQ7M&(hlF(_5=nYbIE5(u;uRdSl)9nd~mZNe5DQq%r%u7F8V}4-~ z%dz?B_P^n1_>|6>k2V?8ZuM|XXv{3*6dmF;L(>#1O^1r7G0m?}ArA?U`6xGS+8 zi?!t?lJStI`z_wRb;h}0EMp-l)C+8^uXA$T=E3I&oE-&BqL{qpa`H6B2_%hk64SOY zPjl)`o0(dHvZGOUO{!&!Qo*2JwW*d(W*bFBnD9sMAMz_dyMk>>RF>ff@ia75if8FN z(*hHvz@NnQh5?2lNumi^oDqZ>Q6gDbe}P`Ni(xy|W*5k^6vHs5FgcA00-wh0JXxM$ zS~j-p;gu@W^`HRLvI%^jnWa@K%~_H(r`JBCP@b+3b;D%P>!NECP3jcN6*MWheElXL zzx^F_!@w|Yjt_R3_+uQ;lY#4#|{Qly+_`lWeJoMptBdS-ZfMC;_3qtgSF zf(w_g({7!x_2@p`PKVwoq}^^))HBwvTt~NydsOGvS-*S}t5D|R&9^w%e#-pwuMx!=FTeGtDbHZgNs_9EpM3hnvP~n26D^4&nWvf5G(&(; zx^8vKWru>Jaqwue`{3i#ptPe*4T)vy z7@EYjBn2DdpvBzk5}`ksE`^1dKA1va+75F|Ym6sjYBLKAx*eQC8Bio~gir#nSYdAU z9JW^?%QE_%Y3b+T-WEaN(`%oRCNXhMqk-rE03ZNKL_t)XAT)!c-KQMwJf>8s@{OPW z)6}XJ{P7u)-(h)ik-H!Nm^cpEymA$u_HO`LqSYKafdwY+qr%z}fJRux(nDqO+|DE6AxBu7wk)69AFn{g> zFMRb|NKGU1#}um#md{_qt`DTjQkU#|DUw_Pbt8k`;7c<+qMI6SB1=ai0|5zq+>Y%AR(NKaA<$OBi=D3YN`J zy*!6u=#Xjb@3c5-Pw?t4o>}1fibB&g9-dCHiaKeWW11${e!4_$rbwK1IXG^inUXve zM7qEZG}?ZG3={f8pD5EvvmEa=!8i!mlQC%pDhoP>qf?o4+1vBU;tbLp&5{_Waw=LW zh*F58Ajo0l!t*y@Bpw{`aHq$QZ=LbeSITt83H7-Z%tUeA>hk%+Qw|3KmAb_X7t1Ux znw**@6|KmEl=PATPVp&2Qi*yF&IXShY{tnOS)kbMH$U<39=Z+HL*>D zB!e)6AkL*#Yzps(3uPeEUce{JZErrh?Igtxk{F%oE+|O;mV8bJh;X2 z@d0x)Gt957G3c~eJa>UC&H0%>^=J70AOAiVEa3i94kI5D zgfW&=k_$0yRubAY@vhae0{qL9lkeu?>|MV@~CK4RC7)L@XEI;RO%(hdv)~m0?o<>y_R6>(U2_3=~jFiO_yPm^3t2%L}(I2 zC?@*}h9PjPio?ATxDvf&@kif2qSsZ(VnS`vCW{rJo{$;P9mll&gi)F?QaQ8p8|cB1 zrw1M0zuV^LUt4ExzRHEmoAl2@etc(#kDm1KswSK3C06GP%+HosnQ34aJ5=XpSbF}e zlv0;DtxVH)QDcD-N<7oRHYMTslybpi{rvNsfBv;8W*1Y-D=H_>1O=zS_rHIaKYVA0 zzx~&)v%csdb%o=als%g`$Gq|>^sC6a(ueWg^TO>lbjjPU}f1QPfYIL`jp5|SZH|EY(eW}k4KL_fIR&P z(iMQA>r;9orpGo}uBJVdLIbH21OcV`ESp!Zv-9{NpMLl@oz^Lh$~1k5G-h^giSK{= zUosh<(pa8d^Zb|IV0LAh_Q8El zcJE_4Ho>S*mMbRynDJ!H-TSxcn=Y->T^@Y+T@D_7PHTUQ(}OKePFv`fSJQN>EWopi zi%YSeOSgPAt2EEm49{yOqke4EW)_8RSWKh)rrCf(FG$ck~+7ho&K=Um2+9_Xp&ci|ygc&2h zMaB$5SR5Xnp_RsD(LMCg)qNfA~8~K zoU5?+aKih)*G5D+&U!%aNDz22XD2yjR}uvQn$U0^K@bT%SEpJl5JdxCx%pFk<*S15 zCrtqIg1N767oo>&vkstWH!Qj$8wR1m~qI|Ve`qcl?|79Q(Y zUP38Iq8J5?hJ7maCMwIGm27HQu7{A4JkP1mEpWKI&C13Fq$Y7(53g9nay*baqkf-q zrOMgK0hRg;!+wvG-6yPHdXD46Jr+%a!MH=aeTG%4@pEr}gF*X{VyR5K(}f53x&G3t zj7LMBd;TW7+Yd?82dG@&hZ$Q(A^kL`R`Zb3V9*`W?GM>{^oVlVMHd=^>1C-9f+WpQ z3N&3O$ug2ukSR&36d95fjXce$Hs@GeJ;$JRMCWLm{hi0?n#QnmN@IS7>o;z4w*Qd& z##hOO#^KWkT)KXf>fAC?2zsac=$4CXRnQC@m8ZP;#xL;sJHN-$=0&8genJoQYLl9? zg{Du3Y#RBb@ze@`_oE+R6iVn$k&UnaDcai)PB07ku-ytZ5zb2CduT41)sR9snPwNN z(kscNb^JurOhKM!N{B=#G8EHBT_Kdv^l7i!vmv1U>R|1dh~db=NWmH<$0bZ<4F>{{#yOja!GsZz0aRK{@cItix=$N z-}v~4d$jjr{FWm0bBeafxp|K~Q*^okqj5qgB}taEw!XpX$uT=eeO4+umsU$04Kph1 zIwUqKPY5RyYK4U2Nk*{XqbY~>c0y+xj`j!S!GO`(BR+ihJwnaE&@?vAU!!O}&kw$L zizgq9_~y^Pf|6sX(Q3NXlh{M%|%a^l){F$}^%Mfr&vhN|*#Glko^4Gz?7;jM`KxMJydW zTf;O2wfYQ$vmH!RVwxJdVPYFqe(=EoNgDCr{3kDBXbQ(NC|6u`)1ce)84O}3eo7c8 zn3{%XX>58$s)^24I3}+sM!gi3#N<_zAOpjPHai& zl*$#_CkI@-dV^87jgpdbr9`DvLDMA7`4uLkKHJ;dT)lpihYuf6ug&lee(V3F*BcT{ zCWKi=rbBV2M3RG7ETD^+TA|3LD>qqLzf7rIC0EbZelS)40i^`F5{!a~Bp2jpq?sZY zf=o@@snaB8JRA~Cd|C&4v#CS5{&2PL;d;ck+Dl)gW!DKw-@fYv$!W%!!>A@3* z{SHpCgk^ivTL(G4w{YAN4?cSbO*0sF&!{z*sLm`?sLycu##dRr@-jIZowHMl^?9Nw z{7JL15Hbq3KXj7Bz17{mGtk^`6wlgGJ-f~zU~rc0xeBB({nCwewjb@&A&{^Zn0dm`OdA6AkJ`8 zgLqFNM;78)-B_k_N@blw)k5Yuuf6&z@$i^~qape6G4J1Qv(=0Fg)h%hEgIxPpk$6) zG^y7M)T$KWISLz9+0V&Oel&C z9Vv8HmoJco9UMbYYc4atu<`7WNj~#7OwB(iL9Uqo?C3Z>_ryTD- z;mXa|d2;I$k|3m?WL&(^q*lne`^6*Pe)j_ocOFx2%&>a?dFl&mNX_8>CqKZl3mDEc zR}nGo1xVwV?xfrgTEnYXsks*W{ayaazxv-ucTd>6y~XB=OI*?6S>Y_|_ttCZ_Po6=>%4XR|ep&CQp%^1?5WX*R(mAlWHuajt+-wD7RxvI_wD&@~#faYIfSq=S8`oC&AO4G1 zXw=Hn*Hs7x!e>u>{SB>dBT-WkzuD#JTmCFGW;tQ zVv#_3!WY|Hv}Yu75b?A+V&~}t@=S5Oy-TJf+mE*pQb#FC;QQnXvNR)(6Gr1PNt!bl z45zPbt`Jhgb{zbPk5?-5+>MuMA049W(~!_8^3inzAthd+MDOf`(V&B&3z{=CxL$#! z^-E+~N)Y(imdV3Ax4Hk>hnS{8qfw^lN!+4`QP!~<8plUI1>2+AsFElXqf(<(uVA?@ zaT=qtj5N)NqiFj2^Q^may7E9iL!ogxT?wYSB1#f+A(>rRpw~WRdGj0_mlpZvTi4iF z@VNEi9md)W<$8(QYz5mg>2*&DClgQt(P?O(ogaNtfGQkwkR^9V<1Y%b2iFym-{pWpfME=y`@?LcMC!ENRRyl$e|Gm}$7oG(2XSMP_O) zGc}J!#fCMThl67_zieT|8lN6*v+>GT`1()HQ1EPo$|2Pmk8^sXlmw3;)=09cxy3Ye zEM1Z3DS{kIV56ah3Ga=2R8kMy$(RIPKK$fUZr?hh-JXDwC{n)YPw0q@q%p(UQH#J* z-1_W&p4`9B;r;X33n8h${9(?f` zl}4Q;iBWmN@z&j`wYPglmZZG%o!?_J9I~`_3CnT`#shMtNTQe^9N~{foE+`aIys?K zu5j+$Dy6E;?0g-WX%x#A^@fS&3cBN`%&fUwy805c3m4FIjW`a7!VrHlLO0BRcN?Z4 z%cn7EnSwOSrW7cO34)k37YNOyy?dAXT#c{%;-AH{3;gh%r@VLX7(eZ^y17E7F~|Eq z{5HdWkM8Law?FtEPj7$3csQWfI%50&r%d{N2JII8(^H1s78h1)j8C@d9UYSn`)tk? zk$wlKG0$h;{pWn~A^x?1m-ubn+|7^*W ztDk&)pR+NvLyL(Y5Iuv}H?Pd|(hEx*?RPk8MVP>w&sF*G-XVYKul!B^_3!=*bVKK@ zx4y~Udv|#5`R91$mDh;#36^QGy|Yj2XoBZ?jH3*1)nsY@9Loz=7!Sv+%x}=FE-@HS z?VxMRv%LB06}&=?M$yGoisOM#cRVIVF&-r#Et<_ay1fp|D;xCtT^8pH{K8Lv9X(F? zhyV8<^KU-b=Rf+DO_U}G!i@enWgOkO3kBEHYpb! zik^mN>o~eb!L=y3CZ(cHxn!Yg@Y&uTHMdCZ(B{3nM|e$(aOao{^DaeGBMcMXJ8e;r z1qP#t&M+d&6%s|svG5$7x@RJ?1kcggH$&D&7J-r+4#$WjV{KR_j|9F0rE-xccZP%$ zfj`vndy?j|!_mDqJ0}m=>TQ!7V}iEE_TDbnZ``C%ERrS}gF&Cb4`|NJkYy>&xj9Ou z@=t`pEX@#7GVFJl479T$*KWP7S6J@{g9)>g}x zF3xN{{^8e(u6E59_y76!LHpge-@pBh({@nVYE6ifObx?Wt83VnNxe~{SSVoV4t_jhZS5i@+hw^{ z0>jfjhYwCMO0YV68GkZh90i=5`sf5i{U`K}k2ySU(d&WF)N-up;{PG*J)b4d&ilU4 z`Q^~LZ{OQDPfk0tVX$F#fn9_J5l8?GpeYh0C6#HGMbje7mP&S&RkYuz%2vsy%jFVD zvn-RMDJFn~7!b$>umE;38z=1KaO2$A_jd07<=;7e(Ib>x>OY{X-<;FWd7kg{`7j)f zX|-CQYh+o<6DOwm+{d4TH08}V-{Twa?eW=<&okX{Nn=TOoU-3f83j3MmXVt}maXGk z8l|dBvE)!PpjP#nYSyXG6sZj@Q9Ni5SYwD7Y3NE}8X84M$2M|`p3PLPf@Ygow$5-Ab4Tlw zYZ~UUoYHcMM{-C{B!nYHFideg3(L{jUr$K#h+Zc~9p+??B-cXjfAfGrze}ajKuXEl z+8U-|fDky2L$}`}jw7no8oFts2|=Du@&%f%@$k+Kj+{JAp;RVKV)8ua%+t?Ll8K`M zhG{YG9&qO3OSpv+TPruI&n+V~o#AlI`olYDDx-a{g%SqkMiV6^-}~-sgy8^P&nZ_e z3N@2Lp~9UTTYUD@zr^tqPh%PmyN|ZHcjpGCZc(jIVcU~tWtL^cX-cL9Q6>n|oFK_b z^Nc)~WRIiFNu031^@s;|Z{ZaRXsS=SA~=5jNvx7juXD(-A7Z;D8na70xP67KjfW_0 z^1RsFT;=RjALjOZ-=}l1i|Z9xy>}HOKEzNt<8eT@AJZEqbO#CJQA){_R0}!>!w`VR z;t7IbpLa;Fhu=u!=p0ghAsP&Z zS(bT1Alo}TS)uB=;+W?9tL+!rKlb&tas2V^sDE0wjTgcs_@5?YY{kNCi3?90XWZ#> z<<=FxbE^Z6!7CR|)7jeK6R&=n2lsDNu9Vr@S>c5jULlBj0Bo*3V!d^n7oK>Q^CwSW zwPUK)GB2F{S-$>vf5LLvVd)8*Qe%W(}JkytB@yKC?(_ChW)nk1;AO(H$oYqn!H>`nV+rx1dwk4IDkkcXU!|(e6uH zu)xdFGG)8D=d^qHqgl8n8b1NIO0*j~FwqdrB!KRX!%rYT7rp%i4w zAd!kF%}CO8a`>~1G|NdJhn)=DW~wlBb2tlZ$j#pkG0N_4jqZr$7CJFk6> zOCNfcVyVW-#SvH6+r+8j!r9||;`!rb<1R`HZe6*;m0O40*&ed6;B)@T8d08;rIKDh zVKB%TjWZ;YM6;+0Ma8kGR6Ob>gDE|yUNZ4Z9*(1935{sKMWIo|tNF+blo(JcP9wq* zn#!Crvx(O9-w$QqO!4cE-^{UVuB z;KA*C$Sh>KV$oSSq@Cp)%#YE0gW1TZeI&#-ErMar)$eapso8i%o1$S;oYFYl9-(Q9 zTHU1aA)h!GJo-jJt<)qD;RMcMT9}qeV|r%NSyzhsbOX=zkuoQaVv;05DT$%$$UNu7 zne(Vz5{6@1I~!D|rb*J2VZTo_9y92*IePX9yh4#qYnR27XVFcQu-7M!BDQwg6kUn$ z+EmIF3}A0>lg3P$EDM=xSPZ%;3?u(C<(ej7z|lD`wSoapvCIL2Y5xF z7s80%VUNv+tCVLfDhrUsIfkj>lnjDyhCt)al{e_Lc2F{-+uB9b4Du`msiUw+;}HUh z5Q03HC`}{Ib7IOob?I5|Z>+L%^DVMm;kX`_6)&?hJC* zB_5BmROptG$5CP!rqm0Sd7|BG2w@0>rN9!9pwLlhO6Hm{bg6&gmwsIwtlSsfUWStn zMM8>9l&PY!TRgC>#xVZRBpa1Cm z7eDjzvCp4gu6|??Ce>Ca4v;<^*S?)C&^McIB4_7U;bl^VvQU3H+gV> zlkM$2ZruGYT-$F{Y_u01s9M{J(#Gjd7CmPk!MXxiZ+=J|xT?Z0XVK zM|8Sl!d#+{EIfVETok&`Ac$EghMZmYIlWMz*>JEbuvE6_dMV$1bDyClSvXQB2?eGr zIRErAOUD{?_9G0%xLdk!j7glUTB`R10Ahlg!!S3@B2Jclp-)IVZw z@fg4VfBzp`z4jij9#Uz{u)nj(c+lbY%^%Q9u4B7!`~`>Fyho?G6s7@&o};4>QsU^6Jk9BjVob{-%TjVFLDMKY37ytH_wU|9N=30YLw)WDN}-3l z+uzQEUSl{I3B8n;TwmkXn4wO&zCa{Gkb%gB zQu!qDG69zxd7d+DAF8P5i(S>0g@PthKgxRBPR43DEX)@5GrOG-!?*e6pMC-%3{IcDKqMM$-+hCvzK2otNMyv^;!%F? z=YI*)w0QBQkMqvk-{*U8yvCW6$N2OoK0&iQ!?53Bs3gMlP?k%%Qs?^Bce#7_CWhf) zyEYHjZZJ+0WS$X@C9{h*k*Cp|S>(a~0q2(I_|oS;&;R=Sf5`gUCO`SJukxirnRu{H zK5lWivCqT(koWH%aO7NtsisLhkfc)4+8H4;i1VB%l-RnC>rS8?zH5+bIi_ndRW&gk z$<&1fre|6{H)JRET3#A&>;LuJMwKYN|6wI1c-X@s`N z*<&6joDyqmhrGAaCypm;)rKa}kf=OIQ;Ikf=t2;N5t?8UovuyOZMTS$lq5@0(E&-A zVi!Dmtxb|7f3*|Eo|RlYQe-Phe!3qZ|fY@bY0I3w>XuJ zckUUD`4{tz`x`k$agY{NOj%t zAH8>fxA4N%ODX>QjX7t^d2;ig91MC->Xy#2XBODLKl=9fU)#Oj>#3FAxc@6tRsD${ z|HM3XDX)6Fu>Sj{K~J=ENgt_FaDap z_{*;`HQV6RpZ+O)TT!b{QF^k%y}P%0w7o(5u!rRcveYJtLl8NB*~HL&R##Rzd*(?V ztatGb3oK99IltIsI2aK0_c4u(PH&g3`wtoQ0^YpV<yM!#`f)$ertqR)j&c0s6lsC zAhi^$tkYkO=xtA?W|owkY&58p%Z!FYI<0F&QOxZ80>OAptzILEVy?gQHdz*P=G<9U z@711t}%3edDhn2nmHjspK#>zs!>tp5@`ad%XAd4=7cNgrhFW!iY3V+2}pu#PR~W zJNrndOqf9sh1jl7kR~Wxq?&9fI>i!6mQDH?N|H-K7)QjzA%*BkU#t5{~09)496X2nm)~Dl?CfKrcq#~+T!&O_8Es68j?KE(PU1XB)A2i_Bf(k z6m(*Z;V57f1PBzl&>u4kQnECIFvfIj!aF7T{pa3=GNO=tHX2iC!OldaT~v6 zX%DZ*iSEvb;qGRyGJjl`C{5^wqYSf#?Uq#>j)f8y3KNt8=m>$(HKG5-zy6n8y!3I7 zH9R511NPd8H1BJ1udkbq=XQIKita-9!5ckgj?J)lu>I2;XGTs+3X;UU+ry^E3>i;GLV`tr+s@9SUY zAaZbOvz$5eBu9=OCk)1%JNqP#>kvgT-~P_mSzmpKixUizi|U4 zGp1*nK+ej&8*~r0sZURH_UUJFY!^+}d2sV8aWJGlJwvflLudlmFJZer-A;#tgEsqH z+r0ngYpkx^qu|*bd*VrK!=Tse@%~%iq3jq)VNoa-`LWM^k#B$NuNe$l4BLl9ql6=i z^RzY}Vxy?JCTW=PU~h;wy~vYKJw>rt!g5@4VUh@qaWV-BWeT!UW98 zV-B|0Q7At3lfOU~N8GsjO}_i>FVmcDGR}KY3>a?3D8u04?j{v*E)^ER$p@|7WNlGfdfp6xF;z{~YDNst{c?JbT5+{l=5AMp3eV&||X`@ZQzS zEG{h3oSnncZKkK^C>Bd7DH!%gn7YNv+6ujXmuhL6FMs6^iGvi|5j5)abbEWawvS_1 zSX^qd*IDD;cfQW@Y@K3dn!`tTxc8vV_inY=>t~dvUEH!xn4~1JLQ@)XCP@=T6y-#T zBn~oc$H3M#&K@ao_W31FT$rN1;9iqX~s(+7rQp%ppk-nt@Ofq3dkz zr~KCMeV1SP(vuu%`ovL4oaT=cei^+%LYydqFs52|s1|K%Ws_>rrdl#66)a>9QJi6< zI!37T_HK*yhhu7uBE=brR0_MG(NH#dEGbP1oT5c%H=^0FIYFI68Q_#O3`ekaGhsS% zDP$&(?uQ5=9=jyLP)wFaq(aCXL*~R$ND@V8D#vqNR2H$mdWUGxq3mg#I{Or_{@53B zJ)bPg*jZm8jRGq5I>ky2+wt%UB^EbI;6F?&)5F!_t@NACsP^aMg_N2rcs~b z```LHN=RfFvUL0mKl&4YkGJ1?gJxrqYwx^)OhZbRpk$h8X#`P-ibKM2$lBQA$G-3d zs?{34>!Im7%5WHGiZDrul7uu(NwbV3iHU%v22ww?ZUbqR|k=1YZ`#5hcgKG=v?6#jiMx?5v2DIdlB8qEKDN zDmJaKvpYz7`@4o=bge?wL&?4pTF$WdNC-^x_pXgTsioF26z0PcdCEG>?ARWvRJ997uY-Lk>F@@4fd9 zvK`P(Vzh&OI{Q7=cl#(b+_FiMCN+S3Yy&I@k%wr7$=n&sK&cGfc39sUbMM|3nq%?osWM?2lje#r zmIR3+OaGGfWZTS6uu;kOpNugLK&m{XhyWHG+hq&Ki zsczvLI@@cz$Z<@m?Be7x17UIE4WOs_`M zTI1%;Z5+$Qvn84~VQ~e9U~Uek9E*D&jG2C^z}EE$AwU>{(P&6-e}EJqG09bq&q@__vyVt`o3{hK}@Qp8L1h@iqt(5|$r+pD)5qCd-(+KRj}JCSDBr;{O~N1nLl8wd;W#6SC0PnNpc@*t zEig<)rQzV%+GK5aD?|@+()N&HJEh-)aF8+XkI6>~q#5>9eX`GQ7zlKPVUT8uM_VJ_ zxV*|Qf9V2_AxIO+uouwl#|(m;agdTLh38t-3MRg-F+Jr_E=>}Jx*W1OO(*6`46h0-M>Kc$zZ7+Wx?Npz)aijK*2)uvvzsnl$W1%t-i3@4vB&r6^9G?m&Ep6@drb}?-e zuUN+Qe9E<{iSp_(g=aVza?mLi<>5}i!TuOM&2elflx(Es(avo=4bGoBj_LU*%VCsh zjN*(aNr;n#D4KBlq?ClcEKcQegk*40HQWlOVWR+IgeS#Rc|H|`F{qrJ^h*xu?1L0FtWT{lbB zMvmsnB*{d3|2;Ha6G~=6sYED}$KJr~hXHBZvDIJx=~q>;TvWDYsfww~U_6YoNDb_4 z{-K{u|3N?KJZ(}C&0|#>%_CyCp6jLKHuCsKxVIkRWdrk9f9gdH6`{f*cQ!{1qJ$*R z=ybZg`rWJIANPIQFsQYe-1U5{$bV|uPgZQ92&z;hKdO^saHtZoFfMV$v* z30wPP5`B)iZ!rvXjCzesH_4@iR3=I|VEA+gikapyG`m2v?vtcBVI&Dc7>^~xeh8X| zZ5k9D#r#s8BS)&t)CyE=6I%yUR~VW?H$byBzS}wA=6FD{=`uUEC{Af)T1u)%#97XJ z-|SJUc=URTZcS2D24*J6Ix$95l01s={E4RAF?Hk^!d%g8+LS$uifz&?TFg(mEX;bG zI9lYXr|LX$p~i`m6=r4%96f%Dh1nUJ%_hgroJZvWx88jnLC(^#Gt4e7Q<<8=b{urW zB1v-&_Yc_H-DmZ|J=P!G;mvRS5jq)R6cgkbm<77M1A^g@`gHSgm8es%*NEeUvR`85 z_6ie(sbG07%(13G|LWVU$VOvk=%<3HWNy&BGUx<6-oBr&vVDt5_h|%6=(0_DiUjdq7nRaz%PUccmxwP^;_mz@1O4=v@ zvOrD0<)Eaklroi+x{^}+p&ncaK@ul)4%^BtxT28wc`_I!=KN9|7dC2zO8R_u*V#!N z>6!VG@$`#7&EI_Rn_G)$3xD~ifAmiDX#Amt8CQ2)8(S#ig8{pvoG1|>CBxpBQn=1< z{EI8R^wCdn{+UZ;X+|g&hH0Q1`eai}DrBx04+r$xJ$k(k`+NJuaX=7`dF^li7AbQ~ z8(c?6w-iTDFJKr3r_P+jpWA11{T?T#r`cT_Go$IebECz}uY8otmw&)pAKYgchb%5E zlO-ma4w_)Hw4xHGn?)Sknkbt43AO1WM~>Gp5DJlm{uW#@pEtwWwGP0=5@Y&?kAD2J$6pxKJ;dnq%=ZGzVmWT4Q? zI(knr-pXk{V>0Xrd`&0oPmHwrK%mM7i^nbMMGs$tlCM!My9mdlQgA7hr*O)%_|*kW z)1zFS!Yh_AG?+hflH=!|drabyTUc;m#nY9c2^^4O4^CluM=Q z;}<=`R=}BNg`-bAf#KL>rb`lOglS5WBqVV(i2!Hvu>&E+a0(1|*C@^`;+HCv>rKYN zHiz3|DzyTdolpsE_BPw-iD1-^k#YhYP*Sq-;2uK3#_Amgom~vI&)JzW&*>TIDZ$iI zk?;P&HgTp1LqT1)IXE&V-VLw|pr;DM(0SAvVwyS`iZlbEiwW6GNm`-IPc_}@;i1?{ ztGlaCd3LG2c`M7Ka3BoBunP5pQVN+QK`!)~%Ciim9Ho>o!GtSYNNFLZ1w=nI18p2^ zvD{b`5AHnR_~{d9LKDbbwKiH=^TYc`Z(o1-tMj!}|NPq9kDdy{(RB04G!J)^uS=Dt z1vP&lQ@!W;mAUQRJs}imxuTy*22oCuDo!+uBvDFX;S82-kf)M|_wP<9tx^$&5#czX zeQ?13?k4=7IBN> z;y_R`Z2BvQ9QNVLc8?nmR|utKoW$grVrOTMT;>xUl~9vidPApFG4P8M^?0#j)7uP4 z69dEXF!TbY@)FI(ql^bVq-Nq<7SoGm($pYNH4q+B(*c1T#%Kse%H!q->wx0yQi&i| z3`Z~w6k(Dh3~h4wJ%h5T%?}YxtSqe5J};j}BQZx)`xR zAvM@)jVT_O42n58D<1{=`#Mrxz)fN-Ugsiu%kfUa5jtD1wqmIq~}$rbU+J4Eh6F zt%J!w|Mnv;fAinbKip&M!F4919AANx2^-{c@odHTaI^TfrMP`XKiOE=0GMJeMT zB#L6vEF*oq1eC-fSsbzU?rTgfoyOF4!eN)e&N{X)nKdf-QBHri!+t-ZQgY{~AR9)cGoYI$Q*$*eRpa5+ZALO=`h1;2PeX4ghU1(? z)1iG35#$gi5;O}TG)&h=A&Hf-w^Y%c#`8Q;U%ucER`1q(`-71%tzDt(!mUi(;la+j zQ=f0@d2H|LGp41-BIf&F{nj8_cf7)uyI~tcThpwAFcT}jUp}F!m{FdP zDuX;E^&}xI)d7?#j!K96iNlq5}Lw7&(|4k-lwP!NK~1rg-ck4Ix6dvjSnz1iBqkh!VY$jq0B9&YZ_sk6NWiKtjKjA zp-F6AQSuE+o=&M~VVefJ05pZBgIVyx{eQK_UfZD9(5S<>lj1D;}uB3(14@k|A6TTm$Zc&xK z#-QrEl*$bpze1rpgV0SZr#KOkJjQ=#X^i6)kP?zOVmyu*3%!o3#30O(Px+X$1k9(isNPgprWt zgeoQ1ET)h%J|_0@V(oY|D$bb#O2rDR4=^p66JZp&HLH-+)FbLwzpb! zGppXv8>2JHp?aq=TULXOOvHKLNRmWM%`NRWX6H^YIwV$5G&DNFA)aO-`YrzAo7XUO z!E={hC&ru6001BWNkl@vSlWqPKDZ4|k8>lVk4HgO!6!|@)bqhp#jdC;R$F5&6m z*fvu~j}e6j1j#n{u6&jH{CSFg6VegVND2D;G>^TCeYnkub1!4tCRRD3yT3^&y2M$8 z>ALuyF%f_`I;PBUiauF5M)#|bE6@^>ZkM1RQJ>XGEd#wE(S|8fYSes(ES5~$7Qz$k z^aCth+T|QAPcR%4nNOa170sh+8+b1@S&o+RDvQ{n%+~5%4tF0>EEMouhvhTRuzd17 zLI}KK0n@U`vlLC!FfErnPZ0u=pid-IbhAJ%Aq*mh!+^nHM8DVLaDR(C*S}AveK^sM zjRKT13CDfTUwocUduNh#1J?FN^deAboLp}3Yya(k$4p~^-}+boBX7QQg}q@qp$=&Z z$J42mOe&5>p>7i^je@3wZ4qnWdj)RZ|0W;)@Xrxvl3|n*#u;%GktE6F&}SKG5+P+y zIOwr<>k8AeOQdPS`unfbJJ=@IZ49qUG#cO!6O^1_+S6#n z`ki<9rzO$IxYmeT+K^S#@bdF9-TN@yCluHE4D6X!&=P*|NgRtyKj;K%K`#Obva zQLZk=-r#-|(a|)+$Z{#-VC>AFzmP60f6^4DO?Bo7o9kUI1Nxm7zx!MN3g6ZcGRG$8sV6US{pNdAW4)TTfK@W7T1%Ma9%&L1v{$gQ4u%4M>NJJMS+e#mWbXl49Afy8 z^#aUlliIR}?5CKziD7Es3xd%kX6qDv7)>_XEOZ9Lgt>YJ+n+^bV@AUvnv`Tpq4hJo zMK(GPcAKf@9HwO;^9ghRYB&WQ(}GbZ zNfWSqk0h_488uRLf;3|sj3;oDY~l#Wvm6B^;g~#)IPuI&s4SuNXqC0CEoPU_(ptO6 zOu?hOtI&iX4Ha3QqX`|;o`iL}QHGS@=o+q}CS8Ax3HlDu?E*($C^I*x5V$cj^L1_{ z9p3%!I>T;4;~AUH*FzRxagkS{HI7KL$zFky0^QOm*JjC+i0PuxvfR)TA?jz3H+gt{ z6Db8ss92(fSsV?FEVE=39TG+RqEtOTp}$ChQYK1SC}k<7bRp3GlMCQKZVHY^V`bTv zdS&Sg$)A7a_XmTme78Dpf1o$we}4AWhV$?*!bf?k+fvs^lT;k+??$z$PZ&ope1u`| zkWRNxu5^C-mwui7-CeHypZ^O|3Htp3zNe!}fo&ROQcH?usP8~nRtDpKDfBJ{NgRV(X@a#)ZvwOIKE=~5jo1A*iqTQ-7=(ibfWT>%1D`?1A zVYxb%UBY!uS_eDSDn+s^#Bp?rC6g>QIDPIYhl3WwR!C#kMI5woTmxSz;!N_|*M5tS zef&oVd%IYcOLwb>Q^;tTvvk{gjD~M9HMh^q(m5QzKolm}ZV5xPDAjCKo+I**()1j< z(_z@&rhIgYdSekeUMFt7%`oj#J^3S8&F4tEdk7;#>f40jHrYs#quRZ zI~l!$5ra{P5E*_!XRD*o27R^RniR-zNR~;`G$T(V(sZ&uo8~FOXiOA^bXyzTxb`MjZ*9;W zrC7Sgv6%uhWrHkDA145d$ULQX(kB`jXr9I(8WVT#@N+-=Q@rwtALsJxU+2;K1HO0T zCR(vbc5r}F62G8h2*qs0#I+#Rz%J_;x{g`YnKBK`!W>0wna_Um7f5gzhB^Jgm|?F= zoaV$)N}5V!o+Hx~B)Ektn$TFe@;%0*0M9Qlvvicbl^YED9kN^z#b6@w9i1S_IqZ#S z2a_XI5IL@qBdp0qSDmvMwG(6#GB8r+=c=^qfK-m?-_FQmgZ)PVnMyh0SJ3pB5B_+- zb5E7XOA5y_Sg4!q-${_ti<15B4xM#*;P_=N>>VBm zVFbis%QWm9QS&QPHrN*5gi=#-ow(uJEU}1l2o7-34;HzK$Q`S~j(6fkde(kG_ zf&hucHUtPcN#11{mj3_I^&YX7W!ahDxA#8&`sZE53-2ocI%}={|NjR`JfYN@Ns`WW=$^}&?F)SM=YN&M(|tV0@a9`TqADhY3-I~Z zzQX=+hwV$N?Ctl7l;Vc+xj#H5-FKM{24s1G-*7(jtXpc00ICuzD#{|~(zR`@&XCH% zuM3Ji#R)7{m26&GWodB{Tji`Q3V!sx4`?=B?tb!!UU$IJ{vk#|K}nXJ5DyZHqGUE4 zvao)I_Tn0Tt%c)-Ae}j!Qn-YTCQ5mPjYXpR0!Ej_-RIO7H>s_^gx|Qx=<#=ndw0;f zz^QrQU!cDBIwD%b>s+F;ic#-?{>hYVYAGv4GMT3XYXfdzsq%`WS&6n4sf55vj>i)& z-#E)sXnFRu%Madp!n?P3`S7DV9PB*g{A!5w0;2i~_0}?Jl2TO`V+51YkSxiW#u?L5 zpV>5JGU)U8lQ$Xk9@8J~@aUr-^2gu$0rwtvS#LPpSg*6%_6U@rC`yb0-*1vkCQL^? z%A%sQAdSHbeEysN?ti4yTHxdNe$2g(-{j8SI~->Pd68nOInN>pBrR96)OJ~24ykuM zQY-OmK1Mlsew#*fiJ$wqU!~q!W~LPf-2-M>K|D+5_x?#ro+fCmDY6t@7U(=<_x4+y zJby~F)ugkyj8uZr=^;W0W|J|Vgjy&`vyw?!aWE*UIrF7$%>&05H0zSalFON|)LB{! zxVH5IeRT*8iyJ6vtq7qUdWRFbPcw?tqH1O?#qL&YU8}KjH6R15C+I($^9^R>F0K%y znc?jDO@iycA+5EH2hZu9#t@d_?8rVq2;+K@5kh5R)ZcN!x+CLB4~qn}uqFqlp8-SS z|KASa2Y27J|LPC^g_P|MX{|4uzuFuf-aCEg(m#opy@`7~*eyEpDC$y~9BY;9d1`B| z*;M6_gZKZ8XZ@TkaT!f__-OcyZa>8eix)Ul+Mr8;&=#o-#>`tK$B`^VK3SS`SQkVpiRyJ$&R=9UoATh!cPO$iu()~=zt+Z$>Nu{Ca3oj(D6kOJTGX10 z$aA-_RZ215r7BXISAH97Qi^PfwJGEM`=pZ-gxkaonz-Rv)^5E@Cry|f-sj}t5pG+s z*lbel9ML_A@hX?)z{OwmC<;R_DOhVZIefaycb^T(wabkQXV_j^Cu%R@cpisohec9G z(}yI7&rn{Cq7p2uY_PC?4r>j=-U)}hj~E?2BP%B~Ry#BoH|ZbudGFIMg%-SWp}|7S zXWW~S=9;q9xc;2Zlb!Cdu)K;%4MELC=9)8?uJDzw|2F&g-)B0_n9LHA(E-Pk1YK2g zW}zpjAqjL(o{?)mb9?+5<|-sH2n%c z@Ti3XJd5vp5D2<^F)!AaIeGpN??!+zIi*%OrN`l;5&L&imN%OeZh}Zc4nLlt4FszZ z!Lp@+CELr8E|}6H5KK!Ao`-wpE4C=4r8*M3PeSrc5cqDa^UMmNa2l=U;^bgog|(1j z_b~_qRP!_-=9Sc95te^Y0PKIHhKS?iQ~O8%+n=y_^_t_q>rZb3vO=)59x=-b!los*hnSwBu_bsh+UD7lT@H_)ljjDD zr5^djMM+v1gey?4L^z<0CZ1X9OChy(gV||LRmRj>A>(1j#dGHwj*d`D5C)Q@Uy$S# z`ApGlI8;@Etf1ZuDGN;&XLHvViov8HY%Jo)I@7q1?keO-!1DGLTt7lM9&X^zskxs; zR#&=2xel&dBU*hCY=tfptZfo_OQe%7i&rmF6)B_RXYAenOWeT432RuPsCU|2c>ULq z%45=fjwgOfBb?AX>LEr0W*OYNevy--V{Sj`bKJ|2j>GSMd4VL&I5_->!zY@wvi#}L zu(@34wd)JGUc~9{DXyc~aa_Vsk>w?ErkRz3rS%3&TNf#E&4=&ZM%6Yb;*6Kic<55o zJDD)*#t2v8MGl>nb(E)g@%688``sU59YLWj=da)5%B|P=-nae@3rlMppPq92laHwf zl8z{lXtZN!*F5q{)9EPI*XxYO8LH+|REGUWQyMLg)=I?cVw=}r{tezg_(L{3zkmor z41yxhDAJ59nNb!QJGb9v^V;jgqaN|#gz51PjxH#y!6Z`_FI~fN6emv~A}O(AP7)f% z71qE)H2*&-f4+Z^t|2ZBjSh@vCF_d;oiO4^^{F?S7~$hK9j3Dkr=~ggVuQu2F8ALm znDjKQEuX<&&bXW4F8TCNGP;iogs@ly)<9OwZRJ+hY+h)SCf8-yykwM9sfqvevQYAF znNITFr|;hLl`{>uZ?4%v_Yp!#tdUp>JNE%pLZGd+<{yjzzxR8;2cH21ho#_|kxrQ; zb~f$G@Bist{gs#MU!NsfD?j}B;GNlVRpgEp!b%}0SGJ-znxXQ;u+GkYmxED`E-UI$ zjkKsJs|su9PC1lE=CtuePTsys5pG~jh0bSKMI4XFSzc|R9H>%5Kr9>j;j|emp>4pf@XJCl{fIJ=HD3|lE|0=EZ7Z9Jx zn0+V26!BaV-PmB|>`m4$-eUdy4PN}2ud=kUPEiQDW5tth#rA^7%Nrh1AjnGr%Hen> zFjCR<6{EC5D#`lV0+&Dc0*hz2sjV#GH9IVxxxo1wx48cDOZZ_OKV0JZ?jD~!9q{16 zW6m@xqy?=FlSxjJ8=|Jm_ND98T8kJf`S|T0Gwh$zT3BFh>k^H2iywXW--GfvJbA#A z-5qpUFqozIHHRQ@Sz2vSNkbGUJXd1Mibi{`GZ4WXiyqZI`iB`Smo~U_@2B{yGd_Cz zh}O1CmIhdB=p7%ER|$jDXP63R-2<$yhe%c)^_M@`NRhqssm|UvT~(PnQ4-~K{^g77dMPp+q`PZG4_s`st|>M63DSO1~CyZ!`kF;I{^Dx5=xv^BF;+g?)SvS z^|p2DP0>2oshX?X&Qep@yp#`A#nnG-beW`B*%pTTFF;$UgO|kMk-1cqJSU@IeYFRw=P}hyMO*om`E%Js|8U_Qg1pGc?H7Z z@h2yozqo{T3u;Y6I+J|wFTX{RRpd#|g_cXZ5g@fCk9=HN&~zMprIDWE!K0Lx=i)^U zvZ^4rge`EjAfJu+S7>@3UMIpzNs=Up&v^B&=Ydp+Qcx(1>nV&jIMPKr z4SY`^5QLp|rrkZBe)t1|Mgt*hL{W=$Is~if?>-`)N7PGl1V0UMzG)#Gy?n(^rRq8E0oP44CwAo*}T}qixiVZ z#W*?P^6QHb3LqPx?#-`{)eP;e@!qM?M_WUOP`VI-yEuNU4aVq%tLX z+@qTI>G%%D6Qr{WAB$6yG@1^jwHQy*T2^GG#7aY1S<0DZ>8gj77U@bh&Mng&A2B+x zSUKUz!&5fTcgV_=s1>5D$Fujlod21C(Lv7h=TP1$D15`@Xi8WQ(bWv)3yd{{^^T2C zcTa?WycX~8uSms3_7J|$glLOV? zlLA|7MUq#1^WDdCT2`t%%e2-Ob?%~?{PiD~H*Ylk2T$Vvd3(Y6r`p=<0tN7d{CMX9Ug%<7mqh1`kDs#M40!pKSLpuW z9r|g7>s#{7VuZm9JXSgl5QYc$j%Y6^#P9^EeLPQ6>5|ZwNEZ~AE1Mo5Fr~|&m(i>_ z;3+PicNtDoe6Pgjmeyj#Bugo_7nqrZs20)CAr+eGa6m2{C>)Bi!1DsCGQ+QjL|%j+ zgt(4NQAr%-pb{52!fe>% z@yCC`pMU=jdF60(BjVnBe~l?}inL&m7fgWow9Bjzq?KVf=!39Kl8ii=JLa^pRMlf# zXCB0vqC=UG8*@BguwR&wN^;ryR2`n5swF~ zZEX0fiVTmj>B-6(p>YA4JgwJ3Q2Ai9mjCvdWYU9CCzgd zTM?(bF^}%Yyz&pYdPF?yfe4wNzDIUCB{m73&B&)cbUwqF5?d6Mx}sjI z@%-LDP8)5kJ7aTk9k&jS z37IA_iyMmZv85_{*TM5GonS(v74azEplDqt^_zIQ$*ebJedj~=MhRng87T`E zpv)4m1xmU^&1Gbj;Wz=!_8A=4$2y8xoG^1PGky3DNircR z9BQ=|mtMHSq{tY~#;km8li^^5l>zPcB569~_J{A0Oh>dDEgX^IN^o7DEYDD);`#o7 z;jH5F*|XfedxwpMh(>)8-}k7rraKs+b4ktjFlov1@&f0s+~DHn%iMqOJ1nj)B4o+w zY{<$=i?4s<4O*ck9u3J;Lzd3txl5~f-b+Z%2qv>b&O|Max(RM<@fSk$a>?4okmhEc z=Ay^ZX~D-o9+2iaR(d?RbB`!$Q0672iRtd{Qwsvr%n&CP@njES!9(LH@LZ2{Hl@gN zmg^ELEKyBSNP!L{wlG*vqkMtqI!uNISphl|1T}$GmM3qm>-2Sh=^VbgorQ}2iTw9vn zEHg)qrgtu%mUJ3E)=m+Cl8RAfS*>}DD~&dmqJr6M%#ujy<{4FZ6W3EnWq2|P$EVEk=-&nKvPjE6CCGN#h=E4SUleZuGq{MxU4gX2H^ zCWY$})fGi$u&HHQRy698y6=z=G?*MxNqZ?Ga==qQHZB>V*-srhkw>0`FJRJ7NQw&O zS9mo=w`NHpPj zvxG*iO{10LOOF(V^H~b7g}HF0tjMwi1Qgkn;o(EdWP~odq|=NjsN>YyQ0COPE>mx{ zm}DL|uDw8b^Ci+eop&-;6V_{_rDo7O!I+X}YmFq$X|>w8?iO2H+a%KoS{E!WEm5!4 zFh+86w9CW0AJRSPbC9IG^TEgDdCF0Lgr^LhhRSG#+DR#JQ`l@Zi&* zaQ~BC+)!ZjgjqI0E5nt`7iiTZPLFnZ=iSG|(~2xBIq0RVuLZpPh0EyWj7F`=G%ir5 zhTuYjbY;wIUweVkQJ<$j9MF2rNkMC> zvO<^wM?mNZW|M-V(gcx@O)C~dNoU!k(BOuGN|$(bg-Ji#DOHlu%#h6tx&$o*RR-5S zzrd$&_ZVjrGAyT0`gl5|cu-Qi&_o3Vr@Lds#hlgk4o}}5vbtTT`?MffOp%_zjU38s zhTl*aW5^SY=Qw=+tj~}3D{Cu`XR&7dey@7r^#x9QBa%ZwcDxg}E`;aOqpW7FbuHF0 z)@oy_454C#C^0qz($5OOKkNViT7fi$Rx+`O~ zcH94@{;2qWlq8hGOnRg0tx-^()1_dV8(cBBUs_{1x8T#Rd5pU89L^{pDJ7#KAZxGU zn1XaRCU6~|>>sk+s`G3-LhBKW-jKraNJT(9h*|RvX*U!0W}4A-Opz9NUYjCM&>D25 zAToUXlfUG~tuOQ1%@=s<;UlVCB8)-e;2KNlDDtEND{wrCj0B;r@DXHXg=?WI70!ao zQq$r1u;STC%)xF-<_f~glFpi-al<2D331W%ikx%%9ePEagwgZ+`Fg!U z;Q1WwJtv-x8T5OM#zTxQm`$hjdtD}j6O_<6QsOCC@-6t5G^@}?QdpP4tRlA#+MN+C zwmEa@D)FSx=-?fE1qZ_!HY<^3LD*J!Q9!35`Si|he)RTpPEK=@%HT#0ORG(u_7c*! z?oU%pHoUhSaa#_mE8To zDQnj%gam6n7Mc;$(GWe$>1#=(9NKFel;dN{0M^z#yoN$ZNXmI;p8=+^btO{-001BW zNkl>(OiqYE6fww!qO#WTOKH zPf9kgG-x+1k01AGo;UO#R8&1pwe51@7g|)Mp|_WFa66+NN+#0`PdcTkOxFQ?-<)6U z)T{o{*vUpFv(8Y=4rhJkIewW{4pMl=R)Ef}FsVf57Mp+8{{Q_B;P0MS&##)l{`ANx zlUxcT5H3#HPxbQFt5$6O#o}bTR2=n=zrV3k{~rzq)Bn|@%B8Tger&!eM&@^#=ORw- z&TtjvB?xH=gMddTGo&YR9YGi=0@o!qXDOA3uo_v!j0Y*_%Qrdcoug1!FeT(V!t38< zdrNbA2+xemQge*)B)I{vw3MNx)@&fGOOjSh(~QHJ;$k}CYv1@)?w|gj#7T@o(5|^e zfksJ7(26*_9+JleD##co6;%Zb3yP+6DTTmP6_571JRLj8N)nuDQkn{JzCzz}nZ+e` z2ez*+kVOM_>tnJ#%ieT=bpnRNDTOu2p+*sMVdXle^1u_UcFwTaTw$TLN|t44UCaAsTnGiJ^U^J8dF^;Gxs~qFkC~ z5pj|*8%~+!hDoZ4wc*3tA7UJV0XzkP2lY@AgbJfA!%@a?Qc_t(p3h^mgw+JDAdoqw ztxzaj-$AT+?lPN&(XeQuKv@7`x|$zeJw zXf`{{hNpad=a8LeQ!c!`#4Ogd)@lq-CTyGyQA_aborLvU4sU<|5f`p3u=iI3L)Y zMZTRB+tM0;EvmacX& zg+Z7J$2Z$D&Bb#Qmg_juM`S>o+3DiBegq&7#(>Ta!$tQrc3vo}cG> z61Yx!Iw`Z!sNi#3O`bbBzAH#7gX22f-5+vuv&oaul+p-tVK7cfTiqk}uH!3*st{CW ziM#e^F;b$-5=SVOy#w@O2c6x=!=qYzl@r^>c0Xl5sj;-&qI)!?8YM{Ua{bmVM#GqX zm7zSJm8B-X{*7Pf-+%j0p!$pys~r}WeH^PO6GL8FTok!3DJFu#z_DRXzoQyfQ*5EWAv)t^^ zAC7Pe&G+8@6LvmG*t+I3IW=@vJsMFHqajTSmKHW}J)a~QlJ&bN0e;}qXfzR0(`el|}%@D#Fmg8W*f#md~)v^9qYh zb2_W2h0t#JcnWHPq9_frD2Sqv^{sWRv7A45kz_hzIvOw-jOPpHs=^d0!_kOu{fB>x z=fEu2T;8rRtUS7tf~q>g4I6yn)&*`qdxCH*7dC4=`0zRVkH>6o*HFUXH6_DdMis8n z?WdUHEUuDp_YRw{*C|E9%eOiln=x;_b%-x(EL>HLo|n|>5n!pU!DP2!G4pZL3TI1E ztGn3Hp)ej#-h7*SU|C;Xke_ZB3kls1HntbvdokBhdll1jKN98_$M*1d|{h+-@3<*&#&<8{t?l# zB9AMM2MHo_aJMTaKP}K(g5I41>CL&GR$GRZLODEER_%=@WwEu@N%x)&hPJFa#)w&c zLAaep-ObO(1RILzxx*;S(N&4#I-g~v zgCBx02AzZBgRC<0qK!}<%ETA{rnf{54)5`p`_vL*-frpzr}-lA5afHuod0ol;x!*2JtCn zu4yhiG+*?|4>Z}aMGp&n&!hAm!i74G$RTM?c>1)5=UCFy2A$8e7*91|6^=qGF zG>T~~tuW|z*?s%~os>x5fM-z3!F3m@FK=Pv6K>wPK(n?=oaUUK9#XG4;B-i5Q+6KR zW!OEL=jb5-O(`VfNdZ<97nY>5tgqGS4@%^yPg)PSw$&yb^_UI1yl`MDNZQCrsZ z_cf|L;p~eMmDVhs3)$Sh$HfycY=e^48fmDMwG~)t1@7Oz{Ct)X{tr$w z@`dZV5@BU!w9r}$-%%X#KV9=H6|KIw-+1;`EABM?9@XIIm3QWY(}QjngsvFN(uxaG zWcN$X-3aNv`GUM){VsG}M*DIrk|eafWH`6MB{x!Y+rWGY-A8sEq(* zo!qTs8fS4SIM*03%p58cF&JsqFE;56I!tUy&35SzkNL^l-{H*WMRafzB_RrZzVel? zVXA`J_>^omrq*z90>g^ip(+Y&%Ob*@%9oVQl={HM8pCW_Qk8~0w}f~^zM`?{u=E8< zGSOW5a+`1dtGmQK%NxJ7%J@WZ{%dFX@sB>nTFdhC0y{egRJowD;Zut|W?fBltD-+U zM%64fc2Ua1ugztzgYk%@v@Dwdqa}`0p)*Zc81i_A6yUoKxPodN^YGyvtgU$W?gy0;)2k3Sy)gcwKaOR%gDwm)51ZQJ`MjO<^4ZL8OwB9 zk(UKQ;M4C7SzdHGILTRQ3UUGMrpGKXjAs>PQO=o#F0>;>%@i!QU946JRdRo)$F&Pf zjLU@Pa7eRO^6_4e%tGlY+?o%<(C=lmn-P6$Xo()ki0NpFh4Tr=@pHC&4ZKp*+e^u< z;lfMXIG*6b*^4}R{~hwoGCs*^U6Z7DG$I$o&q`7?BU)5!-&p13{T@zf7!P+oQ_2de zQI01C%8L-~lGE<|mdA};9A)svmeG-BVa1~=4OMAzBSD@Tio)QB0n?L;aH-$4Og0eQ`r=izeM%M!Bn~Hg6Ouf^+9RYAxYD5(NMcdadBbP=VTMfvR*28?6hR32 ziJqnRilTB*-f`##!zi=kI_WR=tyLS>8^oL^n5^vGD~D0_}3A>Ul7f7_Fs`|>}IzxbQ~ z-21P8>z{Z2ad&Lv;KUGg1mTvzR|0D-rh@%J#--(mqN>o@i#+ma&O&E_ z{q6y7&>#wZ@-*S+fAJgq+*iLr5QPZiVYQ)Mk8lEoI$tt-Qeb?=wN9IbhQU=i)8UwO zoMMU`#{snrlgl&8%p*%vj{7~X|6+@5ra9>iXrJ-9{q`Q0Zk(ZBt5d0jlcy=FQO*tG znc>VU9x^D&ijvN9jcCc>wF=xu#Y|7gGL18nS; z_c?fe%rwdP+SL}G2YEVWG#ikZf>K%BNHR-vS}lig)#F~{4IVbWMA5lK8MG0e%P+6o zXmBG+=s{uJ}{C(wUFy%TZi zl`-3|t?=O4l&T8Z4kM0#IAeTz!UsQo!js!wyt+j=7GpG%ZpmR1Q?EPt$AX1G;EfbZ ztryTqN|_``W2pxMEe)m8#Ja?)9KY#O6oBV27^Ix+Wn{U=aRkYv!mA02LXeL%RiQB& zio9ZUoU!*|MphQoJ1#pvOldDh43Z<(S2vg@1HzyIUdg3fE7;WM>048R1;MzNlZ`cK zOJl>OtUx32ojEdGmHLm2HBMR_a2DHgPAm1i)=i!Lb>EqYujh<+!{{?=fj0z zqO(`}{gabNm$s7s{^O6`KiIx5Z!E1l=l1u~h1JENwRv-ir$6emeyhgL{TZ$jbJ(E* zBOp+M08q@~2$3sE(~`2Vlv?6o5b-IoS|HcXylXhVOO_6h2AYe4niEq@MqJ(4VAwro zFfP#+lp}DI$9O!(6&}~G-=rRDCb6ZgH1TZ2mJc^JlrZLZxx)HoaMCz&U+yp z8DOn3=uRT3t2pAUKdB&|Qr(0wo23EAd=8_thhCl^_Zv z{>mnur8av{AM?rXgs)t#({3s%U4c_DNHRugNm67~vIL*Z=Smja9<$7%wI-bvltoFA76dlM zSzBRgbC1=HWp)Px#D?PJ(UgVC;q*btjo)78?nlpv#|2(nP)#+Va9CQap>i>Ip3W@g z*s_1;A=S7-Iv~9H%hhgqR7R7}3{?ubDafaqK`&-D)0Ak+9I}}vPc^lgk6*K7sl|0A zRbfbmCAQEkY`XZNOH~MNd~uUl^f+^Nos<0-uVLAH(nFVmcmMd9qAbvfU}-Dl@J_~= zt4oZ`6t^KcxnF^l)LcO;bZ;lq>^s(ymDX10w%vbLg?2waG6$DluD=oaBG`Dzm%U*U z9Nn)TNzY~`<-nCFMs7{pFFwTkXIHr%xf!~?yWC=eLLQ`DyS76=f2Qp*tfWj z!u98^(*7(Xk`9&CcowSMGD!^+r=lGyl&WxiNuFCKl|W|+m8j7vpOfLBW)`~u>CiAt zX0%$0?|iVs^~H#rTY^|SJUdCzR@12kyl zAUJ*+qm~`KV{jb@DJ{w>6oRVIxUNgMyiHhh*?oA2yN^$KaUovOFghZ$OWOxqQ zE?IrC#-ooS8cTp$quwy+tbuR7gPtXnXO>wj+dTR7G5Zrk7LR%9{3^CeS#Q-j>Pk#j zP}!1c-r&=xn(;8EQ&YIYBCsrnu;dziUog!qxhIjX%R=ZdN({N4dmXY0bX9Qg!7jh{ zwV&bI`HcSHfZ?nnFXu$2v@j%jiK8sb3m(1JO~Q7`!@sQY?>>Hy*Z#=@hj+%zW;x?j z&|V7=g(e6b>T8N|*O0{}$KQ{+`jVuGCAD*k$$gD=DnwyHO7cpevl7QwOp^jBT>P3q zXOLScl)U)hX{|8Z7wWemB)g|IZesH_%b5s6W5$J-rxA{&#;}{A))Z zTudJEIkj#-a{ThE4?a1r9UsIT4~8sWs8OU9`*#y=er=IFDR4?yZwI)}T$)>R1WjMk z@C2Qj#Icsapd!l*k*5jDAt*!cF4LGk#@ZZd=Mzj63Jik5G-Fa2)?7=wQR9`X8>EGy zs7i|Igq4+L*48>?nV}Re#<6q}Egg8X?I*zO8&IHRf zmoI&Fll6L&nlIQ`yv#!8^XQW<{o^tE*wA~BGuo|C>ki=p#7CBRToNp5JjbWA97lzCDM3j~rya7j*+JHuG&k2dI5}Z= zZ=XAldYoH|_|mOqrh|$}TyZqY8O4^>S1ur3MQ+Zpel}oh=?RyYKV@ZoiWgSMVZ~lN zMK5XYy)z=HN-i%u2n@D@<;5ENBbQx!k-_R$C{|w~4OZakZ9LDR5xTr~rN(e-NHa@e z6+OMgc=0Az{T`9;;rSj`DWs=pMh;aeC`#DcXt3Ih_;hbb78f+?K3?FEBqc&AT&Xy> z+~CUfHCiVJIP@PBgcsn#>x&eHW;9BvjG?*Z@X{}xC!ge)iDB&* z>hwOya3V=1ER#u!kaHZDQWAtE%}QLyV_FtiT`=xf1d&8yDbjhSpfOMn6@fP=D`m5a z)%7NL1;f3PYhSDL#@Eks+MRIk#}m$f-luNY=@)xE{~%-OvSeW+Bpw$`dL`rIk{e&@ z;M7a{CmA-WIQ=+f*tg^*SQM*u8~4Z6w_m%_`JC?umz|BmI*p*ed^z~y;+l8v@lQ_t z_|20v>zX&I#Mn_X_FNgej>wc0If4u*grE|5M&Q}MR{(zh_kW+?`@P>2 z2hR_L828zm<#`y?7h!er{-ICe($~S zJa@l6&t#KLGL)^^fhF0o;Ybb=BgiNJ0|D|W$d|x@;}}8U08ZoxvSmxOL`otllHFvp z=k9l&?>yg{t4^JH?|i6s5J0m0KUA%=_FB*SJ-X+OoPKKU1WL^R&?=w&4|{w1yT^xv zZ&a!kVR;Gt1I0KB8HYLLIhzOX52?*7(q4h9Kp6>z`O=>UaSTyxc-Ykp@-I)9a>$be ze{jS&){JIfWHsN$x26ruDAXt^32cSdhT~y|uO#y`C0@L~#`SYeR+n3hMguBAi&AA4 z&-V$MC0fk|whs1j%0A~VE^_|#RsQ|=KVWBf!suj7k!RQ@MYzjyOGqO@kxNWwGd?aDALX2WtxVts_*I45G#ormm_J?PXTQHk zFlX?~HdY>RbTH&(BgauT_x@tY=~v4PZ@Hv-i9`UvKtI2h$*nrBUt#(56252g=+R@g z_j~N@jloPK#mA$9ySrn03S22zT{PI?A!%1rwN5zC2G!>$#Ewl?w{V*a*nWvb)$q$c=j{#^&tmIHvsZnQ`%Ayh z-o@YG<>C`A&m_n=LrROhD6lXD%1~qlmINU*<4E(u3#*tkqTQQt-2T!coEUsda=Piz z??iN;?vM^Alzp4oxR;);}ImWQH5B)#)EZ*p{HX zl@K1~n69B478oJ%9i?Z>3pr*K1#B3<&4uUHO=$uACLaL?xy48hXK%b^ z-P`O%7cTkDqn-SX!s=ezYgIm6%++sAW;fM%H*)6d-Y?n>m%S$;&%DtB9kKH$;@opS zwWWggx<_Yc0_hZ}kOBfrVhTu7Jq1hzT(3f+p_fgKC}md>=9(FC%)~77sQz^>#2-?3 zbMjt_P7Fz0;5QZX&jg&Pl<$1JNy9y6d3lDV8JpD$D^we0tg1_-LquMH*1Z1aiyRIf zbL%G`(9Jxu;4;?fb?$#WVK|wv=u4!KRI4TG^$Hi?dJ)fGqxbZu+`n~;hfjN4S!&Q( zpHK`maafQ_h1-*uUe3&UhsKOUp6A^C@tBpD9moUr_quepQrs#$^ZF|5Pxm?hN{gG{ zKW5f(sZ<>NM>e&JjX&>WTN2+Alzp3fw;poT3)wjc@oUTY4Z|dgIJ+o0H|Mb&LHwLW zvYAr8SYkrV)(>`Bc+KUt3vUsLF+bgWz{hvmEUyG?4@bOyvCgxND*a(jKFU~GmFzY> zx?{unTA4~D=_`S8YzTZTOA%Vn(ee&?_-MfWOTUBPT4FUjDa!N!yx`*7brlOrKVx8(+Y_;(D0Dj zh|CzWFiLk@P3JdeJ?GhWKW!t;(4W!Xaj)Rf`#m0fGUn>n%UuLoD#bK?#t4G}p$j2| znP!1#4GMuWe`Ov(6360jYnyO9CL8przVvaG`ucyB4CLmp8xxJp>UfZE`C~V4ROPEP z=iH#zAD?;jNhC^j$)u}5D_(hLfk~7ioSgE4#lxS4P#AO}I361IM>%^V%}ETy1p29F zeP|e_hErupP?FRF_yCf>2JM0l! z7Ev?jlOH}}@%$=-LBjUtKHI%@%GG&Fq!oO-8Cq zxKofshRd(da`0%({92ic;TBXX~$l}gc$`f5;5LQ`y4OX6=VXxi6a(pfn3*5W6$!2>{=Klv2_fV!^30|X(=49gja8JT0Ftu`HcCh;7oXvi-UK$ z8h!-B0o}3T+L?exU2)heC^@is7~|Lw=LLfZyplyfmZVy-uvp>U54XuvL!;s0f0?SA z<^q!>j7ORzPq;s-qK5;bNk*0%Oe%T2sW|&q3F%rydBV*SSmNSqt7Iig)11z|2Qkgnsjo4L zrk9_tfx&TxYYAp+5{3O>Fv{*M&UlTomA`%dYx9*_Y|mGH`=$0_>NL+wa)DX6BE{al z{K;rjKeinkOPbuWWMnBZvXmGJAx0o#fygDk76gB}1NaYNph`+na%?=?;=ljRf7br3 z|LIht|9JeWFz)y3%XZc~$Y0-)*}rU!)Ia?A(XrN<;+?;}LZ>~ZvkjAE#794hc<#+P zlyd2wJfgAYFxtql6a+Sek(sKIfpKOS7t`@Xnj4;u;aWrB+Ja;#sJRMR7?jbJY{6ut z$y|+NS@@1c>!OQ3v61r;)$<9n&$vAJo?*8$Ajmx`FFAbohu`J3*T0Tcwpp&9BQ3^| zYR)aa%((wP{m~J0Y%8WaA-{lEyp?2iU~`F&@ym zIN<6_0pI&K_sBz!$Di#pXhocQxyI)oZ*%sI8pV$_PqtJ1lE+7rjD`C22RTU<4>wbs zVnR0S5L8<<9cXR+2+?+ExjC(D7fN4m?jD}*=6Q>AZ`IiUGl!j{5&iB7A~O`01!!zbU@1d5 z%CVKft=V81QY|PZA<77r=BwPe)Z&wS+l(fH#4||8XHr;9!U^#t#j_;7Z{eje%8@KK z1Jc8SpZ@+jYE5$Hl`8$c6d@GXU!S8?E^tdWd12Uk*x~9oD}=*@y*n|p^A3}7Mr+mK zWIJQLYnT&?k|&Ub!O?I$%f&sLL3hYxzo7cM3Qq3F{Mo;Z@@KwXif{jE zlGhsEv1Q{;GkUNEsA--MxxwTDQ-Css==0v)Tl}RCz!<~-@lXCaR>>EIHbh}4zWLvH zC0EI6W8U97brjJukZvkeaShMepqTzJ08 zoqJEnGsAETCTWh_Q1nAhmVuD}LD(Vql6F8yK~Wf@#1OcWh9{6(BW*=ca?ltoB|#ey zhS}u`q9oyTLKtb3SCB;(3l{=<<1xxr6gp*l^N^#HLk>p|d3<*l+jbcaL#|zZo{~F* z)iB(D{D8BkF0p@nK+UhBO-#jA1g@Y}aX9MbSe9UE*5mT|Ii7A0_+-70EzOjRSBi2y z;QH^(F>@hces+;Ki;0tzOr#_j;(Iktt+@EJ1qV+fs>_nyFH-h52Q1u>oZQK$ph=;z zTpuT>;Wy`SD-DnWZ445FE>hBH0$GY>$7K1KSH88$jW-Qjd;6?jn8&K5%$;g*`t>T2 zPMCxlK_N)@7ntNt!o0*d_gOzmIT;i@+MduJs**;a`s_jieo_R+%*rZ6Nq z9JEu8_b1%kjJb7~ap$Dqlg)^qZfAUUl5?+{G0_6cR-h!^Ny>3IVsJ)k#tg`?7Wj%koI&N7=HP`+GnbTFa4 zvBZsUTa4Oe{^d`bBIR^PL|)#g>vv z#epQpmjaU}lr2NEo9;VzmPB_?2Svp&#NF?2@h}l&Sw+v~TVF!ZF zUe4Uv5}UV1G)}v;AEe}|!SN+08yS0_r9=aP$u%W8&Fh<_?2J?MZkC(D`MJ`s+jDj} zRFUsq(P(KH3=8_*;!d^YHiVQ94?oN9N+Eqq8Z0R?+md5T$&Qk;F9aijl>o)h@9vm4 zuC4vm9l(?OkND)n&)CTdVOOeV<5sE{UY*?b-Qdm6=I~F#cJb(_9|e0Nt6Y}#4|h8Q zyVr}pA-BM*z|&h>?A#A2)hxdFd>hLbJbEwWXe*_)>>>k$=ztl4WeZ9QG9f@4YzrqpHB3f*hk<)2P@Swqt~ncv7H86Rfip5~*o6nj9XUpbBUo^f~=}nOlE4 z;KKCf+9GVlK2k2%8g$6XkLB(6i&6q!N~}T=G59EXO`<63=SBaM69eiShk`wImA^y zN@+^j0lf!`<+tZ}_~{YVwMBNG_Q(bX5rH%5;V4ZeU3B7;Bx8J)<9T4K0%5_R)1^ow zqHs)K(@ryF8c`Jq#<8e1 z%dBnx2(6ohOMjEa=pM@h_@n=Ei&L+?KzL~Q=zrVA>V+u4DwWBt622!s$C$OJM6p$)MCc>nI!U+w?^{>eZ6Uzqf|;#NCRsb5y5(LcyT>yI3R{D-}h z?pLyt(!VezDeS6wp}bZ8sH?`OI_+pJ%Z#baSYk3L*u9^!bjiW1TO2%yc z8Z4ucI%7B(lf^MZD}3;mYO9Ji2qh+Otg#cZT%$G&Bu2 zcSi_Wrc_j@`HGSmGn^zuag0ZV(FJAK!df(JZ)em?nzb2^-Y_H13R+dcxjCDQOD;E7 zTuQd#xfO@l7%ndPEZ4zxzdZTQHN$bnI0DxZG^!S!lDL-Q;no<>m0Y-bo{c95>>iA; zv__GkfTSom8B7q4!#Fj>VNBpj?yvk3-&}5U*4g5Z-`(P!i*w9&hs;e5sU;J#C;?rd zvW!_*Q+EZ;lFe+@rQ#?Wo`o$6bQF_LB9_-$d~|b*ryFD5Sn^4t94CibAZgYsbUQgo zn6WVLv(wA?WG805ThN_=l!p4UgTPQNS(t$)Y(v?%=p07uev!~T?@|gpDzgrEf7oXA z<$yDnXW0B%NaKu6=W&Ws)0L)Sy4sF~q>wUqh4?(ri(y_6Ev&fTT6!%|I~(S<*D98c zhWQJgaVli>meXASC>@)L+K~cJNebH%p(RDvQu0_y(FHM)IGG{UdneNq^S=@W?g1B{ zf7UofVwB?=eg2;ZcDZ;nPR2L4@8wNzDgJFn;%<4?$$YPPi?KAuFzoN?O|N1S1T0y}oC*YiBk?RrreKi$hj33p zl0tiw5(E}Abqm)8Q?XEy;r_qfL)ik~4wz}QIDNH6ILIiK%J^jux1M7um$b-meUH73 zA;L1;dGaYi5KwY`F0H;q|7ghJ!9FW1YgB7Bo_+CUTCEnI>!GAet|jNz=BSlxM4b^k z_det1olQP?c+B2VGt;VY_ClG~a)rkqcNn(~@hE5G{RlhoaArzaGahH(F7d^Chpc}( zVA3x*vsR&W#=&(REG033trRnXq!gY|oNQ3--(_Za7j^QGxZfs?Q>0LglM@EVt2piq zswAmaYdqOn$0RQA{_ZYmYH;g{^&iK$%4H^nvThT0cj*r&XanKGbv#$H*i_8bp%goK zvn`(P7T9*q)iY&6r%soE2XT|1g{S%Xfk%5>5XCWhmg3tfi7h#r82qxLQZm#llDQgS z3bJsFNJ4fyBf|cOYNJe^$2{EX<4S{XYvKZixghWq*H?V5t+>2#Dd4$lO=g=8|KRL8 z*IFqzZyxd0*PdlXRK6nQLn;UVPyU zl`3o;g?w|Zj_uf#eUPr=q+_^svxDct%4(T+ACCEKHzZ6nd2X=KG(17W76jd#+9bp5 z$COk?^O8k6mMp#CVEGbV8#4?;bYxgrY4PbF9g~c69{uT4KuZ6 zXL)W|Xa>tOmpyk*2OS2V*AmeZk&4e_tVVmy!1}(d930_E|nTj zCWUw6Z_irgv%BfwaCY83oLeoO5z@$LY?wRaVmBe)FqquX2y7;@ARik_6@^JOjshVJ zWlP}O;M#(wE6IhRQn5h_M#c1QQETW&U`d1JgHZ~<;*b>(#~HcM$RcC@QVkS{9vmhfI1fE(FGc+$8ujg2^_7pXX#9O_UmZMXFN%L^r-(DA$Z3vXnh~W1MPf)YjV>e-NgOAvzx3a-J9PQh^EU0{9v|I0q~sV}%fWUX8Z`$g zHG!{~Xp6&MPBF>p1PxkM$>HIMQ_%pcz0bz(h>eqoFix3d1(p;P$|27L(IjQI<}%|H ziHY$?edTTUz`yGn_DOQ|C%a)QsQ9pYno)qSAJuUYq<7#xG@5gaIIKNi?_SP-^|Md6RuqxToBrIm?Ef0St z=k^a`oSMRP4M8AjRV`XIhd?-lVTvdWGl8Tq;QNY3pir)bmIB+6OpHO=Q_tEk(*!kZ zdV*=eU{s)eIO!+U&%2y-Gj9C)B1K};J{~c*8CFXT8Z)akcf=SttyWnxB2jXm-awY z@hxWN%lIXW>YPXQS=f9uw{*fNtT8vM86+e8 zs?FkylCT{y*e*}CJx%D$u@;OtHS18aZ2BtY=~2Qn z3v-;Uj*)wxuoT|ra`a=I(IMyZJ8Yj6j6#FSa!%R_Th(g>-5oL{nH1d5R_W(1RWZi4 z6qmAFRAvI6y;$ezgEl|<;*isIpGsh(97URgMxdm`wj>qL#kUo)<8%MfA)aM0u169} zN-AYP)I98Ic88jcVL>O>^fNeJQ7n1W0~@7a)>D)$gD50t-?_p5<~E<)K48VuI5NQv zO2mcFor93uPx@?cOt{y}ND9rO4{LSFd{weCYtgJ)w5m3)vWWCFc7EYmpXRkPlU~lo zPmT#ZhwFc1mBkAswmu9AI|VB9ICv0Zfa<(MJcQ|WE(j^XnnsNyWoDB6M;KE)e?Iti zmda&q=gGrA>Azo|F*iQ{aO5gm?iG`NURZqVMueff*UAiW~ziq$j;tVp1b-wCA&<_ql{W+dI*AcLT@FIImjBE$T|G^ zv-mUfh@ggFZBlMF@oNE=yG*W6F&RIPIsAp#3JRa+k#!D0>P$(rm3KRH`*ffz8TNjX%6sL7_7E6E zW{9JdvaM(a4wKBnXp8+=@bRNAkB%~S4#!wB$F2)Xi!C0tVKfLSE0|FbYk}`LER+;W zO^=EvDJg+c7L&0cxUxjffZzM>2ItDq5Qc|s$pt=+S`7=^ zv#8Hllq)_d7UjCl@gPT9Hq;Dmy~6%U#AqWY5H_nT74C0$x%Is^c1bXQ*5|^DvtR^A zk0;buErz=p$`Vsuy(KY9f>LN9qi?->%6awL+;3JEsk>Jkal8{9JBepz7R~wU#fnU# zG@5jC-QVEIBqFyhQMi^2ZA)}4B@UGmdrGRVK#e6{D)EiL{_6|C8^7{*0ff;7I!WX& z{r2x#xW5;l?2i8Hpy~gs;UIj~pD}wgab?>(Yp(a66hAwCs`mCy*0ak?T#z1gpP2Qn zyHRuXa-C|~XA%R(!gdXx{&}BP(_y}0apg>n`Na~ox=&}EqS0t6FqWKV#7o6Vs7dwI z!cyod$7BQ;Io&&Fg=XB>1Pw`1O1S*hSqd^LtAb+aFc=T<%TqkpnJXwe0m>Kjc4GQR z6I^MdBY5GN*Qr+OxMd3=E!v}9?yi5tXq2$Lw8G~feZh-w{%0H)cR@w$9=DmjHct{6 zid<9Vppg_Rqo}2nk}~5#M0zaPAJ`oDR~S0&m|seWD??vj9yVD&o$xV3zS-OI6)PEW}f=mIizKwc0T3!WW*=oHSUZqFfld8 z(SW9%pk%?TXDw#y4&98G3CJFXpqsJz*b+AXv5LO;$ULfJ&AdAJmJYvh)Hu!`I;&@hbJkc zu_jW2=Py-RtawyK#w5xa;xf*1M#F?9ufVem<4_T067O7tGuO-f`yXyF9x2v4aJy5m z8%ug&!l}SxsbX<n+z7BUyri%Pb4`PXt3tu?Q@rw%vL?qH{QaC9fxH?b%jOBU`F{TqqwUlvKm*;UJcm#+`#u1(138Hax!f6o0{@>q>kj$&DfuCCZCu23{~ zev!(;DP~sA&{$YRST4O$&VK!M;@K<6YLmjPagx@#QRxu)4$avHr|a;wwE=Tw%}ril zq?$~OLzbl+1?LzHMjVA2M;WAY*zOfXxkZe}ul}8Hu(h$rUwpjHM3fmDi=GKc;{;nN zEHULj&dn&!EVwMqDBiwOrsWk}I_=}TnzcEL^UGCgj>Y<6#Mz4#j`|)?_r{Ebz)52q zDcBeqURrQjU8%A7{3_4=@;6w&d516VMc9!c{_-Otq`XXeXkyg=G=3V|t$fAotNy-F%a01GL=GL8Q?!%001GRWVp3K zX?DS~ENP0$zxth^Y`rXn6qyzIgt9%6R^xQdd8b)!iI%691Ir)xv-o!Hlw6%0ip}0( zcIx6Q9?yKG!i`s-$0~Ptd}qL*onr#S&eI9~QHtmJq)Ec%MVHrZtWqwy>*?w=wT2&ry+@*VP#Avj~cwpG>Kg6vjw5Z}-QM3#wQ85^t1coLv%fo} z+a6Q(XIX0I+-UU}$_ghluM)X)WLAZ^ah{>Kz{8=E_rW29k)g;9^S0q!&0)2w z2yDr0MdEsf`AY$fD`oOpOcdpux?H7x#>3PdY}+D>e4?mNn1F0Z&cD6L8>?rKMVq-@^`|b-|(kILGxR8iBDazWL$`)?~=~U@&Mt zTmA2pUHqNqvhcg>ncF|ipILu8e2_#%6nFK05i+uErCmqHwk1clC3}{X$5M(TAw*Xo zM-nTMR0{EBsrhRQ!0TT=0fdzF9z7sfT$J(Pu5{VU4Cd+O}m`Zks4wZ!!8u|b~?NBm(4x)gxG((Oe%L|G^vpez8!+kIi8=JyF zUgRW6Osc`}J)-FC5uSO6BFfQujt~ZADU|Es1Qq;7gWAF={6-7QuVVWF%J!xSL{Ur@ z#Yo4Y;tMXj+x$Y~3ocdrMD0F5{cxQh-y867f6UJAHnPwx%~yG0Zo-1yrPioYEqf>> zs8oHHRu_5c`89s=MW45?miW<+?(t_oJ)$pXIRA}bp=FLZJ^q~L$;X@wGe%KNloiCr zCMu@Pew5~Ph7qEec7Tb7Nn+>?HBnyB4<#Qy8gu=e%eC`0a^=wy4wqU1zw%4h`TEyS z^XS%NKK%56Pe0!0!l@GNZo+9>ve2@yDhkK7&<+GO3){76Ua;^k3C@3IiNP==E^@-% zlw2m%E|<9EG35&`TIC;o@rT2(s`P8!ym;6+ zti3z&} zQ9q;B@-UWQdC8-3rp&8<<1C{i4{b;E!vQ-_dtATp8kY75!w@AE;V7b7X>j@UbJWW7 z=<P8Y9K;{Hb+oU&rP15QaGGnWpcWYzBFW=Gs+)<_tqQ%YmMw=byuxTg55YQI3z6HW>;j1)+91Yah{d*N{?R zJ1&+xeG(L=Aey_5P7^3nbe>}J9A(?so{t?=@TxOdK?5hKVwK7mVUwjfQp#!mfNc}l z3IFBVP1eL7yN@>6e00FS{m~QFlP1Nfmq^R2oD4M&9&K@aJiz3dx*f78+sr2G%#81I zt$NI>XCdev@!n59?HI=xs;*>dq0ZHF4W7Bw;_8h#;$ENM z``%qX_@YOUX7plsvOD0$vWr{uv704I)iT{w;+1V8ro3FPB{ORt?Y2P6g1E?#wjgL# z3B!VWyGLAT&TzI>q8N^ej*oc%{e3p}bNVJnF}-D>A`qh~*YWLte2xA=!IQg3L~TP* zRfPKmQV3>e>wNvWCE`wpo5zMx5Om6}{@>4CU*gFZ?RujkzB)wx(frw}+ItjsY*)0y zJ$+)?N?XEYmTird6g??KTS##zh3E*xXuAHhGf5eMckliD|NF21wL?H51kv%4P+m#6 zRj;-({`M!|`OfdZdHMCyX}7Gyct?I7ZLnXxE?*ct;)}}{%5QFL4y@xa#?G^a=T?@l zmTV)gy)tii(-EV7jN`2G^z#n8+Zzn_rZG!KPvs$Lp&3s!N*ng~1}rblV7c?CEXH>- zUc6jqdp~9OWHRO96@*<)o=(AuQKujqYod|CEld28#1t9|L4P+P8s%73mkVF3kwuEf z>yNR0n<6vJ&d$=EY@@W#xF3^-8iQs$9ukHVhS3StdY!bGu=A*mnl11uE~UD|z4v;g zV}-X=V$v?Ldc{Xa7D_l+GNMwG|XV4!O1oPF+IHEJ0%t ze|7;?pT(`#NpUbt6>HM6u-p>L3%H(r&SU!uLI|=fC(AX3F&L|aQW7PmHv?!rWeOYz z<@ne^6|X*nTdgA;f9m7&e57Sh^BI()z~Wo=yVRpCKK}RtKmK5w&pTDrrMGcTUBeWb zJkQa76S;VugNet(j^V?HV}A1CCLiD0q;bbTsCq%hM+({N&R%PmeWYBPf+5lZmD* zC8O>%EG3ByJ5T#;-0ZS+wGPHGT+b*9aD9h2pIzY7+if0<1&LdwRC500{Q0$i@!;l8 z>Se`MQRvfFOZf!p^-P(gq+KJWwTcp;5{qO(3ti9Qjq*r?0_dDl&Ywnqq zS$lPLb#*to0dzNj1~33-2+nYZw7ATWmL*vhBOH!kC>-HXgni)~{|~;fFYK@{9Ck=y zQ4CrRi=>7Uhco6-AO;xhOE-E$RoA}dp84gso^yPW7}}C)DM+)f^19+X&w1bXywC69 z2jy$#=F+8d<@|AcB(RN}m&+^rdsBP=AU@ka8C4e>j++)U3gzHdO7x-!G-_>nXJ?#0 zO6N~`)*{9HdCy>pwP9-Evj^vV^yyQ2QB1@4$#udny|InX3_E9I;*mihP(B1@ff54O zo&Syo2B{z!&57u4$-(m-I=6hzA7=>11z+>zy)oCGUqg>7RH`1`(IKBb`6&-RdBkFC z1LawI=Y7Ju=!Aq2|xaDp<@ui;mlIOQ5fIJmx#Bfk<$DZawP z!PkpF;^Bise(=r-;`QI6_3E$8=MhB?R%5ahou%{GGN*(N7x6DWPx;1cgj+9StzlMbMgt9MLu1hA`!|^4@-6>CY23!m+HnA*)4jUDTQ6U(^=}|=B zWt?f3k`MjZa5_#17d*<>DyY=LnkJW)LTDdu#cWFl}1hnD@*`#-U7KKuH=y8`&h@BL%m_}BrG0+l&6!F0Zu3Q7?U~H#PsLKjG~3EWNT&lWr?? zxN`M7eEDFP{?P^FvtsT9RP(;xQS)J+kb*Qbq}DPsmToV_6d75rXfM|J_G_1^2LboK z>|(_n0&3Fvrl99bY7K{UW{9U2FBAk-iK9L0of6Xv%VOW>Y^P6UyF~9W!nzsNddOMt zl)d{y7TR@=PfmEc^MvE0BZ8nzqt>FZF`dpLl!ipJ3Dac4i`&1aTM9))kKKy-hhDGMj*~ITUjTV?792UoKM#1L}UjdNaVqa;XN&FXK}n90$*HadpgP zXTVyq&qneEonS;~_LyMsh%77E>3j<%#hn7YL`8l%OUsQ?ctRiW_m_$ z3$lsf&e{r{w9H4Josk%bg+fVzu!=N+t>rSk$(&tKSc^6iB_!7yb+VpfbdghE4=5tR zO5J7mJcUXf;dqoA?Sod~{kMDjN%`)L7`Uu8?UoYW)q`I8wniM!QXP$k#m%&v zvU2$@Uw*X1!TkY6QJ@q^W#`|gnsZ9#920>x0xK-3h2FRznZ+RIOp}*xwAol}^3MHJ zO!igpjetT>6aoi<3IxVj>U9ULAs&L%hTGS-IM_YIUkd5mv=p&LdsE5{k0hJ1^YCPz zpi`PL>E{T4%F@yb-TpbPdYeak_qg-ytDKx4Gc)IGHtw(z-k_4zDFq8W+W&+PKK`7r zEooN*lyxZiB`TGGY9(Me*4Wfwq~fi^D!bD%)2v|CoWgj3=LI;*VtY>!-2>V|%=TtL z|KKq{PG6(zZ8CM+pj@=pIIh5TB*q#j^t=*q)I2j#NQ7YS0(S$H7b3k7;RU#Uh>&we zq~ka!>3|g&tx=A{mEoUpd@^Lb_I2EaHS}zX&Js+TV6ucfnW2*iTNLvUQ?0ql2Y4c3 zy||B`^k{WLqQdg=qbcW&uMwIF`-c(I!og{Vs}z&rfO17)q>ocuL>t59*@yHFj#%r2 z%pya-pV0B3>G@1&1s@*8d@)j7LLS%#NDYFOcAaKP^7L$;S6P@j_~NB!m*&e=qXelG7rl&jrOarUFhWvl zF40=M!qV#XFV3HS`Gc*s>2JiP@w16n_*$C!=#lq!K9hZEa;t<9N+|@k5W-FzDf?22 zb0K9<3N?16N~JAnNq``*KU@C!cM8CN`9J-0{_EfRRlfe~zbVr3NaT|V(~~3H*jC=r z&bueojr>0gOO@4Wp0uV9#fRZaarNe%<;&;C<3}IAKYDk4+5h%-+f$BAuRJ~}K378Y zM^Sn=iKXk;9&+W*b9DR_cK3F0T>%D^3t=EpE_kj)iaA2R$U!N=EH{j1mf^(|M_C*M z&tKo7?xpO{#*_oe!lFx&2>d|eN=X<>YE6Y#6$GI~c^VsAZY%|yO-9TTLp+rP6_Ccm z4d7xYqqADU7=um(t!9g~NVxalLzDx1=l3aW&h2YIhwHhlx1Qti{%2f&_Bwz1drX!V zBz}uh(PuI<*rJ=QGrKG9GuP?yK7o3Vs`qP|T=S)(=EV1;8CP`_9LijY@ zh_L$wuC+|3n$3E^x-amg=Hc;-pL8{++F`8~u+&goDjBYBHTn6M*Z9FZd;I*j*YWEP zg_O*+VU`)jv0;$rWGzMWQe{3Tx(wIX7C1eg5+#aK%fYCW`m)E_M?5PiH++PI z)=G)yC57+$Ts)j2MwTxgMI;uioqIR(%wROUdcDnqg8{R|U;!m1z9)J8&JrIzIOXYa zPE|=#?XX&PI2~zh5KwNdP_B1uv$Jt3-Eb)F@b#jUJw~|G*{S$sa%3O*LExfOt%Q|I zU>wKIq+qC&>`EoOj*ufIRqQA)m$qgFW&ZU3pZ$dw{a+{mZ~xvu2H@3S`@5n@Vi6B| z6w`?a+lx+~4wb(!+&kH1c5>!DPjW(swIbZ!==3H;a42$zTn>DDe*XG?In+s zCpin7F8$+#`ijHJqXgGiRB8cvF{NH>@Y0QMaoRuP>H}Wqen1cy!1l1=D$kbl9VACeU8P zm5N8xGP^z4-#_Kb)fRa)VQjEf4^FM*@bS&sc`;mCD! zmTRA3^Ar`VAf+VBG+C~Zeu!5JQI12Aq?khENQL5)XQG%pP@p%w(L?QW0%GqUB8)52o~kZQS7rd1{HK1>>2ZTfN0%zR#|D2CaO` zOvsa*#CW8cp}e%lf)}wV9^)N+Na#bmDp_qQqH#`llJnWnqH8{D4UgMvl8t4b8<)#0 zECy^|>hS6PeU`7Zz!hY%! z|LyUE*YB=hy}9XZ4JOtcUF7$q)Rl`-vQlysJ`oRhPv;*V7DosW5PFhQN#d#b+OO3D zrN9(`g}ks#5{uM1Us3?K64!vmz0EUz^A;?X_+ z#gG04Nj|}AWsH(z#-}-p^)+62=}j)(yhEe4MYC~}cCA7>ImLH8s`VB#TW0;rGkopM zZ{w|ObDY;na>dMT5avCeUD7Pq9GdGTT$8aC-D5$VGYnP;dk;u0xJ`ws`s89l5|z2M zbiia)O<6!Rqv3P@QIwBeNsb&W>^%3K#r4T-TMd3Lao&rY|6y5?}F`(put1N|Zu$3j~ zJ*F;4)U#uVMmVy-cLlYI!b`e@agVx8XjXliO%J~!DLju#$Hl9=bQWMT&bWLzq}7p> z0+&ep+`M_2Cp*Weg@97%fl~A*IcctW?LR0%kWpJvY}~3LgABLo^5oNqS6;uw$N%hz zAk__pR%LN~+W3jGI`M))i6Sw=7%7z$QprqVMoP(^qnry@sgaaw=Exuy_*O6< zy8fjF;GIACC%p6fzs)!P&Ton7!JaThE=-XNq?JWqq-g5|N*`H2T@yBN8!OJC6j?=Q zMIwYKPS4c$XVd)7&C8|gO4(l7J5NsrBmI#TcFT1|b!#!?te@aFJ4hjjXAvb&;kgn| z&IeJ26_f%&=u5)DA`Xo*aFIR%3(|IQR%tp%99GY&h8YlKZ>IOx!xJ+`7s3a+6Ye z&YQQdbE}i^tvg%fU;G{|F~P}WjM8K>KwFI#4uhiu*ngjTxkOqikS(9{WEnxuTKgWI zg$ueDWu!=G*AEzsmcdi%Dj7JJi5kzc5Ng_9OguA8Oo_y4VzeeyIh}^fC=txu z7HV>UFdEmfxSk;J1f9BIxvp>=pOub>=P8m5q+@Z)9+X{#C&`TC~ z?bdY;pC0k!k55=xt1wLq#xp&C64i!s%jK0{SRyGhmakT*w9D*$nsVv+I-F_T70Jq- z5|?jP$V|fEJj3xF7B)gGmLgGPMKSk|3?`In4peg1mY3;1F4%o5!to`#uvp-YJ4@{B zT(EyO!}sToRwWh7Hv-nKmpJ=!ifTzl#|BTiEY&;?rxvHuL<_~$+izH<{NqXY^uCZ& zl}`0>dU5F^&vObd2!%-|DLRv49vD?fL8JtISE;V+xC15CL)x-`*2m9yV58cjLPMj0*Jg zi<|EH)5G*9@jyR!bJbOs*J{t-+lxLba_(i;+*xaTPNQ7r^W8I^yZbfjwFc3shXpi( zz?b+Alzc_S7wAkN9EI;mTqVg1OJ*#KHJ_E08mDI?4v!Pimb|dU33x!scPM<9^NT57 z;ZW~~gmuHyANP6sXCuNwuyZB~^oSyLW&$rn>n=mdsY&s*(4SMNCHvN5;y&;}3bd!M5 z>MtTIZESUo#Hkae=O_hZZ;gC#Ol%a9(;?CxflMfQl4>C6M{r->g=9d*&*tfMp^Geg zSOIaGa(tdMjwMJ*QITXd$(CQ``#*R<&)dTB2PBDQni#AU6dIfWR<8zJ+P=&(g@N#W9^v=pZst{d($T#L17G~ic5Q?#&lGmttOk~j6O{1KG9fV zx$;JZ^Cu}*TUM9rxFX~8-6>XB{6JxV>Vm@cBuiJSoE>QnznIav;dA?HlT&iKeTUle z2CbzH78Wl@#-K+h`=3`jSMOxw;&E85gd$7(3TthiASEtFTPaMclsl2q_FU=o9mk(Y zoLE@zE9$TN=jZsZBmn>U|L}kC-~9dWa`zX$CyIEA)kc^iLj}GUo$nguIe{LDu?>q> zC8$iCwd`-}#H@Lq>$#y>>Ywx8&V#JG-14s3H0R|P)|Y?$!Re2+HL6kfuWWCWIq78_ z9_;htYu{oTjnUbZawrKsMd(+lH5VE626M*SJWB*AAaDhfNYicwT;1Fto=kaiIKlS> zH*5bqlZ%~l;GQR%8GaNk_&XZtWLHk+_#4!O#3XG=VL=Nf62a5~)QY`DwEANJY0 zbct7QKS$qN#woSQWyo-vaz34q6*+#r2@BUL%ak-3ayE{c&Sv!bLmIvz(h#HrN{&Wc z#8j3P<5{0OFLqg2tI+RHi6cXM)n)(51f46|OD?mtPBK2h4>hrTp2S{9Vi3tAVzUGW zBpOo~(m2LiGml>XDimWcZV(hhmV*heK2zqrKjYlF0?W^Vfg&rohyuK{$E1D}wR9cn z2Uw}lRuL5EY_xLx_DF{O== z`ZlcJXrWa>&=g$uJRTlIWX9l1Nl#QR6_%e1vD(mi&c`4zV+UC_-21p7Y&Q|o z^%U4mKe{}(P_YeLF0C#`!Z;}pr!Wu)7Opw0s=zONEwvKWFWo9ByMG=&lOZd2j5lrj)THdE_pnEYlGQnz?0)CLRdm? zUO_mPIRyxTD=e*gg(ORrAHC?XU>M$Hcgf+oW z|K&bsJ25#43(Wi~K4;w(@(euJ!w-CvBj?;yBJkT855vM^XkeN0l>jQApw0f0Oq2S8f6R!GHI^WAco+*A*5px)4qnsAP0* zbvBc3sTN4bag^gZIzDq3sm%PwB1IC?xJtUFks(g^tApKI3d3JT%VJCam7 z1zD^ao%=j|_>|-QjLCG!qL*YLw$Mi!)9yBBazL zg+{r87{xS9Sg87lwVaKc4LVyM^;(Owi*sDp=kV!(bY{7HqfSt9$#cWI-|unuWkMwk zIeZ-R>Pi>4Xt4MA2`|54$npx-R9I?!3HglSq=|Grj5b(f=X(WOlct*Ktl;4DkJvlv zvleP@ET-&6b#zd{4FU?cj$f%#SiZ`%R~W4^;}e2>z}Y}>>fNMNE@2TA(nIDGn*DdN zS`h2G{kv3gk=Ejr456=33yRS=#Ucn76lFi4zG}(FWq$HU=Tw(`tOuj0gP83xnase? zS-$47b-j#q9cGE8n3l=BF;O%{B_2=iol)s{=)w|5hDxc1REl1AL>$L#-B@Be$S5@> zCr_qypC&9{sZm)klXz1GqZzGcop*lsn8Ocau5Gm0IT~Z^oQ1gYQi>_k)+U7D8^(}Ba@?gdXDjVLl0^ky_x!1H`2qZzI*dHP9)Bg>e+ zrrGqkcru}NT~Th#D6$!4FXHh24(IvLG0m1SMTT%gg!sx`7{wH60;XV;`{-ms@1jqf zCfI30l?g}gWsKEGIVa3#X@=DXgT)HVB=yP8cZlN@yYw7ZxnyZZm>yC-{1LiH@Iys; zUC~-9(YWp~o8)*E4{uX&{q<`p8tssFtAnUy zWXz~m%QTluEMG4Z_XV+u=-wA3Q_cM!^*H$;;o5qG;V@z_&2b!o>p5g&$>K8(&;3G~ ztSI>42Se7^Z*p`HP_8y{Jj0`RCT!lkMHs9mQU!xN&q<@n&=f_EF==*s^m$lsEjYF2 zmdTRDW@$_kjg;f2j_bwJGE%6>5q>I_U)Wq1Am`6&e_a9q@WQvgi(79pJ3bPsTp^td z@EVIA7_0oU3R){Qzt(Q%@pP&qCyRTtk46W@Q^j0B9vn1!yZAcPG$^8 z2QjLibNY0QRhIp|bCz$E7){2MD`l2mlw3SEOyh{oHIMOy<>J1kvL+bp7o?*mrjQ8f zBcwx-IgBRr?XN5=NSz9Px%L(H#YG5-&J!larx$1JO%#VyMK`IivRuInEKb=aHiq1l z>>fmT%PEs_#>ta{Syxe8&$<8Jh~>*6Cr=|bF0E2)OE8j!`ZA-*kk(S2vwn%rtz{P0 zJ)Vx*cw$ClEoX75%(#Dw=MLH4IHK9?QY#;M!(AxlJo#4x`UfdON<5*cI@1m*rVzO_m}(zisk-DzdcacUEtr z3t@{qx0BJS6f%{{O(kk7w4Ey9q(V5kKx(Uv`Refh^$Ng;|MH)5_m}^Uh6Qld3NkfuaE`~XYpR#hJMX%RGg((Y-kjc{m zfyS6PLXY`!p6Ef~v>pA|`#1oT&DUJ zx|0%3G++N4EoQR}*Yz2l<=lC*PG_ZzW6CUE@-f}~At(DOK{+6f z441c;IXfJ1JfdjE(pHtJJY#EngUN|xWH0Ef zR=9XPBN;ib!f@%i8sGY@HuX*bO0xfG!W+N*3EUvr;s%hgHQF$zi~jWr0KlzpeNPl|gwa~yxB?-CD%BiN#;djJeq-@X;RMZe+JCIG z*jin<)}(>w$x?mUbk^^M8gM=hE9QG3OB1c;XV;#CD zPx-a~_#Y4iRc50uD;t|U`|3BT)vKKDevZj!y!qY#5GNb5bomuVMTJXS*Ev1!k?IVM zq|vPKwHufC{<{x}bBziF<4I1G7{-S=Q9L6b&I_mgcV>(aVYI8U>5M^dP7d1pXqjkI zA#e?6Zx3(+iyI5{nZb<&^+1zrLu@=;-$f_~AqAc*F-3w#;(M@MQ=l9&>oP4AxeDkk zglsh>NK2*ZdR2uISnsLt4RbF~I;Ba3ti+#Y( zdw64*4}WKvEWs2x0)v(=p(}ZDF(jKFb6{36I>U{QxoSTlon}mQz|d*q_MQ^D5!ob1 z+A&ja9e?nMX=HJfqWz4=)Kodx8S?4-J=U+52j7$a3Xojon8>g0hR17KEhN8_-;LX|~HmQB2TKY~NTV zY&awn%Y%2iY(2Zk!Q&CV!h~6D zZd%UvVyr4yy4=8VLds!{Kl-1aGMl7){g1i_0KBL#^k^0@C8aa4{+lZV-; zyKnp5eYf6RwWGl~S{veNPncpVgp37dDvXVUodCuYLrw63hFfXS5Bb_J{w+@TKW9A2NoEnxyzn}$ zg$A|eGUY}M@&V=M7VFn9RmyZK zhKd>CIEpoAjL0)AP_2|%RTuL^XM|;7PnNed_%MU zctW20^Y&@kB{!0Vb_wj5_djf-O^w+^Bc#9@jZRZ+QIIDwy3kV_|e6tc?*e7TQdOwKEH; zur^1C0<8=F@7v$}?BK({4gmQp3&5Rk|AvsI3TgjB;JCsVD@~Rf8CI)8xt=wqYlVDK zYfUz|cwmZ5>NGdP(<{>?{(8A7m!hFwS2bHdKh8g%4f2&%MKu>XP4aZUm;ZE7pj?F` z6+&5p&_S7)FFyDYqyB(#|A@8i+dTZ_eWLM@o3DP8`oc1`))GN|lT>T!jRhu?39Bm` zG#A=b>I<03DW5+cu(-I+Xf($0BttZV(KNI zx@Twwnvk4YiFyC(398jHm2yC`=`xE8>dPUgk5k5z0>3IKWI-NV(!9VLm%T3v zqFzos5`=ZdsF!o_$YVAMSh{Ap|C2IlbKs};dJ)?WE z0#;Mx89Fa0@{DQJ8I59=wkkaMa6qLVvalM` zUM?X#Fuu?3XCulrNx9^*v=w4x&aIc5EVtJfj%G;L!e(S6FgUR-znB5@kfw95+-L<>Ca2 z@YLWtx=@V+4e|d!)yTEgP3N!M&?f8|Kjbv zz9q}<`@YZK`{c@1w{DKzx5ISz#2yR=g8?ui1W`<~s3lppmzVkIC;RX4gZ~3RS?{(f zS(a>xqL=^>nAp?P)46Zo9O_oClh4`P57pxZk(6kFv}>*NT&u$V?qB!@OW-$bhW#ni zqZ})QS(p=LhEC1F2*EVWaa>7!S)y#nXJAzXi(v&jl$dtG#X*eQDtY?jgwEnSq`bi7 zi$uL?Y|6TmOvhq!@~o#vy?x8FZ6~PJOdQ1~&r_jwA|Pgd_$L?>fk`l=K#tZq@QW3| ze^CB!bvORzGk`aL_z#6PhB8UeSt<-j4ANkPu$(rI*Ue6kJ`;ZBEv0N#OokT%DQ!mz zT@TlveA!Hbebt3&CFU^pn6t8HhsTKAA;#?94$pFbWWtofeZt@uEJ{Ys-9 zWrE2xVKf{NRO{?Le!!b|-z6QNb2*rC^|iaST3ud$^F6B78m{ZGw6w}(GG=>ggNKhF zqb!@>`3Jwt%Y!|VG(-x}Wr=M|CjEk|cRi$SFa_NG$Yrb5CZGZJLdgs9vf|!&(3QMqLP|hP?qvlrh%kLHBp=sM+KH= zk;Em&l0=h|LYHWzh{h$+FvWHRj;kP8WRRAe9`0Z}hE^@*kC+eX?p z!gAQC4A^$h`SPnCJ+Vl$ZD=l)EMHwkE?C@tyTbLi*9e-LXFng)X~f`_3@;PLFH%Ce z#Bwk|E`SmSmBj?L3KdJzXh>YoV%VFoxKhJW@OOXrCVnfU%%Rz-pvl>|T}SAgx?g8= zx6PwZdOZ61geXkG(X^}Akgi31?sE8Gg6Da(mI9`O92IB~^GNr*KiI`!a4nByVhHmY zPd^=Ud_Q6N4TqOMosvuq7oTS|1Dhl*m?fH4-61awVO+9#y8>Es^?L#78C+XiWJzek zYcryuMbwvccLV%(i|IwgV)xth_RjKX9C}4KmM0HCDMdOvQc@X#R3ght%F>|AM3hA+ zgp7pIQ-O%ksR=O0y!BEsn+Uv zK@F2nx%;h;INX29cfRujgs`|gzhp9-A(SCYON=puqk@%<8n547rPX#=xGk9#34>9B z&jS7P1W{zTBBJUmYE=uV1&%9OTXd*46rGwN*ExshGd9}_kC><97L!TJFjk;TOp*~z zGqR|_vec`e&9JZ%P_52aNkTw0Dku!N4SPP9&<*wmyHr$0H92H_R-%kx7FUTgL3bm= zwmn9DpYcG^JF=McJ$jFG!XyAA5lUi=O+Jk%vyx_Dvr;qYG-jO6W7rU4zOv+%Ly%BN z+XiJ(Wx{`bXT&s&hyzJ=cY%+7r$cMqCDfNtMl9{v{L$}U@bSOx<2N0QQ{n|aPd}cb z>npfSS$m_TEFow};;8{q;sgrYFR=s&%TQ@5p4}f4g){P0^5u^&$@JW5x#}poD;}Tx z;TggxKKkuzRF?|&A4M2pla+#{%1w+{(BAQR@%1^;sKl#DhL;iD)iy{?u2Vky!y}s8 zpd7)=k5eY68BTMqNWFZPasGKqnHv^69$A{>*$T(8aBPR+I3i0jx@#WG?JC!I+Az&H z2|@~6Bd};+ad`RV7{9uU%x*HioaoWPb7OL&2B!z)NjP#WCq`>!Ii6FdNohAaK3O)! zn3Pq6PD|O>DD}pC&v?1S+ccS=e@hP7*2CWlaQhiAU&ceLrIC&^FT#62(Z!tHP4~6+vFMs zmp!VrCezW7pi(7GQ`+q=NRM`>!~WqO%S-DVpS+~es*`0AS(;Pp8g72OM*k$`hyTSI zo$VaITBSD_FijF#D-KuQgO#g3)A8K$TcBxP0oQfVF=T}#$qZ3kkS3ZaGuW0((=|L! z*X9PADj}KWSQIFsu^kH~fLD^-pkiY&SLP)#tlVf5PYSYWNix$cRTHGLX{$?4^F^$< zhcJRfS13`;1~$W@&WmTToS-JAt4;--JbuPvRBS4Jck8AIO?k<;l4>`FwDcwE1=$-O^`cyzB%z2our z-wk;1(-|8#0*?0#w?0^6Vda9$vw||Q7|mj8ZHLaf1$Ihj!DbX@%!Z29?HX^syUk=2 zBkddnw7V9SibK7vkkWAX{nwZkXB_X($g&c@EvT(YCOz2Mcnw!~C@)%cRu&P9ebOSv z_k7MT`xqofX_l|lajDQhjp_F$m|T)f<`s2#R+5DVjG$GwF-3vvS~M#TLI}3k>b!P! zkvG5Fq5fXU#7_A0Kkc*prpwC40{A7Tdm)Y1o2c?Sqmx8mKKgtt!sE)x-hL5<6X8k` z={&6nq0%DDG=|co=}g*AB1%0FhQ2_LG0cDnOpFi-TF={nc?9DYCO@n# z>i5#z*m+;>8^dU$>wWu^`vb9mnKKMa!dXg%jPL&N@3FbkVee!}IEk@sLs@|3NYd1h z78$E`NfC!Ud-?+Dx917M{Yz}uW7zLgt<@-sf_k%wB_$hMJM{ZK?!NmSHrKE4=_j8c z?1-Rd*ti+6uxVjQhx?zNa()_ex}Ty;!_ry<+Z9~jz0UGANz!-F+OYdp1HW#QO<*z= z#Qp{|(P;8RlVqXBa$WKi(zK+} z@UUGAgMiYYm4lgFP-Za;enycO7z|@w10+Jt2U$YM`Ry$gesYQ}B(~HzjwGfs@9mW! zGd9V1NYhs+rD!y(EHwnLZQ9&ggSXdHzIQ|My(@<6wKFog$D_S-2Fy6gpmBAP*=3JU z|L_8(EL4zlax&rL|8v07j>C;_2lNh0?tHh(Y^Z5(G$_&$@wCLVEu<3Me7{1itvGxd zVp}djQ(?=1rA-&xQAj(dKOA9OE|#uydfcbhRydZ&!_WHs(eEEpUy-b=E;5{ibXObb zQtnwLITB@Fmr~}&$ix_9Oj&0B;+m~zvlA(}l!i+w#bgcu zCIk@!sWCczb@+ej0pKqM0KfM?{Lcs>z!;JBd%}P)MJ~_=lV?^j?uT}Fo+P7c+_oLzL`+@Jtl43!J{=GC3;Tu<+?`COfr>Rb!3^_d+ zMvb@-EINH|z5i>xer=tL!GPZ77$LxREQBza67F2>VoS~Q7sr^OjptQRwnOjil<8#5 z(ZN1`P$j}GN2UM(AOJ~3K~zn1tXC0QU){gQm8;h|J3ryZ%{!c(9nm`*AP~Iq{t}NL zol*%JbXF{+w0Ql^8`yryLSuu2{T`RSh`~t?7)~D+0P_-D%1QGRl%(S#nj*FB6Aq_^@h%(2LIgW(LR4Js7a=kfUK{@1cgeVJ&q@dfh z5v9S>l4v|38O2K%Q_=A7er`nuHcH1>b#tCJj*t}7pp<2wMgu>*k?FKx*AG3bV=g!SroKN=% z!r;*9AA;YxBqd%NZ~3P3T@z?0r&D=~&dn4)yL5 zwqIv93kljjqfrmz#2h^xF?g9%(;nH_!fi?{|Yrn<#+%Ki|(j5QvX;f=; z>jmB@o(w$O6t-olslgs%j0TZeUQh^Q29)`kGUm*ZY9NIeBM3o+2ohs-!uZ{8>`=Z_CpD+Na6%9@QBPlXU`Kx=Nc_?!f8&H7U<#P$W34|Alj0Ew~{3WIS2lxtH&@!Uw&u{e8_P)du=6^DZ- zQwlAZ3_^B3sFEBluzK@ta4H-<{Mq>ECkF==tGd{H{Wrwm=(&?m2hx#b#_~^q!WfYm zOoG;9isD=vPLz}vwz39N$SH;yfewpz`rs8eDiweNB>A< zqro?~zc9KKrYIy@D>EAx(yvxUP~8;DY8h#@i_m?NP4%C2x1z1;LRL?wdg#}zHOCQ! z*I?bPXwyGcU%a;IelQxv^rz;eDCI+CtGE}M`uWMRyV$8xU)p8&=1rCY$)mkvkb+jA z*j;Ng8jLx+j2T6mI1UMFHL^72&O7gN<>oD3Jbu8!!Xg)E$BZUZS__L5T9c+JwR)XA zD`+>?h-Ncfr^aj+(p`7BJT6&Ssv;%Wp5}``8u94k3DqSydYF-%E^u}b;#Up7^V|Ow z>+3rl9qe=G_B#m6qv3nR7f*>RcX)a-WTDj{3_}D4Wm(i44eUxC;{|9tU~%IXZ{K~7 zYr9veRvScWh16Xn%>^q>n_A#tr9)C@kqb63S;+cA6;~Ei?3|J4kPC;3Q?gl0+3l7r zSrc3$5_geA)TufpCg@=M0hZ&T?723t zZ}{?$hm@BVS)@6Cs2Lq-EM=2sDYxGJ0JqveIRS+fIF`-nL63MgBfdNY}@HsLJp*qA%<8;k?6Ag#oqs~8vgw!*MGk<0KEBw-=vs~MVZ7f-vnUFQc@NQ zuyi<$Rl9q$7!RImtuyJ@-Y}tg91Zf`@{QSlQwf5qW#@;&F512wOkiRZsee1jQ&QEyx{0XN=FJD<5z+Fk550pkCX{p+q-j4gEg0Z7DYi>me>*mhEWtTnkGE|>SJp4C9Kv4 zI1alvU#GQvm4)T2tgdcy>-HOLUwfTAnNeJRO~sQeR0KB{BNp9=tKNv6j-u%bDy|?m z4pV0ZAtcn6k#2wx3di%Y9S12TMwhRgZA${3kmZ_Tsz{yAy!R~&gf1CR8^p7W;ZS0E zivE7eblRcbo-#U@EN@0kqaAAg7{BK5`bUd2mlWL{hiRl490sJ5g#OWt%{xAi9}Xz{ zE=8`n^`jaKD>cSxg5RicaxuiI2J{CJqkhT4RiCrR^Z2;QS&ni9*YCC%pO>7!NEx0L zy#3)Or;n$^V}lVADI{ArZd32ByyBBoz*sD;u5BWO`?4I!Z-)r*PP|@sK{3rt<^je zyzG|`1h_&jwsQ?_d*f63YTOWwHqF8j~UN%I(# zCLAhE(jiL5OwI*cw>@sZzrxD$4j=#g$Fvt}e0lKCDAWZy4*1~vZ?e_h;m%v%B4i!o zFVZ_Yn9ua@<2jAuDYj+uCHv1>X9Gx69INPIEb?Nwq zzrexW^?&A zLb zJrAi60>`pwR0F!57AuPjtSq$Yw%as2ZI-HrcEjO#WH8D@nu0vdFnPq9bB>cu86_1= zIj=s;TvL{ir;02ZF)M0l;}MNr97oYR6wJ;9w-zdN{VHi#GK(NSE2#`^WCpcOk0Mnt zZ8MpTxbxl;(_Tp_3tC%%kn~<;T<+&MN|N;rLKxD%!C!&OqGa$0rWYkb&NnBv>)};u zEN@@M@q7#tAq3L)z;ek;!+1O;3NwzLKccg~i)CV>IHpKL8iAt7GK7xk1SL&dQ|2kN zw7?SuNt|JH!Eb#33ccZsm*)vNf-FrEW1%9xCOVFe9owEdmK{qWb0KA+i{hKI|1VDd|61<< z-xvUX@cM`R+0#$iXfEP+mngy+uTne&rWA96kd|gTJ{I-nni&szs0p6PxjX0 zB&&Bfu2QW85R+S*SvoHhupmJJ!;+-L8ZaxpZzhv_FKQslgIbT6T_7^Ycw_^ z9ADrzC-|zy2fw+ETPp~ob22gH>w_P&zkiAEbm@2nKl zj-czM+_}0)Be1ajCg(4oLOh_^Sm5MfkN){F2QMGe9}GA(OZ3VG9$#okW+ZXWIMIam zGNQ6XB%AajIDENJR%%MOPU^2y7>Vn8IKGGPxl}6w%bgbOMjgkpP)g$1HjeFaW7Wg5 zYj|EjtFyq;;tK6%jfUtEjn0sw#8!er_z0K>mkL2u&5%VxD(eW#Mr*-jP_p8cys=S1 z6gkiK`dnOQ5DOd}!X&4&?9$w+uy&)4E}>grp;25Xu`e-L9DO~b_fk*%y$Nm&>~X>|Dw0DL3-`F~CQ{Tl1?R>$L^pEi4LSjG)K`fGJDCH&sB9 zL7Id@3Wu`56uB}<^#T=D9N|43o@Lr^nXA&t*L>M{DKit=9=o(PK}#FyDOFVKt?AyA`?vj|PGfP6WIRB-EutvlWHiE{ktl?m2Q*H{18U6{>sN1qRCs=s zMytd5@jk9!nU{ltSH>6fHsJX9kdMCm+g!YOPXBDm_1kY!tuFBJ-WNQ1^nlJvh3?7< z_kMDK7~W(&4zb-jJ9pN2^!RIx7Hn<4#U$$SC;!(&dXukNX}rN(Z@xvAWb`l2*uC*O z?|txh*uHX;?VTG?Xqt@-8Al7xar3L8XH2`>ZZ4(rwmh1oKeP!FbA9 zf5>DqV>q4?PKR7?CmbG+xCm1m64FxPc>zv!fwe^k$9Bo{f@xX7_kDC(;Mnsp(nz+j zyb36VBT8W6HfYM#-e4W@d(x{kOpzg_FummA|0vz;y-MBrVtK7`^LT$WbZf;% zVzSQiLalc2Nl37cdG=}XFQ!9rVcB-5lr^<&H&IdziXvG)eEO)nvA#%ad55Y8x$wwL z!1(fMtnJUnw8ZAVP~8@9Z*aZz5RsYdBLNf91`Ur;c3jl^8sExM;Og$ z^o(bpJfd1{6HUiROVMn#sW;kmmo`{kze2UrB#Bc{4z2DANfKj8MGyqcdM8wZ3e`@9 z<;4p3KKTjDoi=Aj&x!MdwdFe7D>jXaBC+ZmCv~!N?)g7TEOr-C>h%_jE4y?WH7q6R z4aXdw_PFTvNRkY#HT~g~@p#OJdO;;0@cEN7&V~a9!#?Bw8PASK#Nios*<&`2z;7^2 zTnarm45?LX)VmcBK8ve0ZZ0@{`-)B7R_vdQIqb#w6_;AWLs^QA%`2>KY+%`nT6GEK zdn7s}oI#i-R5~T!6N^D!n zPcSk=s0<^Lc+?*zlhH7Xrk6#UoGWGZ6azSr$?sp9{QokC z|5pZp{oWD#mq%=M*F_P9xXrfES^O%-s}$eN0w_xe6~jU$x3O@F3sF?IT+1IR(O9-`PtL@>C^HwX>&{%qnJQ)Fj_nT9GJ1co z_k`;!Hg5z;GIGRB^AdEwl zQ!=_Jn7t@TCS#6XKI6%QFBx2%v$DBMnHPjn!YoRdWQN>Wq=iNpgYP*^W(ob?kVM;b zcV5S>Hu(6HhkX0yYXr(6IeLbb=7dqq@hGO0hOKta_qSrMwi0BXkZFtcj$y6ik|r}I zqXCD#kgPN~avlUKb&R8jY*mI_w+}cwxZtzBkXRdBd+wWHsO1UyUP1p!H%fDjnwMjy6qVcYW%RL8MV3T#`VEQ#eP zur$?$ZS0_hS8bgL5Hki;2stILr;n+^k7FOSP{4ntR)RL-DQfX6`+8Faq-~U(IfBpXg0KfQV z8}+UbN}*gIlVunX7$eYT&P6Q>BP-R4s<*a<>slgFCw35Cef(wr*3RvUzr5?-*txRe zWJO}1ofpqWhgo~4+7KVU@s51!TI=1xc#`+0=0V_BV%xG~DXnA{4!&iCs;mVuetHbG zZEyg|KsLXt^m}J0%cat&6V1lh)~obfOL6*2{kOb!b)J@MKw9%bk+f}G&&RSYY|BOn zg>(YaJS7SzjQTxhlM#dSGp=5{OYdmJwe1h7sAWj1WK7t5_JA@J=m|(yBXmjsQAsv6 zBwm3n1?eckbp;p47xd2#aQqs>NkkMU#8Je92Ve5!$rC>P{BxwV7>s9}_xs%c@*W@m z`A;$FC7*q{N0y`)(IuwH$cqv?@F;<2mx_OPKVx|OoFy;fJG%zos^MFPny)eG z7;S7C45-N=Hx@!x#7ip4F)#N{`Qj`w>s#PB>=1fK9;Rh8)<1uNP(d{m=w7Nz; zSjI}4JpBA2YYW%u?S;$+A>E}-MuXn`e$jJTHBus!1wtaE#+?vDz9H#hJ#( z007up+z|O>j1>e(%MwPlMzW0QRX?y7D%bMJUJx_hcwztC~3`2RKz^exzrkgz;QjK zluRZQq;2840gYA%tqr&DzR%+F3OYB8E_;jyBU%ef49_E;e*Ji!fNLpSp&6Zl! zrbxLwzhIhZ;#r?8oY1LD`jJNYE{?A#0E-f@RwWlc7nehx9A&&b9r1kcbiVq!>{CvT z(D6CpEM)&Q<=&Cx>9mU*RH-!Ebkvxp%25cGUaPQry-9N!%D$l5^{83_wM|L)TAkH5 zYOKHB=HetID&XuaqEs#_x2Ud4me!UrrhMfbY-3p-?QVzNYqwZlUSnZ#nOe2U(#q<* zEz5GQUc1T4>K2R38!Rrba`f^!y~|51Yi{VO9Gg;Wj49_@0R={ZF&f_!Shk|-&vPPe z+oE0dxOw{y)z%`)tDux1%U*c|=y{}9mL_CLJZ~XAm(r9hI5Xbb4v3?a7Z)WH)1Y2g zkPbj7e*1fy{N#&MM!BN4e1&Rj$+Rnhpt^o+IYBiWovqpJ)gzQk7RLv(^P^{Z5@kkX zFh(0gA*D*ZTBB^N?bN6H&wg%cec{df{=`vMri3h|kXjdcIj0Hpi_b4s0Katk|F;7G z-3^44qL@x_nr$&J%LH>hsSrpd=1^d1U|=`f9|fH&J4;*k=3>_hKK{e=sI;<{!i|M3 z+>`xjIy@6c4^M`Uo+R$_+5&aEY+PA({!W?>7jsejtSEIdi^tysxVBcOUa2xVJK)}f zr!~+EGcr( z^X~;Wu(2!)$DiLifp6H}*rG^=)ay33GNiK!4JTq@vB~zv3bnfAwKv|uwgX%zrr8d_ z4`?sfaobfgAel=u2uN{cisyM4VH44yZ|!h2t3t`hX81!1fi#uhbCvCvQmG*1r(jEV+MwM9|PspMH+ zQj~M@SUj7c!yXMUWwGPq<|A63rm}jAvD(DyT)}rT;{E|z8-DY9*Ev2JaR0On<*iNUcH%RgNsM5 zvWA}P$BwdcC6$&4jX>+7_{GxyFNuDBmD?Zw764$scf?kAW4;9_a_nkN7-JAhiTPtP zSO+`+03ZNKL_t&`EL#|i7M3gQs%PMOk*R6 zypPA1;qvh0%soH7q}m9$xmmmF$~$2`V-6JOeX#B{oK&9EWPNH7^T=Bri32 zo{=UQaWo^(bFRPkHlP3OPZ;!j6lF>@$;fj}%@r5}lUae$67%ZNyA>OxoLg-xLm>q? z^Zt64j#=BhhO#X-HrE)9B0B3;>fL3+EF?^0qyytok7<*ARXHLu0i?KXbB#&Y`#)u6*@lwhlX*|Y#-Gdwm}jd_?zapB-ORg|=_EDK#~Uc0`< zWiQ0Bd@9u{8=F^fU6-OL=q|0y8Inemr#aI0uxy9((_=2qPnd-v8%qI~!x89$>svLX zPDzYHR9s@faV?@;QM9iTiW(QglAh>dR~qc@Y=Dqt`TXL{3Qd+}q-lyLQjGGLrh@+d zQ|iq&N?OGJHc=X3i5$w9a5Uoes|)1ejGym?nk1o^cXp&AxGux~9I?po8atoo0krZku%i>Tb$+_?Pv1{3d6jBRqv=BlY zqf4Xpe{}E9FS+{rTLpmc-}(-J^7T*ITHFvi&v2Vf3_?g{%?EDE62i6x%96seq_Km* zsQOY_IHk0!RlmCAI@a}`R zEU*0Y-e~;BN~}JVi;}X;NwbLcof}B$;Q9d< z$HyEzyN6IILC{7Q1-i%yXEQ*~+X69{fJWhra5ANLdP48?l<8>5bT*`adCKAQ=M-rP z!XPYxlr}ZrrpSR;?T0P|o?k&SA5=+Op-aQc+76THoQ)PVd^WCJqns3!Ml+p_8TR_~ zgKY=A|H0p9|L{43(-GRXIGHq<}qW_fHDJJ&Xpp@ z@b2P1X(7n8$95;DE+cXyaVkEzRhHK3Y;4srk%N)Y+VC+}Mx|1tQmgUs(Noe`Bh;Kt zsB{&lsgOZV9G0X#3)@x1qk?fiLKm7hU)x|XD+wwMdgrH%M`KP;j`6Ei;wWNqWsOR` z$@$3%7Z>N8pB{63u+NjP?=c>a$jX8|FSvWN$@)@2>92A+P05QKBS0dFrx}sEgB=7Q zEvl^sYnA_-x%cYPY)kL^ek<+t=C^Wn?vp3ah~$VSIfNi52sQ*8y70&I7x1MG7=mGg zhHQ{Sn!|}F_v!AcuCDav9ar+jt{xs9jwn%@GtvVz3f&joyEpcF))W4rW!PTv{P_jN z!4qVXpal5Pt3j#I;f0}U9o2S&U!O6Y9dNzsNN0O&uV0{4V7pwQx+On&GUZ?Y%?({z zKvr=4@O?&F@#nQYtTrJBi$+d9jJR z-K*3jWnxSxh3I=Gh(Zb<`rbc$B?0{BQvv+w8-LjUMhIa>W1*51loZk=2xWxOeKkPv zkr?Zk7EeI!g&;=|gU*I?#7LOpXyCh|YIc`{rV4Eon7ULSE-UeQSzA3V%rqNF9-dA( zoXT&$y4i*Q{!hQ5l(1Z#U(i^1c$5)L#^uW|c>eiE?4La3_`zF@XNNf3a{k4qc zhXaby7^0wVwkV-dS&mRCLml}2KmM=z!5{r8P1W%2@Bfg_m7F|yh*Al=-G;ZlcOPLpKq8RCv>r;-bJR91H6aD%Rph~!U+M~|N1Y|CgkKtzXkmd$F7qt`>w z-3lcHw~Gz)Cm*oBT_Eu|54qU#o%@nM`_+QA&WTA%F`4n;!8cxMnS@BlZM99&`Gt_$ zdTXU^s?cpWwQF`=Xd3V9vQ<(YrB@c~FFtB$v!)Bl*L3_SE6Q37hqnPd5*l2C5fEUsVkI7o%`w*T*%u%4 z>CgX?_39ayXU|Yta`yR$tQXf@zq;V!<*)eY7eC?Z)l1f^Yot^>dio9ii+}dtk_}V7 z`JF%DTR-@HzWaNBNScqB9^B`_g9ki(_=v}EzsGPiWH1~voD6vM^li5571G1M`0xG| zg%<2e$70o>O~#M@^q=$2yWisE^a;l&4|)FVQ=WbL2|`9}>`5-8#G^t*3@ziykokNH zQL(;VQkM-;L`)`;0bFMhp(U6pLC;A17QDdejDSH8j$n{8ni{6XjM>2+#XQA?ltEhX z`V$E29MPo=_X_Yiv%v``NB5b}4jBvwO!p@kH=|jD871r%EzwovMzcRFh)71$1FY@( z5?=+1V#s^n{2nhp|Ag7zF`s?(bAk_KgCSBIR_i6N&OWEJ9-#z2cp4jt0#1i;aaB@Q zk?WPAV$NiL#$meQQNEzrRebt<%TCRR#VImZ+<)9l#%YsaJyeY)2uZcLBsH4dc1!Cd zllxC-YDcrZWO;ece4KMX-|@?jE;(mFNE3wCHYfQ%_U8{SWHy-EwoRkd6;e0BIqlk7 zc2%WZwR3iLv-7)kBZWW;X|76u*@O0AFea4ju@dr08PM5C60lNJ$K#r{Kk?{bscl#ptboJdGjx7-C^NM|5H* zNutX0p$c6YZL#6rkSx(GmL;`~Jb3h&$B*yxFaG7fVl={vwyIM6unKkw;fp_na}ok@BMF6RyD0Fxw%~6gGFmYkOHkVLTQxN#Abs| zbDVb=RbyzF3@tzS)0||WiD*W-;q4zhBHL3;rw91Olq4B&a57~yA2JwEiA~0^*hlLE zNro{679{sDZRhZTJN+}wM19}JrXGdAH08a=>KhcfAQak^ZyUUZ!H15d59Oc=bR2^Vs!rz z-gZ4tKp7Femw=4kn;1jpcgqWr55FH{97N|jX_6%P7z8qk7_F39Xs)lWgiu~;?fYS3 zs!k4u`&CyT%WO`&?Kpe>oa<$aMRQXtUadUK#&ff^ytr<7eYRnJbw%!1#I_`x-6tLH z5oE#Ds^ak3=K5-dl)Wyl)`qIQ;MMsvbYi)=+ya5F@dWSiu4BCC zkzL2ne)Thc^|K#yb@qx+Kl&*G!+1JH8O_c0Ewht?av4}%uerUtCM1G7G>oT*7-O)F zB}&1^KmSW?({T3k3oc*3;C69=wL99nrm8Ds6euN$LE^f=(eW`=RZ-PDs_q(jbGaO}XLe zTTgkts;Kpd)#fHC^Eo9nKwx^BI^vemBk&K0g{RQ>AN zOS@jQQiw!J6}2+XC|zo$H`4hVE!9RzRVpD`f#`&k0pc6P-&YU+KU(tZ-xa^L1n?$) z^o>8nRwdbNPncpra83#dF+>?dkc6P4cP4gi5_ang^5M4-s=!CDOqwE<6w)Xu6+#fT zZj?5|F8ENt^Ule4e(#;(ck+Sa!;h}G-hw%Jmy4C_@_@giQ)nEMuO5HYD>dQULkgW=4uTC_ZT@(x8&;GO6E#S5zKj(pUz+pPKUS3hC5E6Mj0cI_q3 zcEkX``kVic;c$vknkWTDF=TOdPPN^#y1AyQcI=iVkw{+TjE4o@IU*&S)h&)SHrh70tR31IY%Oq&pxQZtg<_bIrUQ%`G8ylY!*~DSfaR@aap|dU z1%)1x6$66OcrR&MOF*!$uPL$_CdokwluY^Q$A3n*Xb@FKx6Rn?Hf+{QI$N{2zGAU_ z4MdWnpcv0suU3@XHKW-asRY?zLftiFqdl_0m?R%EobBVI;N`PtlvT^4Z~ur_OGmlh z;EY1fA|LGMtk)&aZgZ-#qLTZV*?s=WAGZuvQ(ivb(aI4=vy^-H?x7RiYaE0?T{m>r zBTb649rbpNx0bGHP$uPgo^y6~j@f_2&DBeWPW*V_)KBG|2}fIQt7>(-*uH-8yj|YB zaJ$XYZj~ib^%-ZYOZFWM?>!LSwun2K>FAt{!S_`_@JVbc<*Us?WW^(5Odx6y zQV1!OCNE~=J@3}082XS7ANa?o*$v*>D5`7FVxWUcKhU#~)HmB=h5kRNEb^n;k)T zn%W{%peQnA;u-H5vO-X;9ebwsLH^>s{)q=9fSJYo4Ed%B$CB)NMsR&Cyg_t146WxF9|hOtw0qNutK z*Y@TvtHl-L>5M2MNjl=@<`p4E(zHMt!|{W6*gLw9N(z#}geVk)$sQqqN3tn9Y8%-* zeVgUB;dXINB5PElS={tGiaJK#el+HbR}235e|(C_;D>+ml=bx~*O%8!ra7mxjt7rV zNwO5FbLuwqgT}7IM|ysMwRCkwTUT^l!zhK@*Jp&mA^B*|>echhy<+cKXzHyH_!vTT zT^n3mw^AvTR4Ak&xZKxO61^)VWJ(CBg$PFKMk}>bF)kF`N+DKKh^;`BNLm8+uIK;i zt^b=yKmSp^|C{)&CxAEc$_GUr)yNGcQx7IG(tv* zN+@HL)=C_12_1q{atPu@SEm0RkjI;k{xs$r+6`%7mW$LqeYAOh#i=%t@5y;iCtP^CRAV|0yT$o#1U` zb9s}~=WWCerC9@$`uN(wB7e)-ihECw{?ish?k zD7&QYTD&lv>_rBHj%pkD=J$_zeZA%B_YOHY2`KXp-HV^_@ZIk+FB%T^hA27UW^M6i zjK32(X*)}ZJx#c+OQcfRw!s)f*j;n6*m3gCcj@XfWL~`R+s!r6cM+oZF2vviLOP?3 zHOd4fq#=ePdY2Q)1Sln>*Gjiq>zx*IDMPrCQZ9sGNo0!<6_Mu7;N#7-|I1<@zftb% zKg$XL{&paEf3z?1y+eYxGDe6Y2)q~Jt`#7}gc#CjI{{>Vx2~d2Mnb8yU%(@U%sU}X zqHxnfIcZ7x?DN;8v8C%;+Okv^%kswCE_v|Yw;mXi+V%DIZm{ z^2LYOWFtY6<~)7-U9K)((ba+5n zGf*^5LsXt5OfYgnkb&K@rY${bVrZ%c9|hC-KBMu3>3ojX3E6PUeD4q;67D~K7azbo zPq|s4Ou=xv$7t^!jdcW7V4ISrUeMGf%j+f2Km0k--V%I+a67*LM;hyEI_G%j+XJ>m z&G_^wSLYWL`5zFM8`5Y=CQqqi!cN@hYP~`h6Ow0Cks|6tjAAAVGhX__{-5O&Wt+ka18J*L_klr2spp|Zo(&euY zzmT^=umD!T7ASv%z~`6u{_r)F|L`>>fL~(+q@+B1PH}P{UzGwWMf470xEl&aEn-wL zlE$`naxgh{Hy4*esYIB}NF6g|RBaPwDxzFhCFd7SH1kIvo_+Cr?<6fnlBh{0eevS# zV%OBIfAHS7_HA1?(S@^oFbb1{w(62ZmUHOBO~_RMI! z@5wKMNS>!8S|Xw*L`jyXBx-^{Q)C6pn`;uK$Ol8Dj~tzzQjGV>hdI&j*zT@4yImt= zM!DG1M$h0#a{GFNuE(slw}>J#NT-zBnuIBX;S7?ocX~KSuth1S@Y=K57@u|Hc=XE5Fi{ebhxl) zy}0DrFaCmJ9B|5G<1J$S#;2~MO`^^zzR zF&L!I5keDfgLM&<#?~cLX}W5|C{rxA70dOC>ES&F#iaDR?b0{Z&ec^JLWnW?0HTvh z*T$rk(Mf5Pu_6R7Vvs@zsf6@O%GT(lRzj|12-m7V{I@Z(5P}Ut>|zKF@MV4f--P)4 zb?N^fiu(Ia@wK%8JucurQXAphhGaArv1?-pUfhX^_Kd+8m55OTCN@>8$Fsw*+iYc$ z7-SM6QX_+L4v1cqJI`Qx@VUq*>qRRMH;Ze&`|f=3WWPAxG+kjuwtf1YKiFGcp5KJd zzIyB1zwe#zMoqQTX*$5!7SS7wL5v8YxcK~Iq|yu~`#9S$J33|mU$ArNEllqATo zO@~niA}D3hDq%W3=4E<@MFdYN9^aP(B_QQ5#!k*-USM? zN1_K5#f%stP2D1uL6$Hdch2JCIX-mEPEXiwR$M-R&Ty7+@9~hsJ;Cg0%4S`2 z_GwFbJ7qfhXUKGjNa6a6E!Ap=bAj8}uQ>nY=VZk`qk~5XVK7D$ti?sJZA*+1?;RRX z;{sP#7Yz4~86|^7o)4_8${k9pZo6E^7($G(6H?Ynsg03(o#>=gG1ekRFSSu3VwIGg z$%;#1j+ay z`|#uEiqweHS&7_3@O`_yzR*Z9@V3Jk!(cq)t{Nd747fdi zfo&T`v;ITP2O}nXM&tIJJ`iqM7d4p@gb{%P= zQ8J)Sq_Y+kCFxy7AcZ1PDR{`z3?U+$<(3$F4A$|0po1WQLIiQW%ULVA3J^uzv@`(U@W|Bp-}>jlFC@q6>Vmq)AFXEKo^7KHX?gTxK*@4J&S?M(flCXd;I=++w8{s_nwSy zFVAqcWiXoJoh5ijF&t5C7OXB`GCh2V>l&0!m>u1tCG~OAdBedFkRblb^em!`~Z^;?}l&|PvGj+85b{p z!t;;*8fg^SsKa*-lO))#WqXzL{`dYTviu%RTXS{(39ml+h)@3Vuh=e@?A9B)*5RXM zJV<%^5Wa1Q&Iy%eF*=9nG0z}~y`6~=QcCXFJC4EV9zrHUYE2-9;AKQ_ z30)>bwP!o4tM#Tb!>QcW?zY@D-S*XIiFBnJs?F%}Jbp9F(qyyezoy>+03ZNKL_t&y zf9aa$BF_dNb*_12(qg>bF7ehf+dly!F*srXTbEou`w*cFX)&bhI;MxG9Nd4*U_2!& zhBW1xc56{Vlc+lYj1a_5l4J>+MPzbdnCztt#+uROi192#WepcE8V*h-l%>Gig7H2G znR0UaJzjo#&Zj@W;OJ3`$sNTg$CehQ6xIc_l$b;!r6e&5B{iAO7!Ajy+7O&$|KNzC z7?PVv*G2XYkC4fj=p0#|kPkDgja=SrNwYD+7y@_dI(NE=A>LWcNXdA*M==~@z3XAP zbxqq;b-7}4G9$?g zy2jDD7EjIc_629Z{3XlV&**H6HjdlZ6_dGQy{Zs0^Iy6-!2cwbg z8r5yL+irQYh~AYlidqY~(b{Z`Han$sEz{HqZJf+9p+}RUkjmPNv(NB;Cxk5F&eDrm z3L)wcLQQ}8zg+squl$1l2&Uh^Py9|QfH$px5Q69(LP?}H^y)?;_5*(r`%RtflR?)u zv1>~5;(m2b$($s8Kss8{b%DAv zJbdq%5Ht2q4te!_!_WTw=RACGpR>=_WWx-vD~3tRy}dE_PX|1HyvOO$h{+(KECXqh zFc}ZY@*y@xOk$YN_es-)wv!Z-3Hj^*WeiodVRm##lo>D1Uy~($8-&#T4T$O~!y(|j zqqQB*Il8tc$qLHtn)ULQ?RG;H4(~io(;`d)fU_-9r$iy|zTVy@&Gpht2&J)IOV`wW zf{}_C0)xE?QW>hPrK;DxPTyj|`sRY=>O+dX69}I1AVV9$W~~`ck9hRKe+6>HX0@g+ z&$zvO!Sd=uR+k;26MX-@g5&*RF9*N$Ty8CGFzi2mzkgouhzNpI^s4rb;2kE?$bcmh zx@xUNW)9lj+O*e~pX z&|7&CMJ=RgV~h>GrT15zef}QP?;k7j`HzjSJpsIlM+Xn!uH^S^E2L6UNG*r~Aw&cj zg^&?JM2IK^BD75#Y}b-zry=&nBf&=%Tt`H<&e}vN-D+cu6j627S5oTeqxmcwAKp~X z>=PO@O}dd$oHQX>B%^&PTl;^nHdo(C@}e+Bf$c1V>HO{^)<~&H@|@N61-ECPF`UkN zV2bl3`G6$LdHC+Px%c)rkP@2pg27})Hr~Sr&=J}V?5-lKYse=9UO%ttI(Yo{?{R&- zCN=kY_|`w+^uc$ze09m+{P~|z3}z(hnE4}%Aj9Y(I-Rn-+_GGClx1YSZOQY3_dj^T z{U=W;h7+o)Aw-F5JMzH@Z6sA|IXal)^#og&6te>kj}NG;lBSKlI&KvGI_}L+9|<9V z5coT#93KPL+8!}kSCrc=-no8oDf;BGy1hl3gg`H`<$c6@Pt&&j;7@6M@Ca$}KH_X| z^BskxX=<9LWxaZh)CDmHmRHYdnk`MONRkX8I)>9z0*?Fd{!?bh-^W_T`udjJ%U>e& z4dwcbS09zsTgQXLltd|BTsE9vH#iSUYtmxCV19_sa=Z`t2ndC>mgv2gK6Jz&gc7E0 zcZCn$1R=EBZf=7MJE@Z(x^|(Zth7m5J((FHWi6C$k+MM(F!5E;N6>p|K*$iHzECgD`200D1O%o06+*KVz5@|qKJJ?5K#yb5hC=!k?3y~J#c7C9d)tNJobcAhNR&Q`P+N9JE2ERtM5%*7D4iC;3=b}Du9x|)arrz8 z(|I`iw)5V};^1~NIsS{)_U4<@{gb?|)<~tu#&ewQAbM=uB8Yg`aee;9uZLLP`@U;Y zinJJU{N#NOpS*|f8gx29B`JgH5w0`5^T%UO-<{&akkB0xb&HS#61|VeE?J-NxP84~ zwfKTy4Wrq(e^*a_$U9H}g!$-Qrh@~rbb^qEiL{K;lpRy78IcbQ_78@f98B<0Q15o+ zMM{zwB9UZpK$>R+VVNGySuI!GuKS67niqG#CxK9kNH3HaVk9`<3x)WOuI(tsQ;Ok~ zKyM+YP2RsJ(X;B(Vn~t=klN7H70b(Ww##eIKK}&oBPPv|O7jMgp|r*eiL(~lwybZ? ziD*=ku)MycZnjjrmh1CN%5q1HfvR3{{^Fd??HN)-xjAFA`!l9{Th_Zzx%#}Jtt^K{ z4#D!`rejw*lvXIM(Mihe^f8lzV*)+q-~0amN(fH4&IeZ~w%KZ#3}UF8wMg^SbzS2Y z%a>BBK-Vm#_Zux$t<%gA!d58N3S+D)hDPSa&|aSXDps3PBD?@kh+f3#iO~xweQ@^6 z{r;O<-+$?-eC>VzmqUE5fBSEUAAREw-~5{b5Xo#VqxTXa4N{~LSwI|x$eb7sLky=O zgwvR1@5aOXe-gY?-gbc)MTnq8Ge;Logl^L`HQv4sFsovSRhDN$YttopHj35N(Z1OZ z?@uA9tUC|lvg~RTr5fGdh(U}ATG0cByrbGI@J-Wi0ivhgl}z^bdGOBnnH`;yq&fLu zz#9l?@QxUIL6Pg1&uGdXQMiA9AC;v9=Q(-%kip;p*MMt0&90-|+~VAhY^=!goThPP zdBM%)$7J@9qx(;=yWaEQ>}UTE!d4vKe}I&l+Ig;5C1t(h-thsG%%T&4wSuCUFqq77 zQL?zXC8{yEmlt$hWH_F%fA2{@g_nvX&v5?DB?R7fgb2fH{V=Hz*Scb4sXMKLV! z?S^8w&+*&e#-te`Mr_wompfj4{tL9E_dOgQarEE;Nn!{Ag6K>C;5=2erm43*G`JXY zbNM;jb$h2WF)+D)LI?Z@}vElp|4G=j!E!e(nxx96> zT|_F`4--u?ma{@_QL!JM}3uwC~?qAmEY6*ei-jkq(Bc zkV!UFu`EpxGLcFfq*A2$h@>d!T!T(Bl02ttDsC>GVLQuce$cP?h(yt={wQtGY0m8E zl+oTH)BPioY=TJ>f`#pR$!>K+yMrW|;Dbd(&uH(EyqM6eBHQ&f#thjkmSp3M(fAaS zjBGTcsynWiJBE`f#dw003XLK$1JFYnrwA5Wuh_Yqi(AL*>x!$}nt)`rT#^~d?BJ*` z*M;bBW+CqmW$Xn!k#tRiNfa@3q-lyak`OGxSG468AA3h1p$#hONjPO$qD(?s%o**S zFq+I5P9_K?L5iM_;XOV$tZ!)B5~Rk5!20%z^Tg6{ z?Lc@DLnQV}UeWt_H{^b!>-U@3eSdH0_q#a!@Hj)Af!FHHpNOUd=Dep?f z{%moFs)`Xs*I1geipA(K`WRv)3aLWtRb{(o6ue7~aJg0rYkguHR$W~+D3f*7)*`i! z$@q4&>$Y{Zn-2y9ay4K&nu?+jq1?9Y%2pVY^|I1BL74=l72er9?*L7?S+lx+-ItS5 z_7G!4Us6X1G4{zID@cj~*1y!S73o~z4O1Z4>65T6WaR6!dhG8<9uwzO>oH9(~k>ZT*nnnXoLc|^p3pyPV6 zV=&O)4i#(gjeGB+nQWIgbt}8P*UJyFOs26ijWcjlV)^v#p?Ps*>H?b zGGd4bA@0^_@$RvEgI}ix9Nv% zS%D7@=OQ73xa+ywwxewutnKJpON>7_AINLl%dv%eC~ZiJwP z=tYPTvGJcI0leV~{5lB;f$v(BF+ylfw6;eH#mKJ# zLoxPfAwYQVWL<7TnvWwwMNf4$VQz%1>5p5Er(yVVUDaJF@9iLp@ zC5cF%2q6A?qTWMxA-LYgJR1)C6GBj01-PG(>3!2@MvyXmCy}ThA3~4*ZW_g2@kt9usA@uJnf{zjB0wI9yES+r$ zzL#A0&f{DlB5BHsYQ14|v%na`cs5};KO{t0-(GR?=|{9(%%y@T|zS#{3#2cmFAnM&ZD4hnW@)$yl zLaH}#BBW3uq!n@yt>27<870wo(qy@w?8RfD!YIV9NC(~2q*7F+a@I-_tPp9EGalvy zl)bG%3FIUfR85CT2be5J2#Hi0lVt2xw|)7Zr2UFZ%Kka&Z~hR&-2lAbvwPp)oYwZ1 z+umD3@V#YLUXY{(N*ip~p_7Ecct(hT(8LI{$r$ILsv52rTefw?MTPT`B2O9Q2{A-E z2kX@m=N(Pc3tuEfL6Q$qDg~iP(hNlJlq&>xI{N`BARi7HjK=-*MM2kfL?lTuKxuOV2^6I zV|{Z?UGC8Nh^8w0%1S5#0hy)h|g{o>~nkO;%7_9ZtwhpC4^v=1as-!j{RMjhY zeewUd_pLpSEXj2zA~UN<_G@}(?p>``me#TWTZRo7{^0))_zxJi0YQe1)oXX}b7uNM zvZ^v8jz2^eo6|dUcO?OPp9ye^WV1>JGq38r{zu zd_FeZ<6k}6>tDLQH3tB^FA@U63-~gV786=%tv3vSAiV2`19<}=gzL1w^IWzb!|1}| z7;XTd>WEino)i~%jojFb2c*MQ9%M@^jRK`gGFR2IAzblztnjwT<+h}ulxa5Qz9Rkn zc8kZug60U}Pc9$?7uUDAfBPkJ2LY`vrwH!!?g{{NhS(@dZMZqKWF*Z2rlkz&TVA<4 zN~hRJo??YtNMUujz|?@l;|xt(mYu2F&`fA_VeH1t6%>;(y-~E?6&`|po}A=6jV20 z*dmQP7!+x90VjMjj*qX$;cx!z7r*??i&vlgnSkTO@$=tyW1%S=*vHoA2*_?RRf}m2D?P_ti|d(mW?3QDV)by;+tE0Pqk>Try2=h|ecOCC!qyX{sl zt}dmN5x3U?$~_zoh}~TTb(!JKaR2pZ$Yn$sFCd~Y^8m*@jLNLRdKMD}O$R(hAwZv1 z0JPT7nt=dXt0zpfw$}edindIEWK?so?>MBnU@^y14NE23Qeo2pUw`=phU~a33O7gO zV?H+slylxQ6ipeF$i6j=!7-L5$WC|?R27BJNA|kA!o@{%-MHn+ zndYTYTbo+y*{C zKovmzTzaG=4fM{lACAc57Sr(vZ%cG4E(;_Nz2-JFH$Vi3kQ2W^K=Zo|HC^Rr#!TOw$Cnz&ujQSf(Q$-o62Hf#w0e9FSFTJnT`n z7jOW>_F z&5u>s7Ml(i);yJq+dHXvOl31jN(q3X427W9`*+9o@OGazyRjV)2W`vVzy9JM5_wlN z-KV4vN#r0RM+sT4#YUfK2;*N69e(Fh-wzu9xea_9>sxmK>-vyJ03_HdBSi&)a0GH1 znG}N=BO3t3*<^Zr%Q9XugA_Vj`UG#wti11mSx7$jhP~FcjPi)nJ3V# z1DnxoW9GqxNWxMVNJ=m#(lBBe252b|?R^5rJ<>QLl@acSvbzFAvDin6=H zFm7>s{p#Ir*!;TK{!!^!mDm2&-~Pvkef@I#`j^{0`mJs+m$n>^-fC(ye}yzIdE9wB z?k#P17n<@wGlOdOWkG#-cktumq~mtu$HPGm4{z{z_hr^}%&Lz`^)W#Y0FFW)pLG4Q zfIgF)@xJjtPyPRBI{!apePvViVfqKLi0qBH#umeGQL^O5w>xP25 zOrbc0AP-wmG+twYDXrigG#YX=71U+MxV!3jg(7WsKpr6_K+7_0kjD*D9^p<5y9)@2 z;o=7KJfk^Ik-gOxnN0Kw!Wd}2XgWE$WtuP@4j^JD-xUOPkKrpKvE`bzhIu-GRM`L`EW zuZ~xn+ZSy*4%2ctJhaLGFGRA4ZY1SPh>qe8w%Rn8V)Nwn{_Vl*Y?5-q;o(8{_itsI z?vm=9M2;$Y5a1xhLBIoJ0LTnrW|RVJZ~u9h-x~da5A^r{5JvzM!8}c<%Yva~EFEZU%})q@tA=JoR)sQllOBhRaU76`3zTt# zk~Nru%WIVFB{o+tP&OBkJfLiM!3wB`QZS`cY2p^n$-JS>3n;)cFKA{VCenOjJI2eK z(|JUevz9Z_2(--MB#!$7yw=#@b8cXtHQ?@WBJhole}3`y9`*4Z@-ofa%a^|%wwI8U zIq#=mak&2NREPhL@#X*CzJC3%d;RKG%Zs~dxj>uzjn($K04jO7)?xFy?jI`aY_`9f z%GHaVwMv@~Ms~h(fBy~-Z{J93hpaj&WKxl%LiR!)NbW)IK^!4Cl01MsA}0HMlkxx2 zrM|y!eWx72y1vN?0ElQ7ZEKMU5GD{45r>G0C{={wtYv!mt(W1tmEo0ma8p8eK$WDB zRJSUc6?Jj628eh7ah9=vXp$3I8{ush$*B-+aI4AP6{)Q1 zYKwj7@7{cY!^1r;Z(emiJkn!pHCTfV7&;=Vv#aaOJeqS_MBsUvPT(OSQCB;$)YkC^ zhgd~c^ypqNjs+{il>~^OS;&C%xPiOHcRGvM&WOgX8R`-a$my^a z*SKizfMk?0)*4!Ca1XX&nT`Q6PE#;tz3Z?|drWghZPqb_1?{*;JKkeDJYqjLZpPgq zgDz#Wq`Y}>_iDAypiG?Se@ZDG$}q_0)$M;FcE{Sbtsa&y+Tr1!?6`kdu3r2Sx1U~-OjZ~1R>h8wN5A{(D;yv16}lwRDTypGw;u-(dw_e8k02f)*pqw& zc|aKdngN^-_XM8R{ofYl_o4NjbO6Bn-GiqmMM7`uPAvo2`Bo8eiMC4$#8kvZgeu|! zuuO+H+LlAzT>LrK@x*3Lk{1z$nZj)<-`LbSl$nDnUTiuvIl^Ks#_Oo^=} ztM|n{FF>l4+?shpDk^0FXmAjhyDhS6^w%GcFpnV`!Ic{z8XS3@`sek&{MP!!B15eU ztX4D&gQ94R)2cDn8jv2LiDrg*sb~&VS1fal_p23f!%$)*me7%tb{rKwYKIdr4=FAY zbR|8qfWQDM=-xliqg?5RZtig7$?)9?s|~F#aSrA%#B0nP?f4MGI9yQ8VReE(zQe2o zDl*Hkn>J#>DToAYf z@Pd$+1YX6_Uy}Z!Y5rusxc(1yd-dl^Tg1IdlEHHx$TqZARdqnwq>hXy2oz@=%Ui|`Pa~Z0;%?_m$&>g81sK(@e{0(1ZhZ%Zdf6j2%J zX_t{c5;<{a0ys`Acuj*{amTLruACwTvU(wJj6c zyRYx<%@?0*ogb5EO{z7CRs|M?OaKl59zotA#(2L4@Rsgx5p(?>0j%l&dFub2qWeC! zzOxy?y4KF9Gj`zQv{);lN=fOYz7i2fjJ0!tSO|$aPfkaf4|iJUeH(Y5Qqw3N8x>HI zA)?}FA|_7CGCN3x8DTBKJ;BWYH>zfXxDr)U(L_>Jn3`D@k`R?%-wW9>NG(9w^6vh@ zT?Tpc**}P+95UbIL+8wM?^SmYJSpm)`U|{+hArMx@@cPw*ySdSUix@1>Cy=^1}K+9 z+8kYY{r62F1!&b!=Z3>^f>~%0T5Fh>1xszHW@t6a8F!pYn`Vw_u9&8Z$H&Lu`)iF1 z0TP)=YiPX`@H)Y!*yr%*ifBSKh__aRN1c~D&GR;u5fo2(7%LJ~GcT5<4ejyaD_@q0 zbvbDKpJ#pX`mb4rpWET#Gkd)MqP%$dS9y2!7c?Wu>{ca}aYt)lt!mSO%a_0T%)kEZ z*GfK?l%@dtbXLfskcq@W1dkx^LB4~?YW(j2+#@3YHyQtFeHV=%_#QZb-{~8OwTgxS z5229e4Dv)18l(?o0IbW0DSi5Aa;wAP;SE8lo6AoKK-yvuiliiNph#8K&>x8sgg}!r zl`N7|0+vK~ibA1DKvQE92$P$uwW@3;ZCS8X?_;nOXtP|7gQ#j9H#f(5KFU0|G+y7L z&Y_Z{8|r+3Xz6-^*w6$BYS3y=uO*#f2N9vIH7xUlD3Jgc!SZ<5^#!3T@Sdhm`#mIO zm=jh*mv2sJN}y!Kcr-m}7R+!0`f96abqVypRJsYMthsr<9~g+G6LLRZz_2c%g%j5aj8w_;Nh3e*M+2(=ZNfwdQfN zEkFOW|Aaie^yB_Zn~#s>U@xIqojujbwwQlqix^gb+(a zHWi_!k(yHMT`JjWje=uxQn#B8BoA;3Qr7;J6ap}*3XuS+hbuB*dJwOZEAk9B=9|}J z+TEy#$hxb5K+*qbMnE{E6~&aXl>SEjP&2TTCoF; z?$MbE2IL|s#h+LRw~hl?gTQ-c(q)?7ySG;dv+5kI!j=lB0dPZ$Yn}q2Q4g|h< z8j?sZ4l5N&vg&4TzKoQbh1%S*+hmWn-@j#@4hfjYtD7=>@~59jE}6&um%c3bdUN}i z5Gib}nYHB8(c1Jl!7RZXe*fl6JihxfiJFSGi1w%gX#ks$3xP=j+WTHZPWJh(uJ3#XaK1h$7K$Yx$<`=kfW#kv6$v>`RH3jxhD3j%h#S-4 z{!xzi$2MMEqihCPb69gi6q-Q?Bs#mmTItnjw2Z7u83`u0+RSW9HXo~1B9)?~LqIVH zTCK3=0%-zKKoeRsJ}XyEX>vafX;86No4G+)4(Z}DGpU+2rD}GeHgChSR0SR4yGO5hhdmYs#$Z* zqH_{i1Xxrs$r|~?`0o)l{~MBDBWnK@^gG~*?t9+dhxPtZ#}B#Q*@L zNEx!Gl-zxQVCF~= z9yXG4fv6&-L;#|y{d{|rM^6sG^-h3tokoO8hekSP&^syn6z1M!q9V{}C~LimrwTCBed%2O0ptTj?#`nAJ^CJgcpvS5uI~rVjQ{(vzIP5_U8^Jb zNJ$`2O{olt?gPOQkS%~q0yh9|0laqL6~NaUNmbr=C-qon^*qwPhSd>piN;_ zc~F6zQsl_NK?;)Ota;q1lRc2L&xJ6E2ujKE!ZI>xrbwbDrMC*DWnryp98$`=CQ>LW ziJ+=Pcc7|rHC_oY11d^H;w^|2TtglKxSOcxQ%`}n)k;AyCiSk;WOONVZ_2^bRHimrYOvCly}Fqo zwVd*fc@UvB(8@sA`73!D|;+;`{MBLqG75V=1?`OoY9-z)N2k3TtpKf*eb4g>E~h3icv z=Pa5s-A4c$02dHk6Y>JUEx}hLUpf4h(O#R`OLMz!UM~%DVY{0fTz_(nlvJ!aL?F2c zG>NPbyEFDSP)STVrJjA<`dX9jO3-ax!R5;%-q>bk$zG*aOOCF zW4^w<(P7*`lS939U+Gw}t_QKq*oxU}3Pch^ z&f6&nhzCz^k~E3*{SlH7P=`teJXffSYU;+XrxZWb!J!dgoLs!MC^R|?eKlQ3SUkfq zNLLzCWVLSXx?_-t)V~k2T;CKx^u`kcv`VC;0i8I!)!-=1VAdiZQkDLOgvT@lw#C?| z;QIuaLiiY|;LD7^Yr$Xlda0AE%fGdJG1a1Moboy(DzjFFZcfU3ImAPHI`^Hr^ z94|J!e09yTDN3u7imc!rNTGOgSVqhyiJe1p5tSVMeyTA>SX=~3QKhP!I`0IoB&ezs zO(L415#9wvyj~K!XJDWLPkjU`!jtwvKv{SC=i^M5hY@UOVst;0uDUoZ$pW z-a75t$V-wtQVN91&E?iEU*1wvh9;G@NGHWXkW6u1H-U;G712}_nq%^D*Cw2dR~4d6 z!A?L_gk+2xj||{+A_A|}vK0|fL?{rhSwb&MB=CVS{S`S3DFMOOjIBCyRsgaNR+aTt znqz-9YtB?k|Ls#L;iRD3wbP$|77-p06`nerRNNY2wbH$7lPqc`H%H1DG0f3YO3++X z%8;cDNks)9#2UO#!OYa!P?vqG)0;dU{wb69s<~?4R0i|}XUrQrp`2VI>f-?L5#%F) zhqI`^8vePe@42R*_=xd;9}Rz9KPU%qzCH>L#w|f4Y0d>ilm&t+o9;*Mwij#wAi>;)iBx8tb^dUUt{71qjKIs*-V^q^{qA5u$;VRZ-ODiZ1%slKP<4 zlMv}?EMxEka1VP9Lpxy-T@yfn8ufoOF>7mm(*G1fiJb`m1fWm`)l^cH5EO9(TP3U# ztrBi7^WiZaA3jgb?y~A3NjfG?O=3dS`du`(o`(T!bj|?w01utlf9N3JD&zf7y8ru7 ze&CGe`@R}K@B?)KzlR}M??*O}G(%DWFhVebynx^m#Fc|L0B#+;1^I%&6+|up*+6uQ z5FCw87Se~h*}bU4_J!rLrD{Qh_sB}+G}kneUJ8(sNGfp@4Shfd_(JT%{stXAKsPl3 z1R6l!(~WBxfTt7f&3OV)xKf7Qi@vZ= z{6z=?W==N~x`i%7ffOf5^7Oq#HKwZqfE#EAcq3bdxDoX@%e3F?^!P5hJ?2#AoXV6m zFDlXmVgWjIsbn~xMskMZ2;sgP{#x7LGdTXWiRaL7Ys5e2_Mi9he=m{#Z(2V{2LOCj zF95vn2)fJ-2_jij4$9EEf#3q*1x2nv-gNr#8X{MKTmo_dksX6s*b*F#KDztH$*~UO zP2FDIGNmDE|D+HVt_?bZr|_x@XcDFz=$@#oYQOFgR%{I#BYHYUCkSu=XRN?lD9DNS z>+i!8W3U$bzKR4$>2s{1DfNM>;VPW$(R(%io-jOp2>XY`&$;$8M^u(Lou}vz_Us~@ zusawAPq>k85Hm~5{!R~f_tKW85L4BfG|ySnqM8;JZOU$_Piaacy&_WheNXl85JtZO ze2;h%^PJcD&kg@QUjOX+`Q9P@-?Vtz^#)TciS3mYi@&Sy09`{dKou9Y&J+Gous=x%U+{(5Ors$V9FvX zNmx+W7>ED>pH__VUo!^;OTd1gB(xtzLpK0+c0hWLv8sAhpNyZYX$x?Q`XJY@8|@M3 z03+5~0(6%^ApxkI(gk*D5h+fKI>A~9m&~YM_qomwxz3NN%}2UXx`PoBPogHGOA>7& zQkCKW4P*mj5GR}y1P73N0QV5N2YJ_x|J?9b8SjkuIsNB~zV{~h{*VlRT|amSu&$2) zgwKyL91=((k|m{q>+Z?AS< zHX}qMGw7R%VAbRUf~4z6AVDr7sl@A4AUuyqCunem6S0;CJU_Xf5%j!(>vQbSooaYe z&NBS9YFVFHbBIVQR>mO49`iKiqt7^F^chG84+U<3o70`*?&@_K>hfl&^BZ-qC%<_5 z)X4268oEq@29YL26QGi42tI!W_=460?g>2LceVTH%{$K>gXK=aRXLkgoUpN^@_=e<$#6Y4D%n&V1r7)Ml z%DAyIZag*y=rhBCHPP-MVecVObk+eK8t4>WI0uNz`6+ew;G`8^t?@}w#Ip;&qXZ}6 zP&f84`!j#SnA*(f24Y7Dd2%h?@cXb3x{JEEWc8R^eJIxU+1o*hSwUxDqbD#SI7={$ zpAMpdY(N-3fCWM6tr36a^dEYCe=g!#BmQ$_|M@OG_|Wl(0Q7y+%HQ@E_!ifCL7yMj z=bs`%jC?k@Euv|L$PvU5;3EWE5L-ZY051Vt2;@?L3x`}L!6l&=0@+cx1+f7zGQh)7 zsnt2N&Iv$v)na+rwmfVsm4PW`KdFF1ZYZtS{+(D4z}#G*-^*_(LT2U z-s>7sNFVvWdJlx*1Fc8-DnJD>Bj~&dg5dKRhL14*)#%rV|J?AOi+Mg~_@}7f@BhoM zvhVo%QDy+=>sdCi-ami5W&@}403t<128fOT$Fr>9$r(guup=WY0I;JVUK5UBpvVZI z04RJqMoY7xIFL;wLsN0h*-|N9a`sd*q3iBrJ#b5R06pgS8k~CS{&{+N$MaA8;3>=J zlMsj}Y49nH@6Y#*7Q%+aXvy1Btj)=7Np4Fb4WK8UIx&f-Db6vk0qY1i#Prfg8@$tZ z2BHG2AeJzCP7s-Tv_AmYGsHZr!LRB6YV;pU_0J9egXung1X2Hd{g^X=)9T)SHTv_5 zLGX0Jbod_Dvw{WiNmK`jj6D*Kh)BN$umi9Y!3Bs5Kz2gz09;UH17ItGEsO#g7&K%d zFfgS7WFc8HQD`lpwFH?HjueU{7fr4yQIonR^^_7-Qz(P9D~wda#xzDH1NxjMME3xJ z=`p&22ngIm15AYcnXcxpG|TQy=}p}&tu$ZPATU5XB8;Ei_)l%wabuI!(0gZIH~KzD zp9{zZz=RmPePGPtUsL@U#@X-VchmgmBA(}lk7xIRGsExK#J})r`hKk+eFgx$ryBr% z_iyr?F<3EzbI>q9Wd$;vF$f!oY(VTFt5aC<1MDE!1dS+L03#wJ7y%3n&~WIF6M+GO z3?P%pc*;yd&4<(8$#N3w^sn1@`#qi430sIK*HR~pG-_{l_lm(cP;W)_pY!;?*YM8) zz8_1}KVLsNfRC(a+5zBq9RcvZ5w2xI?v9`U5+%ZF+~gKL`o~S;DBJ?rkh+Q2Vm*#{ zasmT@fs~9W7s43;2_dPAEz-jotk_?7R`0Qf=ih(z1-?>@XUFvwor;L_USU4%Xn;H! z)VaYKz%Y8oq#djVzozzB4W0``0?8LSf}Huj;U5^0e~s{KTkd&nzu)br;H#WTKwM24iJym=})UPc|EQ08GPShqAh5h@_#yi zJQ+MGd@?q{SxO%fz1R7^V5RHASVMRdhCegX{0T9JYeo#@IP%HhXFT!tYhC}Lt+&qU zywmsn!0|@|@ckj{rwrg5*0a(8_;7h}eq0^FS|+5kTNtWHk#GPj0C-CK&+czJuF%T} zY);RgUZVrRfoItOJ9p+yjKXVGF@BPu5`21%lgr4Woo=4iRf1>MHA+i9N z12;LP>$7yD?gKf_jeI`4o!9gq+Hdb1qxq=OulIk7`tPrw9KavAKGI(R{H_yN@1J7@ z=Ow~(hcF0GATpq*`Oj|M;Ma0OHXkSr3QmqdG6FNm&pz+DBY4hsoJaW6e0 zJ;ZzU{pr>KoNbN$6!`_x$8Ggm|96f1`SIM?&uP0q)adb{=YER#-@N`m?iEo*)F0t* P00000NkvXXu0mjfvKI*g literal 0 HcmV?d00001 diff --git a/code/ryzom/tools/client/ryzom_installer/src/archive.cpp b/code/ryzom/tools/client/ryzom_installer/src/archive.cpp new file mode 100644 index 000000000..23f61330c --- /dev/null +++ b/code/ryzom/tools/client/ryzom_installer/src/archive.cpp @@ -0,0 +1,785 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include "stdpch.h" +#include "archive.h" +#include "utils.h" + +#include "nel/misc/big_file.h" +#include "nel/misc/callback.h" +#include "nel/misc/file.h" +#include "nel/misc/path.h" + +#include "7z.h" +#include "7zAlloc.h" +#include "7zBuf.h" +#include "7zCrc.h" + +#include "qzipreader.h" + +#include + +#include + +#define FILE_ATTRIBUTE_READONLY 1 +#define FILE_ATTRIBUTE_HIDDEN 2 +#define FILE_ATTRIBUTE_SYSTEM 4 +#define FILE_ATTRIBUTE_DIRECTORY 16 +#define FILE_ATTRIBUTE_ARCHIVE 32 +#define FILE_ATTRIBUTE_DEVICE 64 +#define FILE_ATTRIBUTE_NORMAL 128 +#define FILE_ATTRIBUTE_TEMPORARY 256 +#define FILE_ATTRIBUTE_SPARSE_FILE 512 +#define FILE_ATTRIBUTE_REPARSE_POINT 1024 +#define FILE_ATTRIBUTE_COMPRESSED 2048 +#define FILE_ATTRIBUTE_OFFLINE 0x1000 +#define FILE_ATTRIBUTE_ENCRYPTED 0x4000 +#define FILE_ATTRIBUTE_UNIX_EXTENSION 0x8000 /* trick for Unix */ + +bool Set7zFileAttrib(const QString &filename, uint32 fileAttributes) +{ + bool attrReadOnly = (fileAttributes & FILE_ATTRIBUTE_READONLY != 0); + bool attrHidden = (fileAttributes & FILE_ATTRIBUTE_HIDDEN != 0); + bool attrSystem = (fileAttributes & FILE_ATTRIBUTE_SYSTEM != 0); + bool attrDir = (fileAttributes & FILE_ATTRIBUTE_DIRECTORY != 0); + bool attrArchive = (fileAttributes & FILE_ATTRIBUTE_ARCHIVE != 0); + bool attrDevice = (fileAttributes & FILE_ATTRIBUTE_DEVICE != 0); + bool attrNormal = (fileAttributes & FILE_ATTRIBUTE_NORMAL != 0); + bool attrTemp = (fileAttributes & FILE_ATTRIBUTE_TEMPORARY != 0); + bool attrSparceFile = (fileAttributes & FILE_ATTRIBUTE_SPARSE_FILE != 0); + bool attrReparsePoint = (fileAttributes & FILE_ATTRIBUTE_REPARSE_POINT != 0); + bool attrCompressed = (fileAttributes & FILE_ATTRIBUTE_COMPRESSED != 0); + bool attrOffline = (fileAttributes & FILE_ATTRIBUTE_OFFLINE != 0); + bool attrEncryoted = (fileAttributes & FILE_ATTRIBUTE_ENCRYPTED != 0); + bool attrUnix = (fileAttributes & FILE_ATTRIBUTE_UNIX_EXTENSION != 0); + + qDebug() << "attribs" << QByteArray::fromRawData((const char*)&fileAttributes, 4).toHex(); + +#ifdef Q_OS_WIN + SetFileAttributesW((wchar_t*)filename.utf16(), fileAttributes); + +// QFile::set + +// QFileDevice::Permissions:: + + // attribs "2080ed81" +// QFile::setPermissions(destPath, QFileDevice::Permissions); + +#else + const char * name = filename.toUtf8().constData(); + + struct stat stat_info; + if (lstat(name, &stat_info)!=0) + { + nlwarning("SetFileAttrib(%s,%d) : false-2-1", (const char *)name, fileAttributes); + return false; + } + + if (fileAttributes & FILE_ATTRIBUTE_UNIX_EXTENSION) + { + stat_info.st_mode = fileAttributes >> 16; + + if (S_ISLNK(stat_info.st_mode)) + { + if (convert_to_symlink(name) != 0) + { + nlwarning("SetFileAttrib(%s,%d) : false-3",(const char *)name,fileAttributes); + return false; + } + } + else if (S_ISREG(stat_info.st_mode)) + { + nlwarning("##DBG chmod-2(%s,%o)", (const char *)name, (unsigned)stat_info.st_mode & gbl_umask.mask); + chmod(name,stat_info.st_mode & gbl_umask.mask); + } + else if (S_ISDIR(stat_info.st_mode)) + { + // user/7za must be able to create files in this directory + stat_info.st_mode |= (S_IRUSR | S_IWUSR | S_IXUSR); + nlwarning("##DBG chmod-3(%s,%o)", (const char *)name, (unsigned)stat_info.st_mode & gbl_umask.mask); + chmod(name,stat_info.st_mode & gbl_umask.mask); + } + } + else if (!S_ISLNK(stat_info.st_mode)) + { + // do not use chmod on a link + + // Only Windows Attributes + if( S_ISDIR(stat_info.st_mode)) + { + // Remark : FILE_ATTRIBUTE_READONLY ignored for directory. + nlwarning("##DBG chmod-4(%s,%o)", (const char *)name, (unsigned)stat_info.st_mode & gbl_umask.mask); + chmod(name,stat_info.st_mode & gbl_umask.mask); + } + else + { + // octal!, clear write permission bits + if (fileAttributes & FILE_ATTRIBUTE_READONLY) stat_info.st_mode &= ~0222; + nlwarning("##DBG chmod-5(%s,%o)", (const char *)name, (unsigned)stat_info.st_mode & gbl_umask.mask); + chmod(name,stat_info.st_mode & gbl_umask.mask); + } + } +#endif + + return true; +} + +#ifdef DEBUG_NEW + #define new DEBUG_NEW +#endif + +#define SZ_ERROR_INTERRUPTED 18 + +class Q7zFile : public ISeekInStream +{ + QFile m_file; + +public: + Q7zFile(const QString &filename):m_file(filename) + { + Read = readFunc; + Seek = seekFunc; + } + + ~Q7zFile() + { + } + + bool open() + { + return m_file.open(QFile::ReadOnly); + } + + // the read function called by 7zip to read data + static SRes readFunc(void *object, void *buffer, size_t *size) + { + Q7zFile *me = (Q7zFile*)object; + qint64 len = *size; + len = me->m_file.read((char*)buffer, len); + + if (len == *size) + { + *size = len; + return SZ_OK; + } + else + { + return SZ_ERROR_READ; + } + } + + // the seek function called by seven zip to seek inside stream + static SRes seekFunc(void *object, Int64 *pos, ESzSeek origin) + { + Q7zFile *me = (Q7zFile*)object; + qint64 newPos; + + switch(origin) + { + case SZ_SEEK_SET: newPos = *pos; break; + case SZ_SEEK_CUR: newPos = me->m_file.pos() + *pos; break; + case SZ_SEEK_END: newPos = me->m_file.size() - *pos; break; + } + + if (me->m_file.seek(newPos)) + { + *pos = newPos; + return SZ_OK; + } + else + { + return SZ_ERROR_READ; + } + } +}; + +CArchive::CArchive(QObject *parent):QObject(parent), m_mustStop(false) +{ +} + +CArchive::~CArchive() +{ +} + +bool CArchive::extract(const QString &filename, const QString &dest) +{ + m_filename = filename; + m_dest = dest; + + QFile file(m_filename); + + // open archive file to check format + if (!file.open(QFile::ReadOnly)) return false; + + // read 2 first bytes + QByteArray header = file.read(2); + + // close file + file.close(); + + // create destination directory + QDir dir; + dir.mkpath(dest); + + QFuture future; + + // compare to supported formats and call the appropriate decompressor + if (header == "7z") + { + future = QtConcurrent::run(this, &CArchive::extract7z); + } + else if (header == "PK") + { + future = QtConcurrent::run(this, &CArchive::extractZip); + } + else if (QFileInfo(filename).suffix().toLower() == "bnp") + { + future = QtConcurrent::run(this, &CArchive::extractBnp); + } + else + { + qDebug() << "Unsupported format"; + return false; + } + + return true; +} + +void CArchive::getFilesList(const QString &srcDir, const QString &dstDir, const QStringList &filter, FilesToCopy &files) +{ + QDir dir(srcDir); + + QFileInfoList entries = dir.entryInfoList(filter); + + foreach(const QFileInfo &entry, entries) + { + QString fullPath = entry.absoluteFilePath(); + + QString dstPath = dstDir + "/" + dir.relativeFilePath(fullPath); + + if (entry.isDir()) + { + QDir().mkpath(dstPath); + + QDir subDir(fullPath); + + QDirIterator it(subDir, QDirIterator::Subdirectories); + + while (it.hasNext()) + { + fullPath = it.next(); + + if (it.fileName().startsWith('.')) continue; + + QFileInfo fileInfo = it.fileInfo(); + + dstPath = dstDir + "/" + dir.relativeFilePath(fullPath); + + if (fileInfo.isDir()) + { + QDir().mkpath(dstPath); + } + else + { + FileToCopy file; + file.filename = it.fileName(); + file.src = it.filePath(); + file.dst = dstPath; + file.size = it.fileInfo().size(); + file.date = it.fileInfo().lastModified(); + + files << file; + } + } + } + else + { + FileToCopy file; + file.filename = entry.fileName(); + file.src = entry.filePath(); + file.dst = dstPath; + file.size = entry.size(); + file.date = entry.lastModified(); + + files << file; + } + } +} + +bool CArchive::copyServerFiles() +{ + emit extractPrepare(); + + FilesToCopy files; + + QStringList serverFiles; + serverFiles << "cfg"; + serverFiles << "data"; + serverFiles << "examples"; + serverFiles << "patch"; + serverFiles << "unpack"; + serverFiles << "client_default.cfg"; + + CArchive::getFilesList(m_filename, m_dest, serverFiles, files); + + return copyFiles(files); +} + +bool CArchive::copyConfigurationFiles() +{ + emit extractPrepare(); + + FilesToCopy files; + + QStringList configFiles; + configFiles << "cache"; + configFiles << "save"; + configFiles << "user"; + configFiles << "screenshots"; + configFiles << "client.cfg"; + configFiles << "*.log"; + + CArchive::getFilesList(m_filename, m_dest, configFiles, files); + + return copyFiles(files); +} + +bool CArchive::copyServerFiles(const QString &src, const QString &dst) +{ + if (src.isEmpty() || dst.isEmpty()) return false; + + m_filename = src; + m_dest = dst; + + // create destination directory + QDir().mkpath(dst); + + QFuture future = QtConcurrent::run(this, &CArchive::copyServerFiles); + + return true; +} + +bool CArchive::copyConfigurationFiles(const QString &src, const QString &dst) +{ + if (src.isEmpty() || dst.isEmpty()) return false; + + m_filename = src; + m_dest = dst; + + // create destination directory + QDir().mkpath(dst); + + QFuture future = QtConcurrent::run(this, &CArchive::copyConfigurationFiles); + + return true; +} + +bool CArchive::copyFiles(const FilesToCopy &files) +{ + qint64 totalSize = 0; + + foreach(const FileToCopy &file, files) + { + totalSize += file.size; + + qDebug() << file.filename; + } + + emit extractInit(0, totalSize); + + emit extractStart(); + + qint64 processedSize = 0; + + foreach(const FileToCopy &file, files) + { + if (mustStop()) + { + emit extractStop(); + return true; + } + + emit extractProgress(processedSize, file.filename); + + QFileInfo dstFileInfo(file.dst); + + if (dstFileInfo.size() != file.size || dstFileInfo.lastModified() != file.date) + { + if (!QFile::copy(file.src, file.dst)) + { + emit extractFail(tr("Unable to copy file %1").arg(file.src)); + return false; + } + + if (!NLMISC::CFile::setFileModificationDate(qToUtf8(file.dst), file.date.toTime_t())) + { + qDebug() << "Unable to change date"; + } + } + + processedSize += file.size; + } + + emit extractSuccess(totalSize); + + return true; +} + +bool CArchive::extract7z() +{ + Q7zFile inFile(m_filename); + + if (!inFile.open()) + { + emit extractFail(tr("Unable to open %1").arg(m_filename)); + return false; + } + + emit extractPrepare(); + + UInt16 *temp = NULL; + size_t tempSize = 0; + + // register the files read handlers + CLookToRead lookStream; + lookStream.realStream = &inFile; + LookToRead_CreateVTable(&lookStream, False); + LookToRead_Init(&lookStream); + + // init CRC table + CrcGenerateTable(); + + // init 7z + CSzArEx db; + SzArEx_Init(&db); + + // register allocators + ISzAlloc allocImp; + allocImp.Alloc = SzAlloc; + allocImp.Free = SzFree; + + ISzAlloc allocTempImp; + allocTempImp.Alloc = SzAllocTemp; + allocTempImp.Free = SzFreeTemp; + + qint64 total = 0, totalUncompressed = 0; + QString error; + + // open 7z acrhive + SRes res = SzArEx_Open(&db, &lookStream.s, &allocImp, &allocTempImp); + + if (res == SZ_OK) + { + // process each file in archive + for (UInt32 i = 0; i < db.NumFiles; ++i) + { + bool isDir = SzArEx_IsDir(&db, i) != 0; + + if (!isDir) total += SzArEx_GetFileSize(&db, i); + } + + emit extractInit(0, total); + + emit extractStart(); + + // variables used for decompression + UInt32 blockIndex = 0xFFFFFFFF; + Byte *outBuffer = NULL; + size_t outBufferSize = 0; + + // process each file in archive + for (UInt32 i = 0; i < db.NumFiles; ++i) + { + if (mustStop()) + { + res = SZ_ERROR_INTERRUPTED; + break; + } + + size_t offset = 0; + size_t outSizeProcessed = 0; + + bool isDir = SzArEx_IsDir(&db, i) != 0; + + size_t len = SzArEx_GetFileNameUtf16(&db, i, NULL); + + if (len > tempSize) + { + SzFree(NULL, temp); + tempSize = len; + temp = (UInt16 *)SzAlloc(NULL, tempSize * sizeof(temp[0])); + if (!temp) + { + res = SZ_ERROR_MEM; + break; + } + } + + SzArEx_GetFileNameUtf16(&db, i, temp); + + QString path = QString::fromUtf16(temp); + QString filename = QFileInfo(path).fileName(); + + if (!isDir) + { + emit extractProgress(totalUncompressed, filename); + + res = SzArEx_Extract(&db, &lookStream.s, i, &blockIndex, &outBuffer, &outBufferSize, + &offset, &outSizeProcessed, &allocImp, &allocTempImp); + + if (res != SZ_OK) break; + } + + QString destPath = m_dest + '/' + path; + + QDir dir; + + if (isDir) + { + dir.mkpath(destPath); + continue; + } + + dir.mkpath(QFileInfo(destPath).absolutePath()); + + QFile outFile(destPath); + + if (!outFile.open(QFile::WriteOnly)) + { + error = tr("Unable to open output file"); + res = SZ_ERROR_FAIL; + break; + } + + size_t processedSize = outFile.write((const char*)(outBuffer + offset), outSizeProcessed); + + if (processedSize != outSizeProcessed) + { + error = tr("Unable to write output file"); + res = SZ_ERROR_FAIL; + break; + } + + outFile.close(); + + totalUncompressed += SzArEx_GetFileSize(&db, i); + + emit extractProgress(totalUncompressed, filename); + + if (SzBitWithVals_Check(&db.Attribs, i)) + Set7zFileAttrib(destPath, db.Attribs.Vals[i]); + } + + IAlloc_Free(&allocImp, outBuffer); + } + + SzArEx_Free(&db, &allocImp); + SzFree(NULL, temp); + + switch(res) + { + case SZ_OK: + emit extractSuccess(totalUncompressed); + return true; + + case SZ_ERROR_INTERRUPTED: + emit extractStop(); + return true; + + case SZ_ERROR_UNSUPPORTED: + error = tr("7zip decoder doesn't support this archive"); + break; + + case SZ_ERROR_MEM: + error = tr("Unable to allocate memory"); + break; + + case SZ_ERROR_CRC: + error = tr("7zip decoder doesn't support this archive"); + break; + + case SZ_ERROR_FAIL: + // error already defined + break; + + default: + error = tr("Error %1").arg(res); + } + + emit extractFail(error); + + return false; +} + +bool CArchive::extractZip() +{ + emit extractPrepare(); + + QZipReader reader(m_filename); + + QDir baseDir(m_dest); + + // create directories first + QList allFiles = reader.fileInfoList(); + + qint64 totalSize = 0, currentSize = 0; + + foreach (const QZipReader::FileInfo &fi, allFiles) + { + if (fi.isDir) + { + const QString absPath = m_dest + QDir::separator() + fi.filePath; + + if (!baseDir.mkpath(fi.filePath)) + { + emit extractFail(tr("Unable to create directory %1").arg(absPath)); + return false; + } + + if (!QFile::setPermissions(absPath, fi.permissions)) + { + emit extractFail(tr("Unable to set permissions of %1").arg(absPath)); + return false; + } + } + + totalSize += fi.size; + } + + emit extractInit(0, totalSize); + emit extractStart(); + + // client won't use symbolic links so don't process them + + foreach (const QZipReader::FileInfo &fi, allFiles) + { + const QString absPath = m_dest + QDir::separator() + fi.filePath; + + if (fi.isFile) + { + if (mustStop()) + { + emit extractStop(); + return true; + } + + QFile f(absPath); + + if (!f.open(QIODevice::WriteOnly)) + { + emit extractFail(tr("Unable to open %1").arg(absPath)); + return false; + } + + currentSize += f.write(reader.fileData(fi.filePath)); + + f.setPermissions(fi.permissions); + f.close(); + + emit extractProgress(currentSize, QFileInfo(absPath).fileName()); + } + } + + emit extractSuccess(totalSize); + + return true; +} + +bool CArchive::progress(const std::string &filename, uint32 currentSize, uint32 totalSize) +{ + if (mustStop()) + { + emit extractStop(); + + return false; + } + + if (currentSize == 0) + { + emit extractInit(0, (qint64)totalSize); + emit extractStart(); + } + + emit extractProgress((qint64)currentSize, qFromUtf8(filename)); + + if (currentSize == totalSize) + { + emit extractSuccess((qint64)totalSize); + } + + return true; +} + +bool CArchive::extractBnp() +{ + QString error; + + emit extractPrepare(); + + NLMISC::CBigFile::TUnpackProgressCallback cbMethod = NLMISC::CBigFile::TUnpackProgressCallback(this, &CArchive::progress); + + try + { + if (NLMISC::CBigFile::unpack(qToUtf8(m_filename), qToUtf8(m_dest), &cbMethod)) + { + return true; + } + + if (mustStop()) + { + // stopped + + return true; + } + + error.clear(); + } + catch(const NLMISC::EDiskFullError &e) + { + error = tr("disk full"); + } + catch(const NLMISC::EWriteError &e) + { + error = tr("unable to write %1").arg(qFromUtf8(e.Filename)); + } + catch(const NLMISC::EReadError &e) + { + error = tr("unable to read %1").arg(qFromUtf8(e.Filename)); + } + catch(const std::exception &e) + { + error = tr("failed (%1)").arg(qFromUtf8(e.what())); + } + + emit extractFail(tr("Unable to unpack %1 to %2: %3").arg(m_filename).arg(m_dest).arg(error)); + + return false; +} + +void CArchive::stop() +{ + QMutexLocker locker(&m_mutex); + + m_mustStop = true; +} + +bool CArchive::mustStop() +{ + QMutexLocker locker(&m_mutex); + + return m_mustStop; +} + diff --git a/code/ryzom/tools/client/ryzom_installer/src/archive.h b/code/ryzom/tools/client/ryzom_installer/src/archive.h new file mode 100644 index 000000000..ac9c0c036 --- /dev/null +++ b/code/ryzom/tools/client/ryzom_installer/src/archive.h @@ -0,0 +1,96 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#ifndef ARCHIVE_H +#define ARCHIVE_H + +/** + * Files copy, decompression, extraction + * + * \author Cedric 'Kervala' OCHS + * \date 2016 + */ +class CArchive : public QObject +{ + Q_OBJECT + +public: + CArchive(QObject *parent = NULL); + virtual ~CArchive(); + + bool extract(const QString &filename, const QString &dest); + bool copyServerFiles(const QString &src, const QString &dst); + bool copyConfigurationFiles(const QString &src, const QString &dst); + + void stop(); + bool mustStop(); + +signals: + // emitted when requesting real URL + void extractPrepare(); + + // emitted when we got the initial (local) and total (remote) size of file + void extractInit(qint64 current, qint64 total); + + // emitted when we begin to download + void extractStart(); + + // emitted when the download stopped + void extractStop(); + + // emitted when extracting + void extractProgress(qint64 current, const QString &filename); + + // emitted when the whole file is downloaded + void extractSuccess(qint64 total); + + // emitted when an error occurs + void extractFail(const QString &error); + +protected: + + struct FileToCopy + { + QString filename; + QString src; + QString dst; + qint64 size; + QDateTime date; + }; + + typedef QList FilesToCopy; + + bool extract7z(); + bool extractZip(); + bool extractBnp(); + + bool progress(const std::string &filename, uint32 currentFile, uint32 totalFiles); + + bool copyServerFiles(); + bool copyConfigurationFiles(); + bool copyFiles(const FilesToCopy &files); + + static void getFilesList(const QString &srcDir, const QString &dstDir, const QStringList &filter, FilesToCopy &files); + + QString m_filename; + QString m_dest; + + QMutex m_mutex; + + bool m_mustStop; +}; + +#endif diff --git a/code/ryzom/tools/client/ryzom_installer/src/configfile.cpp b/code/ryzom/tools/client/ryzom_installer/src/configfile.cpp new file mode 100644 index 000000000..f0b8c7577 --- /dev/null +++ b/code/ryzom/tools/client/ryzom_installer/src/configfile.cpp @@ -0,0 +1,564 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include "stdpch.h" +#include "configfile.h" +#include "utils.h" + +#include "nel/misc/path.h" + +#ifdef DEBUG_NEW + #define new DEBUG_NEW +#endif + +const CServer NoServer; +const CConfiguration NoConfiguration; + +CConfigFile *CConfigFile::s_instance = NULL; + +CConfigFile::CConfigFile(QObject *parent):QObject(parent), m_defaultServer(0), m_defaultConfiguration(0), m_use64BitsClient(false) +{ + s_instance = this; + + m_defaultConfigPath = QApplication::applicationDirPath() + "/installer.ini"; + m_configPath = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation) + "/installer.ini"; +} + +CConfigFile::~CConfigFile() +{ + s_instance = NULL; +} + +bool CConfigFile::load() +{ + return load(m_configPath) || load(m_defaultConfigPath); +} + +bool CConfigFile::load(const QString &filename) +{ + QSettings settings(filename, QSettings::IniFormat); + + settings.beginGroup("common"); + m_language = settings.value("language").toString(); + m_srcDirectory = settings.value("source_directory").toString(); + m_installationDirectory = settings.value("installation_directory").toString(); + m_use64BitsClient = settings.value("use_64bits_client").toBool(); + settings.endGroup(); + + settings.beginGroup("servers"); + int serversCount = settings.value("size").toInt(); + m_defaultServer = settings.value("default").toInt(); + settings.endGroup(); + + m_servers.resize(serversCount); + + for(int i = 0; i < serversCount; ++i) + { + CServer &server = m_servers[i]; + + settings.beginGroup(QString("server_%1").arg(i)); + + server.id = settings.value("id").toString(); + server.name = settings.value("name").toString(); + server.displayUrl = settings.value("display_url").toString(); + server.dataDownloadUrl = settings.value("data_download_url").toString(); + server.dataDownloadFilename = settings.value("data_download_filename").toString(); + server.dataCompressedSize = settings.value("data_compressed_size").toULongLong(); + server.dataUncompressedSize = settings.value("data_uncompressed_size").toULongLong(); + server.clientDownloadUrl = settings.value("client_download_url").toString(); + server.clientDownloadFilename = settings.value("client_download_filename").toString(); +#if defined(Q_OS_WIN) + server.clientFilename = settings.value("client_filename_windows").toString(); +#elif defined(Q_OS_MAC) + server.clientFilename = settings.value("client_filename_osx").toString(); +#else + server.clientFilename = settings.value("client_filename_linux").toString(); +#endif + server.comments = settings.value("comments").toString(); + + settings.endGroup(); + } + + settings.beginGroup("configurations"); + int configurationsCounts = settings.value("size").toInt(); + m_defaultConfiguration = settings.value("default").toInt(); + settings.endGroup(); + + m_configurations.resize(configurationsCounts); + + for(int i = 0; i < configurationsCounts; ++i) + { + CConfiguration &configuration = m_configurations[i]; + + settings.beginGroup(QString("configuration_%1").arg(i)); + + configuration.id = settings.value("id").toInt(); + configuration.name = settings.value("name").toString(); + configuration.account = settings.value("account").toString(); + configuration.server = settings.value("server").toString(); + configuration.executable = settings.value("executable").toString(); + configuration.parameters = settings.value("parameters").toString(); + configuration.comments = settings.value("comments").toString(); + + settings.endGroup(); + } + + return !m_servers.isEmpty(); +} + +bool CConfigFile::save() const +{ + QSettings settings(m_configPath, QSettings::IniFormat); + + settings.beginGroup("common"); + settings.setValue("language", m_language); + settings.setValue("source_directory", m_srcDirectory); + settings.setValue("installation_directory", m_installationDirectory); + settings.setValue("use_64bits_client", m_use64BitsClient); + settings.endGroup(); + + settings.beginGroup("servers"); + settings.setValue("size", m_servers.size()); + settings.setValue("default", m_defaultServer); + settings.endGroup(); + + for(int i = 0; i < m_servers.size(); ++i) + { + const CServer &server = m_servers[i]; + + settings.beginGroup(QString("server_%1").arg(i)); + + settings.setValue("id", server.id); + settings.setValue("name", server.name); + settings.setValue("display_url", server.displayUrl); + settings.setValue("data_download_url", server.dataDownloadUrl); + settings.setValue("data_download_filename", server.dataDownloadFilename); + settings.setValue("data_compressed_size", server.dataCompressedSize); + settings.setValue("data_uncompressed_size", server.dataUncompressedSize); + settings.setValue("client_download_url", server.clientDownloadUrl); + settings.setValue("client_download_filename", server.clientDownloadFilename); +#if defined(Q_OS_WIN) + settings.setValue("client_filename_windows", server.clientFilename); +#elif defined(Q_OS_MAC) + settings.setValue("client_filename_osx", server.clientFilename); +#else + settings.setValue("client_filename_linux", server.clientFilename); +#endif + settings.setValue("comments", server.comments); + + settings.endGroup(); + } + + settings.beginGroup("configurations"); + settings.setValue("size", m_configurations.size()); + settings.setValue("default", m_defaultConfiguration); + settings.endGroup(); + + for(int i = 0; i < m_configurations.size(); ++i) + { + const CConfiguration &configuration = m_configurations[i]; + + settings.beginGroup(QString("configuration_%1").arg(i)); + + settings.setValue("id", configuration.id); + settings.setValue("name", configuration.name); + settings.setValue("account", configuration.account); + settings.setValue("server", configuration.server); + settings.setValue("executable", configuration.executable); + settings.setValue("parameters", configuration.parameters); + settings.setValue("comments", configuration.comments); + + settings.endGroup(); + } + + return true; +} + +CConfigFile* CConfigFile::getInstance() +{ + return s_instance; +} + +int CConfigFile::getServersCount() const +{ + return m_servers.size(); +} + +const CServer& CConfigFile::getServer(int i) const +{ + if (i < 0) i = m_defaultServer; + + if (i >= m_servers.size()) return NoServer; + + return m_servers.at(i); +} + +const CServer& CConfigFile::getServer(const QString &id) const +{ + for(int i = 0; i < m_servers.size(); ++i) + { + if (m_servers[i].id == id) return m_servers[i]; + } + + // default server + return getServer(); +} + +int CConfigFile::getConfigurationsCount() const +{ + return m_configurations.size(); +} + +CConfiguration CConfigFile::getConfiguration(int i) const +{ + if (i < 0) i = m_defaultConfiguration; + + if (i >= m_configurations.size()) return NoConfiguration; + + return m_configurations.at(i); +} + +void CConfigFile::setConfiguration(int i, const CConfiguration &configuration) +{ + m_configurations[i] = configuration; +} + +int CConfigFile::addConfiguration(const CConfiguration &configuration) +{ + m_configurations.append(configuration); + + return m_configurations.size()-1; +} + +void CConfigFile::removeConfiguration(int i) +{ + m_configurations.removeAt(i); + + // TODO: decalle all configurations and move files +} + +bool CConfigFile::has64bitsOS() +{ + return QSysInfo::currentCpuArchitecture() == "x86_64"; +} + +int CConfigFile::getDefaultConfiguration() const +{ + return m_defaultConfiguration; +} + +int CConfigFile::getDefaultServer() const +{ + return m_defaultServer; +} + +bool CConfigFile::isRyzomInstallerConfigured() const +{ + return m_configurations.size() > 0; +} + +QString CConfigFile::getInstallationDirectory() const +{ + return m_installationDirectory; +} + +void CConfigFile::setInstallationDirectory(const QString &directory) +{ + m_installationDirectory = directory; +} + +QString CConfigFile::getSrcServerDirectory() const +{ + return m_srcDirectory; +} + +void CConfigFile::setSrcServerDirectory(const QString &directory) +{ + m_srcDirectory = directory; +} + +QString CConfigFile::getConfigurationDirectory() const +{ + return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); +} + +QString CConfigFile::getSrcConfigurationDirectory() const +{ + if (QFile::exists(getSrcServerDirectory() + "/client.cfg")) return getSrcServerDirectory(); + + return qFromUtf8(NLMISC::CPath::getApplicationDirectory("Ryzom")); +} + +bool CConfigFile::use64BitsClient() const +{ + return m_use64BitsClient; +} + +void CConfigFile::setUse64BitsClient(bool on) +{ + m_use64BitsClient = on; +} + +QString CConfigFile::expandVariables(const QString &str) +{ + QString res = str; + + res.replace("$TIMESTAMP", QString::number(QDateTime::currentDateTime().toTime_t())); + res.replace("$LANG", m_language); + res.replace("$ARCH", getClientArch()); + + return res; +} + +QString CConfigFile::getClientArch() const +{ +#if defined(Q_OS_WIN) + return QString("win%1").arg(m_use64BitsClient ? 64:32); +#elif defined(Q_OS_MAC) + // only 64 bits clients under OS X, becure there not any 32 bits OS X version anymore + return "osx"; +#else + return QString("linux%1").arg(m_use64BitsClient ? 64:32); +#endif +} + +QString CConfigFile::getCurrentDirectory() +{ + return QDir::current().absolutePath(); +} + +QString CConfigFile::getParentDirectory() +{ + QDir current = QDir::current(); + current.cdUp(); + return current.absolutePath(); +} + +QString CConfigFile::getApplicationDirectory() +{ + return QApplication::applicationDirPath(); +} + +QString CConfigFile::getOldInstallationDirectory() +{ + // HKEY_CURRENT_USER/SOFTWARE/Nevrax/RyzomInstall/InstallId=1917716796 (string) +#if defined(Q_OS_WIN) + // NSIS previous official installer +#ifdef Q_OS_WIN64 + // use WOW6432Node in 64 bits (64 bits OS and 64 bits Installer) because Ryzom old installer was in 32 bits + QSettings settings("HKEY_LOCAL_MACHINE\\Software\\WOW6432Node\\Nevrax\\Ryzom", QSettings::NativeFormat); +#else + QSettings settings("HKEY_LOCAL_MACHINE\\Software\\Nevrax\\Ryzom", QSettings::NativeFormat); +#endif + + if (settings.contains("Ryzom Install Path")) + { + return QDir::fromNativeSeparators(settings.value("Ryzom Install Path").toString()); + } + + // check default directory if registry key not found + return CConfigFile::has64bitsOS() ? "C:/Program Files (x86)/Ryzom":"C:/Program Files/Ryzom"; +#elif defined(Q_OS_MAC) + return "/Applications/Ryzom.app"; +#else + return QStandardPaths::writableLocation(QStandardPaths::HomeLocation) + "/.ryzom"; +#endif +} + +QString CConfigFile::getNewInstallationDirectory() +{ + return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation); +} + +bool CConfigFile::isRyzomInstalledIn(const QString &directory) const +{ + // check client and data + return isRyzomClientInstalledIn(directory) && areRyzomDataInstalledIn(directory); +} + +bool CConfigFile::areRyzomDataInstalledIn(const QString &directory) const +{ + QDir dir(directory); + + // directory doesn't exist + if (!dir.exists()) return false; + + if (!dir.cd("data") && dir.exists()) return false; + + // at least 200 BNP in data directory + if (dir.entryList(QStringList() << "*.bnp", QDir::Files).size() < 200) return false; + + // TODO: more checks + + return true; +} + +bool CConfigFile::isRyzomClientInstalledIn(const QString &directory) const +{ + QDir dir(directory); + + // directory doesn't exist + if (!dir.exists()) return false; + + // client_default.cfg doesn't exist + if (!dir.exists("client_default.cfg")) return false; + + if (!dir.exists(getServer().clientFilename)) return false; + + // TODO: more checks + + return true; +} + +QString CConfigFile::getClientFullPath() const +{ + QString path = getConfiguration().executable; + + if (!path.isEmpty()) return path; + + return getInstallationDirectory() + "/" + getServer().id + "/" + getServer().clientFilename; +} + +QString CConfigFile::getSrcServerClientBNPFullPath() const +{ + return QString("%1/unpack/exedll_%2.bnp").arg(getSrcServerDirectory()).arg(getClientArch()); +} + +CConfigFile::InstallationStep CConfigFile::getNextStep() const +{ + // get last used configuration + const CConfiguration &configuration = getConfiguration(); + + // get server used by it or default server + CServer server = getServer(configuration.server); + + // no or wrong configuration + if (server.id.isEmpty()) + { + // get last used server + server = getServer(); + } + + // no or wrong server + if (server.id.isEmpty()) + { + // get first server + server = getServer(0); + } + + // no server defined, shouldn't happen + if (server.id.isEmpty()) + { + return DisplayNoServerError; + } + + // only show wizard if installation directory undefined + if (getInstallationDirectory().isEmpty()) + { + return ShowWizard; + } + + QString serverDirectory = getInstallationDirectory() + "/" + server.id; + + if (getSrcServerDirectory().isEmpty()) + { + // user decided to download files + + // downloaded files are kept in server directory + QString dataFile = getInstallationDirectory() + "/" + server.dataDownloadFilename; + QString clientFile = getInstallationDirectory() + "/" + server.clientDownloadFilename; + + // data are not copied + if (!areRyzomDataInstalledIn(serverDirectory)) + { + // when file is not finished, it has .part extension + if (!QFile::exists(dataFile)) + { + return DownloadData; + } + + return ExtractDownloadedData; + } + + if (!isRyzomClientInstalledIn(serverDirectory)) + { + // when file is not finished, it has .part extension + if (!QFile::exists(clientFile)) + { + return DownloadClient; + } + + return ExtractDownloadedClient; + } + } + else + { + // user decided to copy files + + // selected directory contains Ryzom files (shouldn't fail) + if (!areRyzomDataInstalledIn(getSrcServerDirectory())) + { + return ShowWizard; + } + + // data are not copied + if (!areRyzomDataInstalledIn(serverDirectory)) + { + return CopyServerFiles; + } + + // client is not extracted from BNP + if (!isRyzomClientInstalledIn(serverDirectory)) + { + if (QFile::exists(getSrcServerClientBNPFullPath())) + { + return ExtractBnpClient; + } + else + { + QString clientFile = getInstallationDirectory() + "/" + server.clientDownloadFilename; + + // when file is not finished, it has .part extension + if (!QFile::exists(clientFile)) + { + return DownloadClient; + } + + return ExtractDownloadedClient; + } + } + } + + // no default configuration + if (configuration.id < 0) + { + return CreateConfiguration; + } + + // migration configuration + if (!getSrcServerDirectory().isEmpty() && QFile::exists(getSrcConfigurationDirectory() + "/client.cfg") && !QFile::exists(QString("%1/%2/client.cfg").arg(getConfigurationDirectory()).arg(configuration.name))) + { + return CopyConfigurationFiles; + } + + if (!QFile::exists(QStandardPaths::writableLocation(QStandardPaths::DesktopLocation) + "/Ryzom.lnk")) + { + // TODO: check they point to getClientFullPath() + return CreateShortcuts; + } + + return Done; +} diff --git a/code/ryzom/tools/client/ryzom_installer/src/configfile.h b/code/ryzom/tools/client/ryzom_installer/src/configfile.h new file mode 100644 index 000000000..1b5e0a111 --- /dev/null +++ b/code/ryzom/tools/client/ryzom_installer/src/configfile.h @@ -0,0 +1,166 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#ifndef CONFIGFILE_H +#define CONFIGFILE_H + +struct CServer +{ + CServer() + { + dataCompressedSize = 0; + dataUncompressedSize = 0; + } + + QString id; + QString name; + QString displayUrl; + QString dataDownloadUrl; + QString dataDownloadFilename; + qint64 dataCompressedSize; + qint64 dataUncompressedSize; + QString clientDownloadUrl; + QString clientDownloadFilename; + QString clientFilename; + QString comments; +}; + +extern const CServer NoServer; + +struct CConfiguration +{ + CConfiguration() + { + id = -1; + } + + int id; + QString account; + QString name; + QString server; + QString executable; + QString parameters; + QString comments; +}; + +extern const CConfiguration NoConfiguration; + +/** + * Config file management and other stuff related to location of files/directories. + * + * \author Cedric 'Kervala' OCHS + * \date 2016 + */ +class CConfigFile : public QObject +{ + Q_OBJECT + +public: + enum InstallationStep + { + DisplayNoServerError, + ShowWizard, + DownloadData, + ExtractDownloadedData, + DownloadClient, + ExtractDownloadedClient, + CopyServerFiles, + CopyConfigurationFiles, + ExtractBnpClient, + CreateConfiguration, + CreateShortcuts, + Done + }; + + CConfigFile(QObject *parent = NULL); + virtual ~CConfigFile(); + + bool load(); + bool load(const QString &filename); + bool save() const; + + static CConfigFile* getInstance(); + + int getServersCount() const; + const CServer& getServer(int i = -1) const; + const CServer& getServer(const QString &id) const; + + int getConfigurationsCount() const; + CConfiguration getConfiguration(int i = -1) const; + void setConfiguration(int i, const CConfiguration &configuration); + int addConfiguration(const CConfiguration &configuration); + void removeConfiguration(int i); + + int getDefaultServer() const; + int getDefaultConfiguration() const; + + bool isRyzomInstallerConfigured() const; + + QString getInstallationDirectory() const; + void setInstallationDirectory(const QString &directory); + + QString getSrcServerDirectory() const; + void setSrcServerDirectory(const QString &directory); + + QString getConfigurationDirectory() const; + QString getSrcConfigurationDirectory() const; + + static bool has64bitsOS(); + + // default directories + static QString getCurrentDirectory(); + static QString getParentDirectory(); + static QString getApplicationDirectory(); + static QString getOldInstallationDirectory(); + static QString getNewInstallationDirectory(); + + bool isRyzomInstalledIn(const QString &directory) const; + bool areRyzomDataInstalledIn(const QString &directory) const; + bool isRyzomClientInstalledIn(const QString &directory) const; + + // installation choices + bool use64BitsClient() const; + void setUse64BitsClient(bool on); + + QString expandVariables(const QString &str); + + QString getClientArch() const; + + QString getClientFullPath() const; + + QString getSrcServerClientBNPFullPath() const; + + InstallationStep getNextStep() const; + +private: + int m_defaultServer; + int m_defaultConfiguration; + + QVector m_servers; + QVector m_configurations; + + QString m_installationDirectory; + QString m_srcDirectory; + bool m_use64BitsClient; + QString m_language; + + QString m_defaultConfigPath; + QString m_configPath; + + static CConfigFile *s_instance; +}; + +#endif diff --git a/code/ryzom/tools/client/ryzom_installer/src/configurationsdialog.cpp b/code/ryzom/tools/client/ryzom_installer/src/configurationsdialog.cpp new file mode 100644 index 000000000..ec7fe079f --- /dev/null +++ b/code/ryzom/tools/client/ryzom_installer/src/configurationsdialog.cpp @@ -0,0 +1,38 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include "stdpch.h" +#include "configurationsdialog.h" + +#ifdef DEBUG_NEW + #define new DEBUG_NEW +#endif + +CConfigurationsDialog::CConfigurationsDialog():QDialog() +{ + setupUi(this); +} + +CConfigurationsDialog::~CConfigurationsDialog() +{ +} + +void CConfigurationsDialog::accept() +{ + // TODO: add save code + + QDialog::accept(); +} diff --git a/code/ryzom/tools/client/ryzom_installer/src/configurationsdialog.h b/code/ryzom/tools/client/ryzom_installer/src/configurationsdialog.h new file mode 100644 index 000000000..c236b62c7 --- /dev/null +++ b/code/ryzom/tools/client/ryzom_installer/src/configurationsdialog.h @@ -0,0 +1,34 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#ifndef CONFIGURATIONSDIALOG_H +#define CONFIGURATIONSDIALOG_H + +#include "ui_configurations.h" + +class CConfigurationsDialog : public QDialog, public Ui::ConfigurationsDialog +{ + Q_OBJECT + +public: + CConfigurationsDialog(); + virtual ~CConfigurationsDialog(); + +private slots: + void accept(); +}; + +#endif diff --git a/code/ryzom/tools/client/ryzom_installer/src/downloader.cpp b/code/ryzom/tools/client/ryzom_installer/src/downloader.cpp new file mode 100644 index 000000000..f129416ad --- /dev/null +++ b/code/ryzom/tools/client/ryzom_installer/src/downloader.cpp @@ -0,0 +1,394 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include "stdpch.h" +#include "downloader.h" + +#include "nel/misc/system_info.h" +#include "nel/misc/path.h" + +#ifdef DEBUG_NEW + #define new DEBUG_NEW +#endif + +CDownloader::CDownloader(QObject *parent):QObject(parent), m_manager(NULL), m_reply(NULL), m_timer(NULL), + m_offset(0), m_size(0), m_supportsAcceptRanges(false), m_supportsContentRange(false), + m_downloadAfterHead(false), m_aborted(false), m_file(NULL) +{ + m_manager = new QNetworkAccessManager(this); + m_timer = new QTimer(this); + + connect(m_timer, SIGNAL(timeout()), this, SLOT(onTimeout())); +} + +CDownloader::~CDownloader() +{ + stopTimer(); + closeFile(); +} + +bool CDownloader::getHtmlPageContent(const QString &url) +{ + if (url.isEmpty()) return false; + + QNetworkRequest request(url); + request.setHeader(QNetworkRequest::UserAgentHeader, "Ryzom Installer/1.0"); + + QNetworkReply *reply = m_manager->get(request); + + connect(reply, SIGNAL(finished()), SLOT(onHtmlPageFinished())); + + return true; +} + +bool CDownloader::prepareFile(const QString &url, const QString &fullPath) +{ + if (url.isEmpty()) return false; + + m_downloadAfterHead = false; + + emit downloadPrepare(); + + m_fullPath = fullPath; + m_url = url; + + getFileHead(); + + return true; +} + +bool CDownloader::getFile() +{ + if (m_fullPath.isEmpty() || m_url.isEmpty()) + { + qDebug() << "You forget to call prepareFile before"; + + return false; + } + + m_downloadAfterHead = true; + + getFileHead(); + + return true; +} + +bool CDownloader::stop() +{ + if (!m_reply) return false; + + m_reply->abort(); + + return true; +} + +void CDownloader::startTimer() +{ + stopTimer(); + + m_timer->setInterval(5000); + m_timer->setSingleShot(true); + m_timer->start(); +} + +void CDownloader::stopTimer() +{ + if (m_timer->isActive()) m_timer->stop(); +} + +bool CDownloader::openFile() +{ + closeFile(); + + m_file = new QFile(m_fullPath); + + if (m_file->open(QFile::Append)) return true; + + closeFile(); + + return false; +} + +void CDownloader::closeFile() +{ + if (m_file) + { + m_file->close(); + + delete m_file; + m_file = NULL; + } +} + +void CDownloader::getFileHead() +{ + if (m_supportsAcceptRanges) + { + QFileInfo fileInfo(m_fullPath); + + if (fileInfo.exists()) + { + m_offset = fileInfo.size(); + } + else + { + m_offset = 0; + } + + // continue if offset less than size + if (m_offset >= m_size) + { + if (checkDownloadedFile()) + { + // file is already downloaded + emit downloadSuccess(m_size); + } + else + { + // or has wrong size + emit downloadFail(tr("File (%1B) is larger than expected (%2B)").arg(m_offset).arg(m_size)); + } + + return; + } + } + + QNetworkRequest request(m_url); + request.setHeader(QNetworkRequest::UserAgentHeader, "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:24.0) Gecko/20100101 Firefox/24.0"); + + if (m_supportsAcceptRanges) + { + request.setRawHeader("Range", QString("bytes=%1-").arg(m_offset).toLatin1()); + } + + m_reply = m_manager->head(request); + + connect(m_reply, SIGNAL(finished()), SLOT(onHeadFinished())); + connect(m_reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(onError(QNetworkReply::NetworkError))); + + startTimer(); +} + +void CDownloader::downloadFile() +{ + qint64 freeSpace = NLMISC::CSystemInfo::availableHDSpace(m_fullPath.toUtf8().constData()); + + if (freeSpace < m_size - m_offset) + { + // we have not enough free disk space to continue download + emit downloadFail(tr("You only have %1 bytes left on device, but %2 bytes are required.").arg(freeSpace).arg(m_size - m_offset)); + return; + } + + if (!openFile()) + { + emit downloadFail(tr("Unable to write file")); + return; + } + + QNetworkRequest request(m_url); + request.setHeader(QNetworkRequest::UserAgentHeader, "Opera/9.80 (Windows NT 6.2; Win64; x64) Presto/2.12.388 Version/12.17"); + + if (supportsResume()) + { + request.setRawHeader("Range", QString("bytes=%1-%2").arg(m_offset).arg(m_size-1).toLatin1()); + } + + m_reply = m_manager->get(request); + + connect(m_reply, SIGNAL(finished()), SLOT(onDownloadFinished())); + connect(m_reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(onError(QNetworkReply::NetworkError))); + connect(m_reply, SIGNAL(downloadProgress(qint64, qint64)), SLOT(onDownloadProgress(qint64, qint64))); + connect(m_reply, SIGNAL(readyRead()), SLOT(onDownloadRead())); + + emit downloadStart(); + + startTimer(); +} + +bool CDownloader::checkDownloadedFile() +{ + QFileInfo file(m_fullPath); + + return file.size() == m_size && file.lastModified().toUTC() == m_lastModified; +} + +void CDownloader::onTimeout() +{ + qDebug() << "Timeout"; + + emit downloadFail(tr("Timeout")); +} + +void CDownloader::onHtmlPageFinished() +{ + QNetworkReply *reply = qobject_cast(sender()); + + int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + QString html = QString::fromUtf8(reply->readAll()); + + reply->deleteLater(); + + emit htmlPageContent(html); +} + +void CDownloader::onHeadFinished() +{ + stopTimer(); + + int status = m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + + QString redirection = m_reply->header(QNetworkRequest::LocationHeader).toString(); + + m_size = m_reply->header(QNetworkRequest::ContentLengthHeader).toInt(); + m_lastModified = m_reply->header(QNetworkRequest::LastModifiedHeader).toDateTime().toUTC(); + + QString acceptRanges = QString::fromLatin1(m_reply->rawHeader("Accept-Ranges")); + QString contentRange = QString::fromLatin1(m_reply->rawHeader("Content-Range")); + + m_reply->deleteLater(); + m_reply = NULL; + + // redirection + if (status == 302) + { + if (redirection.isEmpty()) + { + emit downloadFail(tr("Redirection URL is not defined")); + return; + } + + // redirection on another server, recheck resume + m_supportsAcceptRanges = false; + m_supportsContentRange = false; + + m_referer = m_url; + + // update real URL + m_url = redirection; + + getFileHead(); + + return; + } + + // we requested without range + else if (status == 200) + { + // update size + emit downloadInit(0, m_size); + + if (!m_supportsAcceptRanges && acceptRanges == "bytes") + { + // server supports resume, part 1 + m_supportsAcceptRanges = true; + + // request range + getFileHead(); + return; + } + + // server doesn't support resume or + // we requested range, but server always returns 200 + // download from the beginning + } + + // we requested with a range + else if (status == 206) + { + // server supports resume + QRegExp regexp("^bytes ([0-9]+)-([0-9]+)/([0-9]+)$"); + + if (m_supportsAcceptRanges && regexp.exactMatch(contentRange)) + { + m_supportsContentRange = true; + m_offset = regexp.cap(1).toLongLong(); + + // when resuming, Content-Length is the size of missing parts to download + m_size = regexp.cap(3).toLongLong(); + + // update offset and size + emit downloadInit(m_offset, m_size); + } + else + { + qDebug() << "Unable to parse"; + } + } + + // other status + else + { + emit downloadFail(tr("Wrong status code: %1").arg(status)); + return; + } + + if (m_downloadAfterHead) + { + if (checkDownloadedFile()) + { + qDebug() << "same date and size"; + } + else + { + downloadFile(); + } + } +} + +void CDownloader::onDownloadFinished() +{ + m_reply->deleteLater(); + m_reply = NULL; + + closeFile(); + + if (m_aborted) + { + m_aborted = false; + emit downloadStop(); + } + else + { + bool ok = NLMISC::CFile::setFileModificationDate(m_fullPath.toUtf8().constData(), m_lastModified.toTime_t()); + + emit downloadSuccess(m_size); + } +} + +void CDownloader::onError(QNetworkReply::NetworkError error) +{ + if (error == QNetworkReply::OperationCanceledError) + { + m_aborted = true; + } + else + { + emit downloadFail(tr("Network error: %1").arg(error)); + } +} + +void CDownloader::onDownloadProgress(qint64 current, qint64 total) +{ + stopTimer(); + + emit downloadProgress(m_offset + current); +} + +void CDownloader::onDownloadRead() +{ + if (m_file) m_file->write(m_reply->readAll()); +} diff --git a/code/ryzom/tools/client/ryzom_installer/src/downloader.h b/code/ryzom/tools/client/ryzom_installer/src/downloader.h new file mode 100644 index 000000000..7d9090700 --- /dev/null +++ b/code/ryzom/tools/client/ryzom_installer/src/downloader.h @@ -0,0 +1,110 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#ifndef DOWNLOADER_H +#define DOWNLOADER_H + +/** + * Files downloader, please note that only one file can be downloaded at once. + * + * \author Cedric 'Kervala' OCHS + * \date 2016 + */ +class CDownloader : public QObject +{ + Q_OBJECT + +public: + CDownloader(QObject *parent); + virtual ~CDownloader(); + + bool getHtmlPageContent(const QString &url); + + bool prepareFile(const QString &url, const QString &fullPath); + bool getFile(); + bool stop(); + + bool supportsResume() const { return m_supportsAcceptRanges && m_supportsContentRange; } + + bool isDownloading() const { return m_file != NULL; } + +signals: + // emitted when requesting real URL + void downloadPrepare(); + + // emitted when we got the initial (local) and total (remote) size of file + void downloadInit(qint64 current, qint64 total); + + // emitted when we begin to download + void downloadStart(); + + // emitted when the download stopped + void downloadStop(); + + // emittd when downloading + void downloadProgress(qint64 current); + + // emitted when the whole file is downloaded + void downloadSuccess(qint64 total); + + // emitted when an error occurs + void downloadFail(const QString &error); + + void htmlPageContent(const QString &html); + +private slots: + void onTimeout(); + void onHtmlPageFinished(); + void onHeadFinished(); + void onDownloadFinished(); + void onError(QNetworkReply::NetworkError error); + void onDownloadProgress(qint64 current, qint64 total); + void onDownloadRead(); + +protected: + void startTimer(); + void stopTimer(); + + bool openFile(); + void closeFile(); + + void getFileHead(); + void downloadFile(); + + bool checkDownloadedFile(); + + QNetworkAccessManager *m_manager; + QNetworkReply *m_reply; + QTimer *m_timer; + + QString m_url; + QString m_referer; + QString m_fullPath; + + qint64 m_offset; + qint64 m_size; + QDateTime m_lastModified; + + bool m_supportsAcceptRanges; + bool m_supportsContentRange; + + bool m_downloadAfterHead; + bool m_aborted; + + QFile *m_file; +}; + +#endif diff --git a/code/ryzom/tools/client/ryzom_installer/src/main.cpp b/code/ryzom/tools/client/ryzom_installer/src/main.cpp new file mode 100644 index 000000000..c78021ae5 --- /dev/null +++ b/code/ryzom/tools/client/ryzom_installer/src/main.cpp @@ -0,0 +1,108 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include "stdpch.h" +#include "mainwindow.h" +#include "configfile.h" +#include "wizarddialog.h" + +#include "nel/misc/path.h" +#include "nel/misc/ucstring.h" + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef QT_STATICPLUGIN + +#include + +#if defined(Q_OS_WIN32) + Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin) +#elif defined(Q_OS_MAC) + Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin) +#elif defined(Q_OS_UNIX) + Q_IMPORT_PLUGIN(QXcbIntegrationPlugin) + Q_IMPORT_PLUGIN(QXcbGlxIntegrationPlugin) +#endif + + Q_IMPORT_PLUGIN(QICOPlugin) + +#endif + +#ifdef DEBUG_NEW + #define new DEBUG_NEW +#endif + +int main(int argc, char *argv[]) +{ +#if defined(_MSC_VER) && defined(_DEBUG) +// _CrtSetDbgFlag (_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); +#endif + + NLMISC::CApplicationContext appContext; + + QApplication app(argc, argv); + + QApplication::setApplicationName("Ryzom"); + QApplication::setApplicationVersion(RYZOM_VERSION); + QApplication::setWindowIcon(QIcon(":/icons/ryzom.ico")); + + QLocale locale = QLocale::system(); + + // load application translations + QTranslator localTranslator; + if (localTranslator.load(locale, "ryzom_installer", "_", "translations")) + { + QApplication::installTranslator(&localTranslator); + } + + // load Qt default translations + QTranslator qtTranslator; + if (qtTranslator.load(locale, "qt", "_", "translations")) + { + QApplication::installTranslator(&qtTranslator); + } + + // instanciate ConfigFile + CConfigFile config; + CConfigFile::InstallationStep step = config.load() ? config.getNextStep():CConfigFile::DisplayNoServerError; + + if (step == CConfigFile::DisplayNoServerError) + { + // display error + return 1; + } + + bool displayMainWindow = true; + + if (step == CConfigFile::ShowWizard) + { + CWizardDialog dialog; + + if (!dialog.exec()) displayMainWindow = false; + } + + if (displayMainWindow) + { + CMainWindow mainWindow; + mainWindow.show(); + + return QApplication::exec(); + } + + return 0; +} diff --git a/code/ryzom/tools/client/ryzom_installer/src/mainwindow.cpp b/code/ryzom/tools/client/ryzom_installer/src/mainwindow.cpp new file mode 100644 index 000000000..81f2d925c --- /dev/null +++ b/code/ryzom/tools/client/ryzom_installer/src/mainwindow.cpp @@ -0,0 +1,389 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include "stdpch.h" +#include "mainwindow.h" +#include "downloader.h" +#include "archive.h" +#include "wizarddialog.h" +#include "configfile.h" +#include "config.h" + +#include "seven_zip.h" + +#if defined(Q_OS_WIN32) && defined(QT_WINEXTRAS_LIB) +#include +#include +#endif + +#ifdef DEBUG_NEW + #define new DEBUG_NEW +#endif + +CMainWindow::CMainWindow():QMainWindow(), m_archive(NULL), m_statusLabel(NULL) +{ + setupUi(this); + +#if defined(Q_OS_WIN32) && defined(QT_WINEXTRAS_LIB) + m_button = new QWinTaskbarButton(this); +#endif + + connect(resumeButton, SIGNAL(clicked()), SLOT(onResumeClicked())); + connect(stopButton, SIGNAL(clicked()), SLOT(onStopClicked())); + + // downloader + m_downloader = new CDownloader(this); + + connect(m_downloader, SIGNAL(downloadPrepare()), SLOT(onDownloadPrepare())); + connect(m_downloader, SIGNAL(downloadInit(qint64, qint64)), SLOT(onDownloadInit(qint64, qint64))); + connect(m_downloader, SIGNAL(downloadStart()), SLOT(onDownloadStart())); + connect(m_downloader, SIGNAL(downloadStop()), SLOT(onDownloadStop())); + connect(m_downloader, SIGNAL(downloadProgress(qint64)), SLOT(onDownloadProgress(qint64))); + connect(m_downloader, SIGNAL(downloadSuccess(qint64)), SLOT(onDownloadSuccess(qint64))); + connect(m_downloader, SIGNAL(downloadFail(QString)), SLOT(onDownloadFail(QString))); + connect(m_downloader, SIGNAL(htmlPageContent(QString)), SLOT(onHtmlPageContent(QString))); + + // archive + m_archive = new CArchive(this); + + connect(m_archive, SIGNAL(extractPrepare()), SLOT(onExtractPrepare())); + connect(m_archive, SIGNAL(extractInit(qint64, qint64)), SLOT(onExtractInit(qint64, qint64))); + connect(m_archive, SIGNAL(extractStart()), SLOT(onExtractStart())); + connect(m_archive, SIGNAL(extractStop()), SLOT(onExtractStop())); + connect(m_archive, SIGNAL(extractProgress(qint64, QString)), SLOT(onExtractProgress(qint64, QString))); + connect(m_archive, SIGNAL(extractSuccess(qint64)), SLOT(onExtractSuccess(qint64))); + connect(m_archive, SIGNAL(extractFail(QString)), SLOT(onExtractFail(QString))); + + connect(actionAboutQt, SIGNAL(triggered()), SLOT(onAboutQt())); + connect(actionAbout, SIGNAL(triggered()), SLOT(onAbout())); + + m_statusLabel = new QLabel(); + + statusBar()->addWidget(m_statusLabel); + +// setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); + +// processNextStep(); + + m_archive->extract("d:/Ryzom/bonjourx.7z", ""); +} + +CMainWindow::~CMainWindow() +{ +} + +void CMainWindow::processNextStep() +{ + CConfigFile *config = CConfigFile::getInstance(); + + // default server + const CServer &server = config->getServer(); + + // default configuration + const CConfiguration &configuration = config->getConfiguration(); + + switch(CConfigFile::getInstance()->getNextStep()) + { + case CConfigFile::DisplayNoServerError: + break; + + case CConfigFile::ShowWizard: + break; + + case CConfigFile::DownloadData: + displayProgressBar(); + m_downloader->prepareFile(config->expandVariables(server.dataDownloadUrl), config->getInstallationDirectory() + "/" + config->expandVariables(server.dataDownloadFilename) + ".part"); + break; + + case CConfigFile::ExtractDownloadedData: + displayProgressBar(); + break; + + case CConfigFile::DownloadClient: + displayProgressBar(); + m_downloader->prepareFile(config->expandVariables(server.clientDownloadUrl), config->getInstallationDirectory() + "/" + config->expandVariables(server.clientDownloadFilename) + ".part"); + break; + + case CConfigFile::ExtractDownloadedClient: + displayProgressBar(); + break; + + case CConfigFile::CopyServerFiles: + displayProgressBar(); + m_archive->copyServerFiles(config->getSrcServerDirectory(), config->getInstallationDirectory() + "/" + server.id); + break; + + case CConfigFile::CopyConfigurationFiles: + displayProgressBar(); + m_archive->copyConfigurationFiles(config->getSrcConfigurationDirectory(), config->getConfigurationDirectory() + "/0"); + break; + + case CConfigFile::ExtractBnpClient: + displayProgressBar(); + m_archive->extract(config->getSrcServerClientBNPFullPath(), config->getInstallationDirectory() + "/" + server.id); + break; + + case CConfigFile::CreateConfiguration: + displayProgressBar(); + break; + + case CConfigFile::CreateShortcuts: + displayProgressBar(); + break; + + default: + // cases already managed in main.cpp + displayConfigurationsChoices(); + break; + } + + m_downloader->getHtmlPageContent(config->expandVariables(server.displayUrl)); +} + +void CMainWindow::displayProgressBar() +{ + downloadFrame->setVisible(true); + configurationFrame->setVisible(false); + + resumeButton->setVisible(true); + stopButton->setVisible(false); +} + +void CMainWindow::displayConfigurationsChoices() +{ + downloadFrame->setVisible(false); + configurationFrame->setVisible(true); +} + +void CMainWindow::showEvent(QShowEvent *e) +{ +#if defined(Q_OS_WIN32) && defined(QT_WINEXTRAS_LIB) + m_button->setWindow(windowHandle()); +#endif + + e->accept(); +} + +void CMainWindow::closeEvent(QCloseEvent *e) +{ + hide(); + + e->accept(); +} + +void CMainWindow::onResumeClicked() +{ + m_downloader->getFile(); +} + +void CMainWindow::onStopClicked() +{ + if (m_downloader->isDownloading()) + { + if (!m_downloader->supportsResume()) + { + QMessageBox::StandardButton res = QMessageBox::question(this, tr("Confirmation"), tr("Warning, this server doesn't support resume! If you stop download now, you won't be able to resume it later.\nAre you sure to abort download?")); + + if (res != QMessageBox::Yes) return; + } + + m_downloader->stop(); + } + else + { + m_archive->stop(); + } +} + +void CMainWindow::onAbout() +{ + QString br("
"); + + QMessageBox::about(this, + tr("About %1").arg("Ryzom Installer"), + QString("Ryzom Installer") + QApplication::applicationVersion() + br + + tr("Program to install, download and manage Ryzom configurations.") + + br+br+ + tr("Author: %1").arg("Cedric 'Kervala' OCHS") + br + + tr("Copyright: %1").arg(COPYRIGHT) + br + + tr("Support: %1").arg("
Ryzom Core on Bitbucket")); +} + +void CMainWindow::onAboutQt() +{ + QMessageBox::aboutQt(this); +} + +void CMainWindow::onDownloadPrepare() +{ + progressBar->setFormat(tr("%p% (%v/%m KiB)")); + + progressBar->setMinimum(0); + progressBar->setMaximum(0); + progressBar->setValue(0); + + resumeButton->setVisible(false); + stopButton->setVisible(false); +} + +void CMainWindow::onDownloadInit(qint64 current, qint64 total) +{ + resumeButton->setVisible(true); + stopButton->setVisible(false); + + progressBar->setMinimum(0); + progressBar->setMaximum(total / 1024); + progressBar->setValue(current / 1024); + +#if defined(Q_OS_WIN32) && defined(QT_WINEXTRAS_LIB) + m_button->progress()->setMinimum(0); + m_button->progress()->setMaximum(total / 1024); + m_button->progress()->setValue(current / 1024); +#endif +} + +void CMainWindow::onDownloadStart() +{ + resumeButton->setVisible(false); + stopButton->setVisible(true); + +#if defined(Q_OS_WIN32) && defined(QT_WINEXTRAS_LIB) + m_button->progress()->show(); +#endif +} + +void CMainWindow::onDownloadStop() +{ + resumeButton->setVisible(true); + stopButton->setVisible(false); + +#if defined(Q_OS_WIN32) && defined(QT_WINEXTRAS_LIB) + m_button->progress()->hide(); +#endif +} + +void CMainWindow::onDownloadProgress(qint64 current) +{ + progressBar->setValue(current / 1024); + +#if defined(Q_OS_WIN32) && defined(QT_WINEXTRAS_LIB) + m_button->progress()->setValue(current / 1024); +#endif +} + +void CMainWindow::onDownloadSuccess(qint64 total) +{ + progressBar->setValue(total / 1024); + +#if defined(Q_OS_WIN32) && defined(QT_WINEXTRAS_LIB) + m_button->progress()->hide(); +#endif + + resumeButton->setVisible(false); + stopButton->setVisible(false); +} + +void CMainWindow::onDownloadFail(const QString &error) +{ + resumeButton->setVisible(true); + stopButton->setVisible(false); +} + +void CMainWindow::onHtmlPageContent(const QString &html) +{ + htmlTextEdit->setHtml(html); +} + +void CMainWindow::onExtractPrepare() +{ + progressBar->setFormat("%p%"); + + progressBar->setMinimum(0); + progressBar->setMaximum(0); + progressBar->setValue(0); + + resumeButton->setVisible(false); + stopButton->setVisible(false); +} + +void CMainWindow::onExtractInit(qint64 current, qint64 total) +{ + resumeButton->setVisible(true); + stopButton->setVisible(false); + + progressBar->setMinimum(0); + progressBar->setMaximum(total / 1024); + progressBar->setValue(current / 1024); + +#if defined(Q_OS_WIN32) && defined(QT_WINEXTRAS_LIB) + m_button->progress()->setMinimum(0); + m_button->progress()->setMaximum(total / 1024); + m_button->progress()->setValue(current / 1024); +#endif +} + +void CMainWindow::onExtractStart() +{ + resumeButton->setVisible(false); + stopButton->setVisible(true); + +#if defined(Q_OS_WIN32) && defined(QT_WINEXTRAS_LIB) + m_button->progress()->show(); +#endif +} + +void CMainWindow::onExtractStop() +{ + resumeButton->setVisible(true); + stopButton->setVisible(false); + +#if defined(Q_OS_WIN32) && defined(QT_WINEXTRAS_LIB) + m_button->progress()->hide(); +#endif +} + +void CMainWindow::onExtractProgress(qint64 current, const QString &filename) +{ + m_statusLabel->setText(tr("Extracting %1...").arg(filename)); + + progressBar->setValue(current / 1024); + +#if defined(Q_OS_WIN32) && defined(QT_WINEXTRAS_LIB) + m_button->progress()->setValue(current / 1024); +#endif +} + +void CMainWindow::onExtractSuccess(qint64 total) +{ + m_statusLabel->setText(tr("Extraction done")); + + progressBar->setValue(total / 1024); + +#if defined(Q_OS_WIN32) && defined(QT_WINEXTRAS_LIB) + m_button->progress()->hide(); +#endif + + resumeButton->setVisible(false); + stopButton->setVisible(false); + + processNextStep(); +} + +void CMainWindow::onExtractFail(const QString &error) +{ + resumeButton->setVisible(true); + stopButton->setVisible(false); +} diff --git a/code/ryzom/tools/client/ryzom_installer/src/mainwindow.h b/code/ryzom/tools/client/ryzom_installer/src/mainwindow.h new file mode 100644 index 000000000..bf14b8b4d --- /dev/null +++ b/code/ryzom/tools/client/ryzom_installer/src/mainwindow.h @@ -0,0 +1,81 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include "ui_mainwindow.h" + +class QWinTaskbarButton; +class CDownloader; +class CArchive; + +/** + * Main window + * + * \author Cedric 'Kervala' OCHS + * \date 2016 + */ +class CMainWindow : public QMainWindow, public Ui::MainWindow +{ + Q_OBJECT + +public: + CMainWindow(); + virtual ~CMainWindow(); + +public slots: + void onResumeClicked(); + void onStopClicked(); + + void onAbout(); + void onAboutQt(); + + void onDownloadPrepare(); + void onDownloadInit(qint64 current, qint64 total); + void onDownloadStart(); + void onDownloadStop(); + void onDownloadProgress(qint64 current); + void onDownloadSuccess(qint64 total); + void onDownloadFail(const QString &error); + + void onHtmlPageContent(const QString &html); + + void onExtractPrepare(); + void onExtractInit(qint64 current, qint64 total); + void onExtractStart(); + void onExtractStop(); + void onExtractProgress(qint64 current, const QString &filename); + void onExtractSuccess(qint64 total); + void onExtractFail(const QString &error); + +protected: + void showEvent(QShowEvent *e); + void closeEvent(QCloseEvent *e); + + void processNextStep(); + + void displayProgressBar(); + void displayConfigurationsChoices(); + + QWinTaskbarButton *m_button; + CDownloader *m_downloader; + CArchive *m_archive; + + QLabel *m_statusLabel; +}; + +#endif diff --git a/code/ryzom/tools/client/ryzom_installer/src/qzip.cpp b/code/ryzom/tools/client/ryzom_installer/src/qzip.cpp new file mode 100644 index 000000000..7372a0e82 --- /dev/null +++ b/code/ryzom/tools/client/ryzom_installer/src/qzip.cpp @@ -0,0 +1,1260 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "stdpch.h" + +#include "qzipreader.h" +#include "qzipwriter.h" + +#include + +#if defined(Q_OS_WIN) +# undef S_IFREG +# define S_IFREG 0100000 +# ifndef S_IFDIR +# define S_IFDIR 0040000 +# endif +# ifndef S_ISDIR +# define S_ISDIR(x) ((x) & S_IFDIR) > 0 +# endif +# ifndef S_ISREG +# define S_ISREG(x) ((x) & 0170000) == S_IFREG +# endif +# define S_IFLNK 020000 +# define S_ISLNK(x) ((x) & S_IFLNK) > 0 +# ifndef S_IRUSR +# define S_IRUSR 0400 +# endif +# ifndef S_IWUSR +# define S_IWUSR 0200 +# endif +# ifndef S_IXUSR +# define S_IXUSR 0100 +# endif +# define S_IRGRP 0040 +# define S_IWGRP 0020 +# define S_IXGRP 0010 +# define S_IROTH 0004 +# define S_IWOTH 0002 +# define S_IXOTH 0001 +#else +# include +#endif + +#if 0 +#define ZDEBUG qDebug +#else +#define ZDEBUG if (0) qDebug +#endif + +#ifdef DEBUG_NEW + #define new DEBUG_NEW +#endif + +QT_BEGIN_NAMESPACE + +static inline uint readUInt(const uchar *data) +{ + return (data[0]) + (data[1]<<8) + (data[2]<<16) + (data[3]<<24); +} + +static inline ushort readUShort(const uchar *data) +{ + return (data[0]) + (data[1]<<8); +} + +static inline void writeUInt(uchar *data, uint i) +{ + data[0] = i & 0xff; + data[1] = (i>>8) & 0xff; + data[2] = (i>>16) & 0xff; + data[3] = (i>>24) & 0xff; +} + +static inline void writeUShort(uchar *data, ushort i) +{ + data[0] = i & 0xff; + data[1] = (i>>8) & 0xff; +} + +static inline void copyUInt(uchar *dest, const uchar *src) +{ + dest[0] = src[0]; + dest[1] = src[1]; + dest[2] = src[2]; + dest[3] = src[3]; +} + +static inline void copyUShort(uchar *dest, const uchar *src) +{ + dest[0] = src[0]; + dest[1] = src[1]; +} + +static void writeMSDosDate(uchar *dest, const QDateTime& dt) +{ + if (dt.isValid()) { + quint16 time = + (dt.time().hour() << 11) // 5 bit hour + | (dt.time().minute() << 5) // 6 bit minute + | (dt.time().second() >> 1); // 5 bit double seconds + + dest[0] = time & 0xff; + dest[1] = time >> 8; + + quint16 date = + ((dt.date().year() - 1980) << 9) // 7 bit year 1980-based + | (dt.date().month() << 5) // 4 bit month + | (dt.date().day()); // 5 bit day + + dest[2] = char(date); + dest[3] = char(date >> 8); + } else { + dest[0] = 0; + dest[1] = 0; + dest[2] = 0; + dest[3] = 0; + } +} + +static quint32 permissionsToMode(QFile::Permissions perms) +{ + quint32 mode = 0; + if (perms & QFile::ReadOwner) + mode |= S_IRUSR; + if (perms & QFile::WriteOwner) + mode |= S_IWUSR; + if (perms & QFile::ExeOwner) + mode |= S_IXUSR; + if (perms & QFile::ReadUser) + mode |= S_IRUSR; + if (perms & QFile::WriteUser) + mode |= S_IWUSR; + if (perms & QFile::ExeUser) + mode |= S_IXUSR; + if (perms & QFile::ReadGroup) + mode |= S_IRGRP; + if (perms & QFile::WriteGroup) + mode |= S_IWGRP; + if (perms & QFile::ExeGroup) + mode |= S_IXGRP; + if (perms & QFile::ReadOther) + mode |= S_IROTH; + if (perms & QFile::WriteOther) + mode |= S_IWOTH; + if (perms & QFile::ExeOther) + mode |= S_IXOTH; + return mode; +} + +static int inflate(Bytef *dest, ulong *destLen, const Bytef *source, ulong sourceLen) +{ + z_stream stream; + int err; + + stream.next_in = (Bytef*)source; + stream.avail_in = (uInt)sourceLen; + if ((uLong)stream.avail_in != sourceLen) + return Z_BUF_ERROR; + + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) + return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + + err = inflateInit2(&stream, -MAX_WBITS); + if (err != Z_OK) + return err; + + err = inflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + inflateEnd(&stream); + if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0)) + return Z_DATA_ERROR; + return err; + } + *destLen = stream.total_out; + + err = inflateEnd(&stream); + return err; +} + +static int deflate (Bytef *dest, ulong *destLen, const Bytef *source, ulong sourceLen) +{ + z_stream stream; + int err; + + stream.next_in = (Bytef*)source; + stream.avail_in = (uInt)sourceLen; + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + stream.opaque = (voidpf)0; + + err = deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY); + if (err != Z_OK) return err; + + err = deflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + deflateEnd(&stream); + return err == Z_OK ? Z_BUF_ERROR : err; + } + *destLen = stream.total_out; + + err = deflateEnd(&stream); + return err; +} + +static QFile::Permissions modeToPermissions(quint32 mode) +{ + QFile::Permissions ret; + if (mode & S_IRUSR) + ret |= QFile::ReadOwner; + if (mode & S_IWUSR) + ret |= QFile::WriteOwner; + if (mode & S_IXUSR) + ret |= QFile::ExeOwner; + if (mode & S_IRUSR) + ret |= QFile::ReadUser; + if (mode & S_IWUSR) + ret |= QFile::WriteUser; + if (mode & S_IXUSR) + ret |= QFile::ExeUser; + if (mode & S_IRGRP) + ret |= QFile::ReadGroup; + if (mode & S_IWGRP) + ret |= QFile::WriteGroup; + if (mode & S_IXGRP) + ret |= QFile::ExeGroup; + if (mode & S_IROTH) + ret |= QFile::ReadOther; + if (mode & S_IWOTH) + ret |= QFile::WriteOther; + if (mode & S_IXOTH) + ret |= QFile::ExeOther; + return ret; +} + +static QDateTime readMSDosDate(const uchar *src) +{ + uint dosDate = readUInt(src); + quint64 uDate; + uDate = (quint64)(dosDate >> 16); + uint tm_mday = (uDate & 0x1f); + uint tm_mon = ((uDate & 0x1E0) >> 5); + uint tm_year = (((uDate & 0x0FE00) >> 9) + 1980); + uint tm_hour = ((dosDate & 0xF800) >> 11); + uint tm_min = ((dosDate & 0x7E0) >> 5); + uint tm_sec = ((dosDate & 0x1f) << 1); + + return QDateTime(QDate(tm_year, tm_mon, tm_mday), QTime(tm_hour, tm_min, tm_sec)); +} + +struct LocalFileHeader +{ + uchar signature[4]; // 0x04034b50 + uchar version_needed[2]; + uchar general_purpose_bits[2]; + uchar compression_method[2]; + uchar last_mod_file[4]; + uchar crc_32[4]; + uchar compressed_size[4]; + uchar uncompressed_size[4]; + uchar file_name_length[2]; + uchar extra_field_length[2]; +}; + +struct DataDescriptor +{ + uchar crc_32[4]; + uchar compressed_size[4]; + uchar uncompressed_size[4]; +}; + +struct CentralFileHeader +{ + uchar signature[4]; // 0x02014b50 + uchar version_made[2]; + uchar version_needed[2]; + uchar general_purpose_bits[2]; + uchar compression_method[2]; + uchar last_mod_file[4]; + uchar crc_32[4]; + uchar compressed_size[4]; + uchar uncompressed_size[4]; + uchar file_name_length[2]; + uchar extra_field_length[2]; + uchar file_comment_length[2]; + uchar disk_start[2]; + uchar internal_file_attributes[2]; + uchar external_file_attributes[4]; + uchar offset_local_header[4]; + LocalFileHeader toLocalHeader() const; +}; + +struct EndOfDirectory +{ + uchar signature[4]; // 0x06054b50 + uchar this_disk[2]; + uchar start_of_directory_disk[2]; + uchar num_dir_entries_this_disk[2]; + uchar num_dir_entries[2]; + uchar directory_size[4]; + uchar dir_start_offset[4]; + uchar comment_length[2]; +}; + +struct FileHeader +{ + CentralFileHeader h; + QByteArray file_name; + QByteArray extra_field; + QByteArray file_comment; +}; + +QZipReader::FileInfo::FileInfo() + : isDir(false), isFile(false), isSymLink(false), crc32(0), size(0) +{ +} + +QZipReader::FileInfo::~FileInfo() +{ +} + +QZipReader::FileInfo::FileInfo(const FileInfo &other) +{ + operator=(other); +} + +QZipReader::FileInfo& QZipReader::FileInfo::operator=(const FileInfo &other) +{ + filePath = other.filePath; + isDir = other.isDir; + isFile = other.isFile; + isSymLink = other.isSymLink; + permissions = other.permissions; + crc32 = other.crc32; + size = other.size; + lastModified = other.lastModified; + return *this; +} + +bool QZipReader::FileInfo::isValid() const +{ + return isDir || isFile || isSymLink; +} + +class QZipPrivate +{ +public: + QZipPrivate(QIODevice *device, bool ownDev) + : device(device), ownDevice(ownDev), dirtyFileTree(true), start_of_directory(0) + { + } + + ~QZipPrivate() + { + if (ownDevice) + delete device; + } + + void fillFileInfo(int index, QZipReader::FileInfo &fileInfo) const; + + QIODevice *device; + bool ownDevice; + bool dirtyFileTree; + QList fileHeaders; + QByteArray comment; + uint start_of_directory; +}; + +void QZipPrivate::fillFileInfo(int index, QZipReader::FileInfo &fileInfo) const +{ + FileHeader header = fileHeaders.at(index); + fileInfo.filePath = QString::fromLocal8Bit(header.file_name); + const quint32 mode = (qFromLittleEndian(&header.h.external_file_attributes[0]) >> 16) & 0xFFFF; + fileInfo.isDir = S_ISDIR(mode); + fileInfo.isFile = S_ISREG(mode); + fileInfo.isSymLink = S_ISLNK(mode); + fileInfo.permissions = modeToPermissions(mode); + fileInfo.crc32 = readUInt(header.h.crc_32); + fileInfo.size = readUInt(header.h.uncompressed_size); + fileInfo.lastModified = readMSDosDate(header.h.last_mod_file); +} + +class QZipReaderPrivate : public QZipPrivate +{ +public: + QZipReaderPrivate(QIODevice *device, bool ownDev) + : QZipPrivate(device, ownDev), status(QZipReader::NoError) + { + } + + void scanFiles(); + + QZipReader::Status status; +}; + +class QZipWriterPrivate : public QZipPrivate +{ +public: + QZipWriterPrivate(QIODevice *device, bool ownDev) + : QZipPrivate(device, ownDev), + status(QZipWriter::NoError), + permissions(QFile::ReadOwner | QFile::WriteOwner), + compressionPolicy(QZipWriter::AlwaysCompress) + { + } + + QZipWriter::Status status; + QFile::Permissions permissions; + QZipWriter::CompressionPolicy compressionPolicy; + + enum EntryType { Directory, File, Symlink }; + + void addEntry(EntryType type, const QString &fileName, const QByteArray &contents); +}; + +LocalFileHeader CentralFileHeader::toLocalHeader() const +{ + LocalFileHeader h; + writeUInt(h.signature, 0x04034b50); + copyUShort(h.version_needed, version_needed); + copyUShort(h.general_purpose_bits, general_purpose_bits); + copyUShort(h.compression_method, compression_method); + copyUInt(h.last_mod_file, last_mod_file); + copyUInt(h.crc_32, crc_32); + copyUInt(h.compressed_size, compressed_size); + copyUInt(h.uncompressed_size, uncompressed_size); + copyUShort(h.file_name_length, file_name_length); + copyUShort(h.extra_field_length, extra_field_length); + return h; +} + +void QZipReaderPrivate::scanFiles() +{ + if (!dirtyFileTree) + return; + + if (! (device->isOpen() || device->open(QIODevice::ReadOnly))) { + status = QZipReader::FileOpenError; + return; + } + + if ((device->openMode() & QIODevice::ReadOnly) == 0) { // only read the index from readable files. + status = QZipReader::FileReadError; + return; + } + + dirtyFileTree = false; + uchar tmp[4]; + device->read((char *)tmp, 4); + if (readUInt(tmp) != 0x04034b50) { + qWarning() << "QZip: not a zip file!"; + return; + } + + // find EndOfDirectory header + int i = 0; + int start_of_directory = -1; + int num_dir_entries = 0; + EndOfDirectory eod; + while (start_of_directory == -1) { + int pos = device->size() - (int)sizeof(EndOfDirectory) - i; + if (pos < 0 || i > 65535) { + qWarning() << "QZip: EndOfDirectory not found"; + return; + } + + device->seek(pos); + device->read((char *)&eod, sizeof(EndOfDirectory)); + if (readUInt(eod.signature) == 0x06054b50) + break; + ++i; + } + + // have the eod + start_of_directory = readUInt(eod.dir_start_offset); + num_dir_entries = readUShort(eod.num_dir_entries); + ZDEBUG("start_of_directory at %d, num_dir_entries=%d", start_of_directory, num_dir_entries); + int comment_length = readUShort(eod.comment_length); + if (comment_length != i) + qWarning() << "QZip: failed to parse zip file."; + comment = device->read(qMin(comment_length, i)); + + + device->seek(start_of_directory); + for (i = 0; i < num_dir_entries; ++i) { + FileHeader header; + int read = device->read((char *) &header.h, sizeof(CentralFileHeader)); + if (read < (int)sizeof(CentralFileHeader)) { + qWarning() << "QZip: Failed to read complete header, index may be incomplete"; + break; + } + if (readUInt(header.h.signature) != 0x02014b50) { + qWarning() << "QZip: invalid header signature, index may be incomplete"; + break; + } + + int l = readUShort(header.h.file_name_length); + header.file_name = device->read(l); + if (header.file_name.length() != l) { + qWarning() << "QZip: Failed to read filename from zip index, index may be incomplete"; + break; + } + l = readUShort(header.h.extra_field_length); + header.extra_field = device->read(l); + if (header.extra_field.length() != l) { + qWarning() << "QZip: Failed to read extra field in zip file, skipping file, index may be incomplete"; + break; + } + l = readUShort(header.h.file_comment_length); + header.file_comment = device->read(l); + if (header.file_comment.length() != l) { + qWarning() << "QZip: Failed to read read file comment, index may be incomplete"; + break; + } + + ZDEBUG("found file '%s'", header.file_name.data()); + fileHeaders.append(header); + } +} + +void QZipWriterPrivate::addEntry(EntryType type, const QString &fileName, const QByteArray &contents/*, QFile::Permissions permissions, QZip::Method m*/) +{ +#ifndef NDEBUG + static const char *entryTypes[] = { + "directory", + "file ", + "symlink " }; + ZDEBUG() << "adding" << entryTypes[type] <<":" << fileName.toUtf8().data() << (type == 2 ? (" -> " + contents).constData() : ""); +#endif + + if (! (device->isOpen() || device->open(QIODevice::WriteOnly))) { + status = QZipWriter::FileOpenError; + return; + } + device->seek(start_of_directory); + + // don't compress small files + QZipWriter::CompressionPolicy compression = compressionPolicy; + if (compressionPolicy == QZipWriter::AutoCompress) { + if (contents.length() < 64) + compression = QZipWriter::NeverCompress; + else + compression = QZipWriter::AlwaysCompress; + } + + FileHeader header; + memset(&header.h, 0, sizeof(CentralFileHeader)); + writeUInt(header.h.signature, 0x02014b50); + + writeUShort(header.h.version_needed, 0x14); + writeUInt(header.h.uncompressed_size, contents.length()); + writeMSDosDate(header.h.last_mod_file, QDateTime::currentDateTime()); + QByteArray data = contents; + if (compression == QZipWriter::AlwaysCompress) { + writeUShort(header.h.compression_method, 8); + + ulong len = contents.length(); + // shamelessly copied form zlib + len += (len >> 12) + (len >> 14) + 11; + int res; + do { + data.resize(len); + res = deflate((uchar*)data.data(), &len, (const uchar*)contents.constData(), contents.length()); + + switch (res) { + case Z_OK: + data.resize(len); + break; + case Z_MEM_ERROR: + qWarning("QZip: Z_MEM_ERROR: Not enough memory to compress file, skipping"); + data.resize(0); + break; + case Z_BUF_ERROR: + len *= 2; + break; + } + } while (res == Z_BUF_ERROR); + } +// TODO add a check if data.length() > contents.length(). Then try to store the original and revert the compression method to be uncompressed + writeUInt(header.h.compressed_size, data.length()); + uint crc_32 = ::crc32(0, 0, 0); + crc_32 = ::crc32(crc_32, (const uchar *)contents.constData(), contents.length()); + writeUInt(header.h.crc_32, crc_32); + + header.file_name = fileName.toLocal8Bit(); + if (header.file_name.size() > 0xffff) { + qWarning("QZip: Filename too long, chopping it to 65535 characters"); + header.file_name = header.file_name.left(0xffff); + } + writeUShort(header.h.file_name_length, header.file_name.length()); + //h.extra_field_length[2]; + + writeUShort(header.h.version_made, 3 << 8); + //uchar internal_file_attributes[2]; + //uchar external_file_attributes[4]; + quint32 mode = permissionsToMode(permissions); + switch (type) { + case File: mode |= S_IFREG; break; + case Directory: mode |= S_IFDIR; break; + case Symlink: mode |= S_IFLNK; break; + } + writeUInt(header.h.external_file_attributes, mode << 16); + writeUInt(header.h.offset_local_header, start_of_directory); + + + fileHeaders.append(header); + + LocalFileHeader h = header.h.toLocalHeader(); + device->write((const char *)&h, sizeof(LocalFileHeader)); + device->write(header.file_name); + device->write(data); + start_of_directory = device->pos(); + dirtyFileTree = true; +} + +////////////////////////////// Reader + +/*! + \class QZipReader::FileInfo + \internal + Represents one entry in the zip table of contents. +*/ + +/*! + \variable FileInfo::filePath + The full filepath inside the archive. +*/ + +/*! + \variable FileInfo::isDir + A boolean type indicating if the entry is a directory. +*/ + +/*! + \variable FileInfo::isFile + A boolean type, if it is one this entry is a file. +*/ + +/*! + \variable FileInfo::isSymLink + A boolean type, if it is one this entry is symbolic link. +*/ + +/*! + \variable FileInfo::permissions + A list of flags for the permissions of this entry. +*/ + +/*! + \variable FileInfo::crc32 + The calculated checksum as a crc32 type. +*/ + +/*! + \variable FileInfo::size + The total size of the unpacked content. +*/ + +/*! + \variable FileInfo::d + \internal + private pointer. +*/ + +/*! + \class QZipReader + \internal + \since 4.5 + + \brief the QZipReader class provides a way to inspect the contents of a zip + archive and extract individual files from it. + + QZipReader can be used to read a zip archive either from a file or from any + device. An in-memory QBuffer for instance. The reader can be used to read + which files are in the archive using fileInfoList() and entryInfoAt() but + also to extract individual files using fileData() or even to extract all + files in the archive using extractAll() +*/ + +/*! + Create a new zip archive that operates on the \a fileName. The file will be + opened with the \a mode. +*/ +QZipReader::QZipReader(const QString &archive, QIODevice::OpenMode mode) +{ + QScopedPointer f(new QFile(archive)); + f->open(mode); + QZipReader::Status status; + if (f->error() == QFile::NoError) + status = NoError; + else { + if (f->error() == QFile::ReadError) + status = FileReadError; + else if (f->error() == QFile::OpenError) + status = FileOpenError; + else if (f->error() == QFile::PermissionsError) + status = FilePermissionsError; + else + status = FileError; + } + + d = new QZipReaderPrivate(f.data(), /*ownDevice=*/true); + f.take(); + d->status = status; +} + +/*! + Create a new zip archive that operates on the archive found in \a device. + You have to open the device previous to calling the constructor and only a + device that is readable will be scanned for zip filecontent. + */ +QZipReader::QZipReader(QIODevice *device) + : d(new QZipReaderPrivate(device, /*ownDevice=*/false)) +{ + Q_ASSERT(device); +} + +/*! + Desctructor +*/ +QZipReader::~QZipReader() +{ + close(); + delete d; +} + +/*! + Returns device used for reading zip archive. +*/ +QIODevice* QZipReader::device() const +{ + return d->device; +} + +/*! + Returns true if the user can read the file; otherwise returns false. +*/ +bool QZipReader::isReadable() const +{ + return d->device->isReadable(); +} + +/*! + Returns true if the file exists; otherwise returns false. +*/ +bool QZipReader::exists() const +{ + QFile *f = qobject_cast (d->device); + if (f == 0) + return true; + return f->exists(); +} + +/*! + Returns the list of files the archive contains. +*/ +QList QZipReader::fileInfoList() const +{ + d->scanFiles(); + QList files; + for (int i = 0; i < d->fileHeaders.size(); ++i) { + QZipReader::FileInfo fi; + d->fillFileInfo(i, fi); + files.append(fi); + } + return files; + +} + +/*! + Return the number of items in the zip archive. +*/ +int QZipReader::count() const +{ + d->scanFiles(); + return d->fileHeaders.count(); +} + +/*! + Returns a FileInfo of an entry in the zipfile. + The \a index is the index into the directory listing of the zipfile. + Returns an invalid FileInfo if \a index is out of boundaries. + + \sa fileInfoList() +*/ +QZipReader::FileInfo QZipReader::entryInfoAt(int index) const +{ + d->scanFiles(); + QZipReader::FileInfo fi; + if (index >= 0 && index < d->fileHeaders.count()) + d->fillFileInfo(index, fi); + return fi; +} + +/*! + Fetch the file contents from the zip archive and return the uncompressed bytes. +*/ +QByteArray QZipReader::fileData(const QString &fileName) const +{ + d->scanFiles(); + int i; + for (i = 0; i < d->fileHeaders.size(); ++i) { + if (QString::fromLocal8Bit(d->fileHeaders.at(i).file_name) == fileName) + break; + } + if (i == d->fileHeaders.size()) + return QByteArray(); + + FileHeader header = d->fileHeaders.at(i); + + int compressed_size = readUInt(header.h.compressed_size); + int uncompressed_size = readUInt(header.h.uncompressed_size); + int start = readUInt(header.h.offset_local_header); + //qDebug("uncompressing file %d: local header at %d", i, start); + + d->device->seek(start); + LocalFileHeader lh; + d->device->read((char *)&lh, sizeof(LocalFileHeader)); + uint skip = readUShort(lh.file_name_length) + readUShort(lh.extra_field_length); + d->device->seek(d->device->pos() + skip); + + int compression_method = readUShort(lh.compression_method); + //qDebug("file=%s: compressed_size=%d, uncompressed_size=%d", fileName.toLocal8Bit().data(), compressed_size, uncompressed_size); + + //qDebug("file at %lld", d->device->pos()); + QByteArray compressed = d->device->read(compressed_size); + if (compression_method == 0) { + // no compression + compressed.truncate(uncompressed_size); + return compressed; + } else if (compression_method == 8) { + // Deflate + //qDebug("compressed=%d", compressed.size()); + compressed.truncate(compressed_size); + QByteArray baunzip; + ulong len = qMax(uncompressed_size, 1); + int res; + do { + baunzip.resize(len); + res = inflate((uchar*)baunzip.data(), &len, + (uchar*)compressed.constData(), compressed_size); + + switch (res) { + case Z_OK: + if ((int)len != baunzip.size()) + baunzip.resize(len); + break; + case Z_MEM_ERROR: + qWarning("QZip: Z_MEM_ERROR: Not enough memory"); + break; + case Z_BUF_ERROR: + len *= 2; + break; + case Z_DATA_ERROR: + qWarning("QZip: Z_DATA_ERROR: Input data is corrupted"); + break; + } + } while (res == Z_BUF_ERROR); + return baunzip; + } + qWarning() << "QZip: Unknown compression method"; + return QByteArray(); +} + +/*! + Extracts the full contents of the zip file into \a destinationDir on + the local filesystem. + In case writing or linking a file fails, the extraction will be aborted. +*/ +bool QZipReader::extractAll(const QString &destinationDir) const +{ + QDir baseDir(destinationDir); + + // create directories first + QList allFiles = fileInfoList(); + foreach (FileInfo fi, allFiles) { + const QString absPath = destinationDir + QDir::separator() + fi.filePath; + if (fi.isDir) { + if (!baseDir.mkpath(fi.filePath)) + return false; + if (!QFile::setPermissions(absPath, fi.permissions)) + return false; + } + } + + // set up symlinks + foreach (FileInfo fi, allFiles) { + const QString absPath = destinationDir + QDir::separator() + fi.filePath; + if (fi.isSymLink) { + QString destination = QFile::decodeName(fileData(fi.filePath)); + if (destination.isEmpty()) + return false; + QFileInfo linkFi(absPath); + if (!QFile::exists(linkFi.absolutePath())) + QDir::root().mkpath(linkFi.absolutePath()); + if (!QFile::link(destination, absPath)) + return false; + /* cannot change permission of links + if (!QFile::setPermissions(absPath, fi.permissions)) + return false; + */ + } + } + + foreach (FileInfo fi, allFiles) { + const QString absPath = destinationDir + QDir::separator() + fi.filePath; + if (fi.isFile) { + QFile f(absPath); + if (!f.open(QIODevice::WriteOnly)) + return false; + f.write(fileData(fi.filePath)); + f.setPermissions(fi.permissions); + f.close(); + } + } + + return true; +} + +/*! + \enum QZipReader::Status + + The following status values are possible: + + \value NoError No error occurred. + \value FileReadError An error occurred when reading from the file. + \value FileOpenError The file could not be opened. + \value FilePermissionsError The file could not be accessed. + \value FileError Another file error occurred. +*/ + +/*! + Returns a status code indicating the first error that was met by QZipReader, + or QZipReader::NoError if no error occurred. +*/ +QZipReader::Status QZipReader::status() const +{ + return d->status; +} + +/*! + Close the zip file. +*/ +void QZipReader::close() +{ + d->device->close(); +} + +////////////////////////////// Writer + +/*! + \class QZipWriter + \internal + \since 4.5 + + \brief the QZipWriter class provides a way to create a new zip archive. + + QZipWriter can be used to create a zip archive containing any number of files + and directories. The files in the archive will be compressed in a way that is + compatible with common zip reader applications. +*/ + + +/*! + Create a new zip archive that operates on the \a archive filename. The file will + be opened with the \a mode. + \sa isValid() +*/ +QZipWriter::QZipWriter(const QString &fileName, QIODevice::OpenMode mode) +{ + QScopedPointer f(new QFile(fileName)); + f->open(mode); + QZipWriter::Status status; + if (f->error() == QFile::NoError) + status = QZipWriter::NoError; + else { + if (f->error() == QFile::WriteError) + status = QZipWriter::FileWriteError; + else if (f->error() == QFile::OpenError) + status = QZipWriter::FileOpenError; + else if (f->error() == QFile::PermissionsError) + status = QZipWriter::FilePermissionsError; + else + status = QZipWriter::FileError; + } + + d = new QZipWriterPrivate(f.data(), /*ownDevice=*/true); + f.take(); + d->status = status; +} + +/*! + Create a new zip archive that operates on the archive found in \a device. + You have to open the device previous to calling the constructor and + only a device that is readable will be scanned for zip filecontent. + */ +QZipWriter::QZipWriter(QIODevice *device) + : d(new QZipWriterPrivate(device, /*ownDevice=*/false)) +{ + Q_ASSERT(device); +} + +QZipWriter::~QZipWriter() +{ + close(); + delete d; +} + +/*! + Returns device used for writing zip archive. +*/ +QIODevice* QZipWriter::device() const +{ + return d->device; +} + +/*! + Returns true if the user can write to the archive; otherwise returns false. +*/ +bool QZipWriter::isWritable() const +{ + return d->device->isWritable(); +} + +/*! + Returns true if the file exists; otherwise returns false. +*/ +bool QZipWriter::exists() const +{ + QFile *f = qobject_cast (d->device); + if (f == 0) + return true; + return f->exists(); +} + +/*! + \enum QZipWriter::Status + + The following status values are possible: + + \value NoError No error occurred. + \value FileWriteError An error occurred when writing to the device. + \value FileOpenError The file could not be opened. + \value FilePermissionsError The file could not be accessed. + \value FileError Another file error occurred. +*/ + +/*! + Returns a status code indicating the first error that was met by QZipWriter, + or QZipWriter::NoError if no error occurred. +*/ +QZipWriter::Status QZipWriter::status() const +{ + return d->status; +} + +/*! + \enum QZipWriter::CompressionPolicy + + \value AlwaysCompress A file that is added is compressed. + \value NeverCompress A file that is added will be stored without changes. + \value AutoCompress A file that is added will be compressed only if that will give a smaller file. +*/ + +/*! + Sets the policy for compressing newly added files to the new \a policy. + + \note the default policy is AlwaysCompress + + \sa compressionPolicy() + \sa addFile() +*/ +void QZipWriter::setCompressionPolicy(CompressionPolicy policy) +{ + d->compressionPolicy = policy; +} + +/*! + Returns the currently set compression policy. + \sa setCompressionPolicy() + \sa addFile() +*/ +QZipWriter::CompressionPolicy QZipWriter::compressionPolicy() const +{ + return d->compressionPolicy; +} + +/*! + Sets the permissions that will be used for newly added files. + + \note the default permissions are QFile::ReadOwner | QFile::WriteOwner. + + \sa creationPermissions() + \sa addFile() +*/ +void QZipWriter::setCreationPermissions(QFile::Permissions permissions) +{ + d->permissions = permissions; +} + +/*! + Returns the currently set creation permissions. + + \sa setCreationPermissions() + \sa addFile() +*/ +QFile::Permissions QZipWriter::creationPermissions() const +{ + return d->permissions; +} + +/*! + Add a file to the archive with \a data as the file contents. + The file will be stored in the archive using the \a fileName which + includes the full path in the archive. + + The new file will get the file permissions based on the current + creationPermissions and it will be compressed using the zip compression + based on the current compression policy. + + \sa setCreationPermissions() + \sa setCompressionPolicy() +*/ +void QZipWriter::addFile(const QString &fileName, const QByteArray &data) +{ + d->addEntry(QZipWriterPrivate::File, fileName, data); +} + +/*! + Add a file to the archive with \a device as the source of the contents. + The contents returned from QIODevice::readAll() will be used as the + filedata. + The file will be stored in the archive using the \a fileName which + includes the full path in the archive. +*/ +void QZipWriter::addFile(const QString &fileName, QIODevice *device) +{ + Q_ASSERT(device); + QIODevice::OpenMode mode = device->openMode(); + bool opened = false; + if ((mode & QIODevice::ReadOnly) == 0) { + opened = true; + if (! device->open(QIODevice::ReadOnly)) { + d->status = FileOpenError; + return; + } + } + d->addEntry(QZipWriterPrivate::File, fileName, device->readAll()); + if (opened) + device->close(); +} + +/*! + Create a new directory in the archive with the specified \a dirName and + the \a permissions; +*/ +void QZipWriter::addDirectory(const QString &dirName) +{ + QString name = dirName; + // separator is mandatory + if (!name.endsWith(QDir::separator())) + name.append(QDir::separator()); + d->addEntry(QZipWriterPrivate::Directory, name, QByteArray()); +} + +/*! + Create a new symbolic link in the archive with the specified \a dirName + and the \a permissions; + A symbolic link contains the destination (relative) path and name. +*/ +void QZipWriter::addSymLink(const QString &fileName, const QString &destination) +{ + d->addEntry(QZipWriterPrivate::Symlink, fileName, QFile::encodeName(destination)); +} + +/*! + Closes the zip file. +*/ +void QZipWriter::close() +{ + if (!(d->device->openMode() & QIODevice::WriteOnly)) { + d->device->close(); + return; + } + + //qDebug("QZip::close writing directory, %d entries", d->fileHeaders.size()); + d->device->seek(d->start_of_directory); + // write new directory + for (int i = 0; i < d->fileHeaders.size(); ++i) { + const FileHeader &header = d->fileHeaders.at(i); + d->device->write((const char *)&header.h, sizeof(CentralFileHeader)); + d->device->write(header.file_name); + d->device->write(header.extra_field); + d->device->write(header.file_comment); + } + int dir_size = d->device->pos() - d->start_of_directory; + // write end of directory + EndOfDirectory eod; + memset(&eod, 0, sizeof(EndOfDirectory)); + writeUInt(eod.signature, 0x06054b50); + //uchar this_disk[2]; + //uchar start_of_directory_disk[2]; + writeUShort(eod.num_dir_entries_this_disk, d->fileHeaders.size()); + writeUShort(eod.num_dir_entries, d->fileHeaders.size()); + writeUInt(eod.directory_size, dir_size); + writeUInt(eod.dir_start_offset, d->start_of_directory); + writeUShort(eod.comment_length, d->comment.length()); + + d->device->write((const char *)&eod, sizeof(EndOfDirectory)); + d->device->write(d->comment); + d->device->close(); +} + +QT_END_NAMESPACE diff --git a/code/ryzom/tools/client/ryzom_installer/src/qzipreader.h b/code/ryzom/tools/client/ryzom_installer/src/qzipreader.h new file mode 100644 index 000000000..720846ae2 --- /dev/null +++ b/code/ryzom/tools/client/ryzom_installer/src/qzipreader.h @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QZIPREADER_H +#define QZIPREADER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the QZipReader class. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +QT_BEGIN_NAMESPACE + +class QZipReaderPrivate; + +class QZipReader +{ +public: + QZipReader(const QString &fileName, QIODevice::OpenMode mode = QIODevice::ReadOnly ); + + explicit QZipReader(QIODevice *device); + ~QZipReader(); + + QIODevice* device() const; + + bool isReadable() const; + bool exists() const; + + struct FileInfo + { + FileInfo(); + FileInfo(const FileInfo &other); + ~FileInfo(); + FileInfo &operator=(const FileInfo &other); + bool isValid() const; + QString filePath; + uint isDir : 1; + uint isFile : 1; + uint isSymLink : 1; + QFile::Permissions permissions; + uint crc32; + qint64 size; + QDateTime lastModified; + void *d; + }; + + QList fileInfoList() const; + int count() const; + + FileInfo entryInfoAt(int index) const; + QByteArray fileData(const QString &fileName) const; + bool extractAll(const QString &destinationDir) const; + + enum Status { + NoError, + FileReadError, + FileOpenError, + FilePermissionsError, + FileError + }; + + Status status() const; + + void close(); + +private: + QZipReaderPrivate *d; + Q_DISABLE_COPY(QZipReader) +}; + +QT_END_NAMESPACE + +#endif // QZIPREADER_H diff --git a/code/ryzom/tools/client/ryzom_installer/src/qzipwriter.h b/code/ryzom/tools/client/ryzom_installer/src/qzipwriter.h new file mode 100644 index 000000000..a92595479 --- /dev/null +++ b/code/ryzom/tools/client/ryzom_installer/src/qzipwriter.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QZIPWRITER_H +#define QZIPWRITER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the QZipWriter class. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +QT_BEGIN_NAMESPACE + +class QZipWriterPrivate; + + +class QZipWriter +{ +public: + QZipWriter(const QString &fileName, QIODevice::OpenMode mode = (QIODevice::WriteOnly | QIODevice::Truncate) ); + + explicit QZipWriter(QIODevice *device); + ~QZipWriter(); + + QIODevice* device() const; + + bool isWritable() const; + bool exists() const; + + enum Status { + NoError, + FileWriteError, + FileOpenError, + FilePermissionsError, + FileError + }; + + Status status() const; + + enum CompressionPolicy { + AlwaysCompress, + NeverCompress, + AutoCompress + }; + + void setCompressionPolicy(CompressionPolicy policy); + CompressionPolicy compressionPolicy() const; + + void setCreationPermissions(QFile::Permissions permissions); + QFile::Permissions creationPermissions() const; + + void addFile(const QString &fileName, const QByteArray &data); + + void addFile(const QString &fileName, QIODevice *device); + + void addDirectory(const QString &dirName); + + void addSymLink(const QString &fileName, const QString &destination); + + void close(); +private: + QZipWriterPrivate *d; + Q_DISABLE_COPY(QZipWriter) +}; + +QT_END_NAMESPACE + +#endif // QZIPWRITER_H diff --git a/code/ryzom/tools/client/ryzom_installer/src/settingsdialog.cpp b/code/ryzom/tools/client/ryzom_installer/src/settingsdialog.cpp new file mode 100644 index 000000000..a919bc2c7 --- /dev/null +++ b/code/ryzom/tools/client/ryzom_installer/src/settingsdialog.cpp @@ -0,0 +1,38 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include "stdpch.h" +#include "settingsdialog.h" + +#ifdef DEBUG_NEW + #define new DEBUG_NEW +#endif + +CSettingsDialog::CSettingsDialog():QDialog() +{ + setupUi(this); +} + +CSettingsDialog::~CSettingsDialog() +{ +} + +void CSettingsDialog::accept() +{ + // TODO: add save code + + QDialog::accept(); +} diff --git a/code/ryzom/tools/client/ryzom_installer/src/settingsdialog.h b/code/ryzom/tools/client/ryzom_installer/src/settingsdialog.h new file mode 100644 index 000000000..1dd33f88f --- /dev/null +++ b/code/ryzom/tools/client/ryzom_installer/src/settingsdialog.h @@ -0,0 +1,34 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#ifndef SETTINGSDIALOG_H +#define SETTINGSDIALOG_H + +#include "ui_settings.h" + +class CSettingsDialog : public QDialog, public Ui::SettingsDialog +{ + Q_OBJECT + +public: + CSettingsDialog(); + virtual ~CSettingsDialog(); + +private slots: + void accept(); +}; + +#endif diff --git a/code/ryzom/tools/client/ryzom_installer/src/stdpch.cpp b/code/ryzom/tools/client/ryzom_installer/src/stdpch.cpp new file mode 100644 index 000000000..a3d45576c --- /dev/null +++ b/code/ryzom/tools/client/ryzom_installer/src/stdpch.cpp @@ -0,0 +1,17 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include "stdpch.h" diff --git a/code/ryzom/tools/client/ryzom_installer/src/stdpch.h b/code/ryzom/tools/client/ryzom_installer/src/stdpch.h new file mode 100644 index 000000000..5d64b580f --- /dev/null +++ b/code/ryzom/tools/client/ryzom_installer/src/stdpch.h @@ -0,0 +1,65 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#ifndef STDPCH_H +#define STDPCH_H + +#if defined(_MSC_VER) && defined(_DEBUG) + #define _CRTDBG_MAP_ALLOC + #include + #include + #define DEBUG_NEW new(_NORMAL_BLOCK, __FILE__, __LINE__) + #undef realloc +#endif + +#include +#include +#include +#include +#include +#include +#include + +#ifndef _DEBUG +#define QT_NO_DEBUG_OUTPUT +#endif + +#include + +#ifdef Q_COMPILER_RVALUE_REFS +#undef Q_COMPILER_RVALUE_REFS +#endif + +#include +#include +#include + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) +#define USE_QT5 +#endif + +#ifdef USE_QT5 +#include +#include +#endif + +#include + +#include +#include + +#endif + diff --git a/code/ryzom/tools/client/ryzom_installer/src/utils.cpp b/code/ryzom/tools/client/ryzom_installer/src/utils.cpp new file mode 100644 index 000000000..aa060a1de --- /dev/null +++ b/code/ryzom/tools/client/ryzom_installer/src/utils.cpp @@ -0,0 +1,182 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include "stdpch.h" +#include "utils.h" + +QString qFromUtf8(const std::string &str) +{ + return QString::fromUtf8(str.c_str()); +} + +std::string qToUtf8(const QString &str) +{ + return str.toUtf8().constData(); +} + +QString qFromUtf16(const ucstring &str) +{ + return QString::fromUtf16(str.c_str()); +} + +ucstring qToUtf16(const QString &str) +{ + return ucstring::makeFromUtf8(qToUtf8(str)); +} + +QString qFromWide(const wchar_t *str) +{ + return QString::fromUtf16((ushort*)str); +} + +wchar_t* qToWide(const QString &str) +{ + return (wchar_t*)str.utf16(); +} + + +// CreateLink - Uses the Shell's IShellLink and IPersistFile interfaces +// to create and store a shortcut to the specified object. +// +// Returns the result of calling the member functions of the interfaces. +// +// Parameters: +// lpszPathObj - Address of a buffer that contains the path of the object, +// including the file name. +// lpszPathLink - Address of a buffer that contains the path where the +// Shell link is to be stored, including the file name. +// lpszDesc - Address of a buffer that contains a description of the +// Shell link, stored in the Comment field of the link +// properties. + +HRESULT CreateLink(const QString &pathObj, const QString &pathLink, const QString &desc) +{ + IShellLinkW* psl; + + // Get a pointer to the IShellLink interface. It is assumed that CoInitialize + // has already been called. + HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl); + if (SUCCEEDED(hres)) + { + IPersistFile* ppf; + + // Set the path to the shortcut target and add the description. + psl->SetPath(qToWide(pathObj)); + psl->SetDescription(qToWide(desc)); + psl->SetArguments(L"--profil "); + psl->SetWorkingDirectory(L""); + + // Query IShellLink for the IPersistFile interface, used for saving the + // shortcut in persistent storage. + hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf); + + if (SUCCEEDED(hres)) + { + // Add code here to check return value from MultiByteWideChar + // for success. + + // Save the link by calling IPersistFile::Save. + hres = ppf->Save(qToWide(pathLink), TRUE); + ppf->Release(); + } + psl->Release(); + } + return hres; +} + +// ResolveIt - Uses the Shell's IShellLink and IPersistFile interfaces +// to retrieve the path and description from an existing shortcut. +// +// Returns the result of calling the member functions of the interfaces. +// +// Parameters: +// hwnd - A handle to the parent window. The Shell uses this window to +// display a dialog box if it needs to prompt the user for more +// information while resolving the link. +// lpszLinkFile - Address of a buffer that contains the path of the link, +// including the file name. +// lpszPath - Address of a buffer that receives the path of the link +// target, including the file name. +// lpszDesc - Address of a buffer that receives the description of the +// Shell link, stored in the Comment field of the link +// properties. + +HRESULT ResolveIt(HWND hwnd, const QString &linkFile, QString &path) +{ + IShellLinkW* psl; + WIN32_FIND_DATAW wfd; + + path.clear(); // Assume failure + + // Get a pointer to the IShellLink interface. It is assumed that CoInitialize + // has already been called. + HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl); + if (SUCCEEDED(hres)) + { + IPersistFile* ppf; + + // Get a pointer to the IPersistFile interface. + hres = psl->QueryInterface(IID_IPersistFile, (void**)&ppf); + + if (SUCCEEDED(hres)) + { + // Add code here to check return value from MultiByteWideChar + // for success. + + // Load the shortcut. + hres = ppf->Load(qToWide(linkFile), STGM_READ); + + if (SUCCEEDED(hres)) + { + // Resolve the link. + hres = psl->Resolve(hwnd, 0); + + if (SUCCEEDED(hres)) + { + WCHAR szGotPath[MAX_PATH]; + + // Get the path to the link target. + hres = psl->GetPath(szGotPath, MAX_PATH, (WIN32_FIND_DATAW*)&wfd, SLGP_SHORTPATH); + + if (SUCCEEDED(hres)) + { + WCHAR szDescription[MAX_PATH]; + + // Get the description of the target. + hres = psl->GetDescription(szDescription, MAX_PATH); + + if (SUCCEEDED(hres)) + { + // Handle success + path = qFromWide(szGotPath); + } + else + { + } + } + } + } + + // Release the pointer to the IPersistFile interface. + ppf->Release(); + } + + // Release the pointer to the IShellLink interface. + psl->Release(); + } + + return hres; +} diff --git a/code/ryzom/tools/client/ryzom_installer/src/utils.h b/code/ryzom/tools/client/ryzom_installer/src/utils.h new file mode 100644 index 000000000..179adae4d --- /dev/null +++ b/code/ryzom/tools/client/ryzom_installer/src/utils.h @@ -0,0 +1,47 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#ifndef UTILS_H +#define UTILS_H + +#include + +#include + +/** + * Utils functions + * + * \author Cedric 'Kervala' OCHS + * \date 2016 + */ + +// Convert a UTF-8 string to QString +QString qFromUtf8(const std::string &str); + +// Convert a QString to UTF-8 string +std::string qToUtf8(const QString &str); + +// Convert a UTF-16 string to QString +QString qFromUtf16(const ucstring &str); + +// Convert a QString to UTF-16 string +ucstring qToUtf16(const QString &str); + +QString qFromWide(const wchar_t *str); + +wchar_t* qToWide(const QString &str); + +#endif diff --git a/code/ryzom/tools/client/ryzom_installer/src/wizarddialog.cpp b/code/ryzom/tools/client/ryzom_installer/src/wizarddialog.cpp new file mode 100644 index 000000000..1cb13c58a --- /dev/null +++ b/code/ryzom/tools/client/ryzom_installer/src/wizarddialog.cpp @@ -0,0 +1,219 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include "stdpch.h" +#include "wizarddialog.h" +#include "configfile.h" + +#include "nel/misc/system_info.h" +#include "nel/misc/common.h" + +#ifdef DEBUG_NEW + #define new DEBUG_NEW +#endif + +QString qBytesToHumanReadable(qint64 bytes) +{ + static std::vector units; + + if (units.empty()) + { + units.push_back(QObject::tr("B").toUtf8().constData()); + units.push_back(QObject::tr("KiB").toUtf8().constData()); + units.push_back(QObject::tr("MiB").toUtf8().constData()); + units.push_back(QObject::tr("GiB").toUtf8().constData()); + units.push_back(QObject::tr("TiB").toUtf8().constData()); + units.push_back(QObject::tr("PiB").toUtf8().constData()); + } + + return QString::fromUtf8(NLMISC::bytesToHumanReadable(bytes).c_str()); +} + +CWizardDialog::CWizardDialog():QDialog() +{ + setupUi(this); + + currentDirectoryRadioButton->setVisible(false); + oldDirectoryRadioButton->setVisible(false); + + // enable download radio button by default + internetRadioButton->setChecked(true); + + // if launched from current directory, it means we just patched files + m_currentDirectory = CConfigFile::getInstance()->getCurrentDirectory(); + + if (!CConfigFile::getInstance()->isRyzomInstalledIn(m_currentDirectory)) + { + // current directory is a child of Ryzom root directory + m_currentDirectory = CConfigFile::getInstance()->getParentDirectory(); + + if (!CConfigFile::getInstance()->isRyzomInstalledIn(m_currentDirectory)) + { + // Ryzom is in the same directory as Ryzom Installer + m_currentDirectory = CConfigFile::getInstance()->getApplicationDirectory(); + + if (!CConfigFile::getInstance()->isRyzomInstalledIn(m_currentDirectory)) + { + m_currentDirectory.clear(); + } + } + } + + // display found directory + if (!m_currentDirectory.isEmpty()) + { + currentDirectoryRadioButton->setText(tr("Current directory: %1").arg(m_currentDirectory)); + currentDirectoryRadioButton->setVisible(true); + currentDirectoryRadioButton->setChecked(true); + } + + m_oldDirectory = CConfigFile::getInstance()->getOldInstallationDirectory(); + + // found a previous installation + if (CConfigFile::getInstance()->areRyzomDataInstalledIn(m_oldDirectory)) + { + oldDirectoryRadioButton->setText(tr("Old installation: %1").arg(m_oldDirectory)); + oldDirectoryRadioButton->setVisible(true); + + if (m_currentDirectory.isEmpty()) oldDirectoryRadioButton->setChecked(true); + } + + updateAnotherLocationText(); + + m_dstDirectory = CConfigFile::getNewInstallationDirectory(); + + updateDestinationText(); + + // check whether OS architecture is 32 or 64 bits + // TODO: 64 bits client only supported under Vista+ + if (CConfigFile::has64bitsOS()) + { + clientArchGroupBox->setVisible(true); + clientArch64RadioButton->setChecked(true); + } + else + { + clientArchGroupBox->setVisible(false); + clientArch32RadioButton->setChecked(true); + } + + const CServer &server = CConfigFile::getInstance()->getServer(); + + internetRadioButton->setText(tr("Internet (%1 to download)").arg(qBytesToHumanReadable(server.dataCompressedSize))); + destinationGroupBox->setTitle(tr("Files will be installed to (requires %1):").arg(qBytesToHumanReadable(server.dataUncompressedSize))); + + connect(anotherLocationBrowseButton, SIGNAL(clicked()), SLOT(onAnotherLocationBrowseButtonClicked())); + connect(destinationBrowseButton, SIGNAL(clicked()), SLOT(onDestinationBrowseButtonClicked())); + + // TODO: if found a folder with initial data, get its total size and check if at least that size is free on the disk + + // by default, advanced parameters are hidden + onShowAdvancedParameters(Qt::Unchecked); + + connect(advancedCheckBox, SIGNAL(stateChanged(int)), SLOT(onShowAdvancedParameters(int))); +} + +CWizardDialog::~CWizardDialog() +{ +} + +void CWizardDialog::onShowAdvancedParameters(int state) +{ + advancedFrame->setVisible(state != Qt::Unchecked); + + adjustSize(); +} + +void CWizardDialog::onAnotherLocationBrowseButtonClicked() +{ + QString directory; + + for(;;) + { + directory = QFileDialog::getExistingDirectory(this, tr("Please choose directory where is installed Ryzom")); + + if (directory.isEmpty()) return; + + if (CConfigFile::getInstance()->isRyzomInstalledIn(directory)) break; + + QMessageBox::StandardButton res = QMessageBox::warning(this, tr("Unable to find Ryzom"), tr("Unable to find Ryzom in selected directory. Please choose another one or cancel.")); + } + + m_anotherDirectory = directory; + + // if we browse to a Ryzom installation, we want to use it + anotherLocationRadioButton->setChecked(true); + + updateAnotherLocationText(); +} + +void CWizardDialog::onDestinationBrowseButtonClicked() +{ + QString directory = QFileDialog::getExistingDirectory(this, tr("Please choose directory where to install Ryzom")); + + if (directory.isEmpty()) return; + + m_dstDirectory = directory; + + updateDestinationText(); +} + +void CWizardDialog::updateAnotherLocationText() +{ + anotherLocationRadioButton->setText(tr("Another location: %1").arg(m_anotherDirectory.isEmpty() ? tr("Undefined"):m_anotherDirectory)); +} + +void CWizardDialog::updateDestinationText() +{ + destinationLabel->setText(m_dstDirectory); +} + +void CWizardDialog::accept() +{ + // check free disk space + qint64 freeSpace = NLMISC::CSystemInfo::availableHDSpace(m_dstDirectory.toUtf8().constData()); + + const CServer &server = CConfigFile::getInstance()->getServer(); + + if (freeSpace < server.dataUncompressedSize) + { + QMessageBox::StandardButton res = QMessageBox::warning(this, tr("Not enough free disk space"), tr("You don't have enough free space on this disk, please make more space or choose a directory on another disk.")); + return; + } + + if (currentDirectoryRadioButton->isChecked()) + { + CConfigFile::getInstance()->setSrcServerDirectory(m_currentDirectory); + } + else if (oldDirectoryRadioButton->isChecked()) + { + CConfigFile::getInstance()->setSrcServerDirectory(m_oldDirectory); + } + else if (anotherLocationRadioButton->isChecked()) + { + CConfigFile::getInstance()->setSrcServerDirectory(m_anotherDirectory); + } + else + { + CConfigFile::getInstance()->setSrcServerDirectory(""); + } + + CConfigFile::getInstance()->setInstallationDirectory(m_dstDirectory); + CConfigFile::getInstance()->setUse64BitsClient(clientArch64RadioButton->isChecked()); + CConfigFile::getInstance()->save(); + + QDialog::accept(); +} diff --git a/code/ryzom/tools/client/ryzom_installer/src/wizarddialog.h b/code/ryzom/tools/client/ryzom_installer/src/wizarddialog.h new file mode 100644 index 000000000..8ac146184 --- /dev/null +++ b/code/ryzom/tools/client/ryzom_installer/src/wizarddialog.h @@ -0,0 +1,53 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#ifndef WIZARDDIALOG_H +#define WIZARDDIALOG_H + +#include "ui_wizard.h" + +/** + * Wizard displayed at first launch, that asks user to choose source and destination directories. + * + * \author Cedric 'Kervala' OCHS + * \date 2016 + */ +class CWizardDialog : public QDialog, public Ui::WizardDialog +{ + Q_OBJECT + +public: + CWizardDialog(); + virtual ~CWizardDialog(); + +private slots: + void onShowAdvancedParameters(int state); + void onAnotherLocationBrowseButtonClicked(); + void onDestinationBrowseButtonClicked(); + + void accept(); + +private: + void updateAnotherLocationText(); + void updateDestinationText(); + + QString m_currentDirectory; + QString m_oldDirectory; + QString m_anotherDirectory; + QString m_dstDirectory; +}; + +#endif diff --git a/code/ryzom/tools/client/ryzom_installer/ui/configurations.ui b/code/ryzom/tools/client/ryzom_installer/ui/configurations.ui new file mode 100644 index 000000000..82cd5f9e0 --- /dev/null +++ b/code/ryzom/tools/client/ryzom_installer/ui/configurations.ui @@ -0,0 +1,305 @@ + + + ConfigurationsDialog + + + + 0 + 0 + 501 + 351 + + + + Dialog + + + + + + + + + + List of configurations: + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + QAbstractItemView::NoEditTriggers + + + + + + + + + Add + + + + + + + Delete + + + + + + + + + + + + + Configuration: + + + + + + + 0 + + + + + + + Account: + + + + + + + + + + Name: + + + + + + + + + + Server: + + + + + + + + Atys + + + + + Yubo + + + + + + + + Executable: + + + + + + + + + ryzom_client_r.exe + + + + + + + Browse... + + + + + + + + + Arguments: + + + + + + + + + + Comments: + + + + + + + + 0 + 1 + + + + + + + + Directory: + + + + + + + + + ~/.ryzom/0 + + + + + + + Open + + + + + + + + + Create shortcuts: + + + + + + + + + Desktop + + + + + + + Start Menu + + + + + + + + + FV 3.0.0 + + + + + + + Client version: + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + configurationsListView + addButton + deleteButton + accountEdit + nameEdit + serverComboBox + executableBrowseButton + argumentsEdit + commentsEdit + directoryButton + shortcutsDesktopButton + shortcutsMenuButton + + + + + buttonBox + accepted() + ConfigurationsDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + ConfigurationsDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/code/ryzom/tools/client/ryzom_installer/ui/mainwindow.ui b/code/ryzom/tools/client/ryzom_installer/ui/mainwindow.ui new file mode 100644 index 000000000..ec7e52ad7 --- /dev/null +++ b/code/ryzom/tools/client/ryzom_installer/ui/mainwindow.ui @@ -0,0 +1,212 @@ + + + MainWindow + + + + 0 + 0 + 627 + 539 + + + + Ryzom Installer + + + + + + + + + + + + :/images/background.png + + + + + + + true + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"><br /></p></body></html> + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 0 + + + 0 + + + Qt::AlignCenter + + + %p% + + + + + + + Resume + + + + + + + Stop + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + Atys + + + + + + + + Play + + + + + + + Configure + + + + + + + + + + + + 0 + 0 + 627 + 21 + + + + + &Settings + + + + + + + + + &Help + + + + + + + + + + false + + + + + About Qt + + + + + About... + + + + + &Configurations + + + + + &Directories + + + + + &Quit + + + + + + + + diff --git a/code/ryzom/tools/client/ryzom_installer/ui/settings.ui b/code/ryzom/tools/client/ryzom_installer/ui/settings.ui new file mode 100644 index 000000000..1ab8f32af --- /dev/null +++ b/code/ryzom/tools/client/ryzom_installer/ui/settings.ui @@ -0,0 +1,113 @@ + + + SettingsDialog + + + + 0 + 0 + 400 + 150 + + + + Dialog + + + + + + + + Language + + + + + + + + + + Base location of Ryzom files: %1 + + + + + + + Browse... + + + + + + + Location of source Ryzom files: %1 + + + + + + + Browse... + + + + + + + Use 64 bits client + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + SettingsDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + SettingsDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/code/ryzom/tools/client/ryzom_installer/ui/wizard.ui b/code/ryzom/tools/client/ryzom_installer/ui/wizard.ui new file mode 100644 index 000000000..b7b6475e0 --- /dev/null +++ b/code/ryzom/tools/client/ryzom_installer/ui/wizard.ui @@ -0,0 +1,260 @@ + + + WizardDialog + + + Qt::ApplicationModal + + + + 0 + 0 + 402 + 464 + + + + Ryzom Installer + + + true + + + + QLayout::SetFixedSize + + + + + Welcome to Ryzom Installer! + +This program will allow you to download, install, migrate, configure or manage Ryzom on your computer. + +Just follow the different steps and make your choice between the different propositions. + + + Qt::AlignJustify|Qt::AlignTop + + + true + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Show advanced parameters (expert) + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 6 + + + 0 + + + 9 + + + 0 + + + 0 + + + + + Files will be installed from: + + + + + + Current directory + + + true + + + + + + + Old installation: %1 + + + true + + + + + + + 6 + + + + + Another location: %1 + + + true + + + + + + + Browse... + + + + + + + + + Internet (%1 GiB to download) + + + + + + + + + + Files will be installed to (requires 10 GiB): + + + + + + c:\ + + + + + + + Browse... + + + + + + + + + + Do you prefer to use a 64 or 32 bits client? + + + + + + 64 bits (recommended) + + + true + + + + + + + 32 bits + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + currentDirectoryRadioButton + oldDirectoryRadioButton + anotherLocationRadioButton + anotherLocationBrowseButton + internetRadioButton + destinationBrowseButton + clientArch64RadioButton + clientArch32RadioButton + + + + + buttonBox + accepted() + WizardDialog + accept() + + + 227 + 406 + + + 157 + 274 + + + + + buttonBox + rejected() + WizardDialog + reject() + + + 295 + 412 + + + 286 + 274 + + + + + From d819704717af9b03a05d3f236dfe5400803b43e7 Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 27 Feb 2016 18:43:14 +0100 Subject: [PATCH 02/32] Changed: Renamed configurations to profiles --- .../client/ryzom_installer/src/archive.cpp | 6 +- .../client/ryzom_installer/src/archive.h | 4 +- .../client/ryzom_installer/src/configfile.cpp | 118 +++++++++--------- .../client/ryzom_installer/src/configfile.h | 30 ++--- .../client/ryzom_installer/src/mainwindow.cpp | 10 +- ...gurationsdialog.cpp => profilesdialog.cpp} | 8 +- ...onfigurationsdialog.h => profilesdialog.h} | 12 +- .../ui/{configurations.ui => profiles.ui} | 0 8 files changed, 94 insertions(+), 94 deletions(-) rename code/ryzom/tools/client/ryzom_installer/src/{configurationsdialog.cpp => profilesdialog.cpp} (84%) rename code/ryzom/tools/client/ryzom_installer/src/{configurationsdialog.h => profilesdialog.h} (78%) rename code/ryzom/tools/client/ryzom_installer/ui/{configurations.ui => profiles.ui} (100%) diff --git a/code/ryzom/tools/client/ryzom_installer/src/archive.cpp b/code/ryzom/tools/client/ryzom_installer/src/archive.cpp index 23f61330c..bc40c3061 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/archive.cpp +++ b/code/ryzom/tools/client/ryzom_installer/src/archive.cpp @@ -338,7 +338,7 @@ bool CArchive::copyServerFiles() return copyFiles(files); } -bool CArchive::copyConfigurationFiles() +bool CArchive::copyProfileFiles() { emit extractPrepare(); @@ -372,7 +372,7 @@ bool CArchive::copyServerFiles(const QString &src, const QString &dst) return true; } -bool CArchive::copyConfigurationFiles(const QString &src, const QString &dst) +bool CArchive::copyProfileFiles(const QString &src, const QString &dst) { if (src.isEmpty() || dst.isEmpty()) return false; @@ -382,7 +382,7 @@ bool CArchive::copyConfigurationFiles(const QString &src, const QString &dst) // create destination directory QDir().mkpath(dst); - QFuture future = QtConcurrent::run(this, &CArchive::copyConfigurationFiles); + QFuture future = QtConcurrent::run(this, &CArchive::copyProfileFiles); return true; } diff --git a/code/ryzom/tools/client/ryzom_installer/src/archive.h b/code/ryzom/tools/client/ryzom_installer/src/archive.h index ac9c0c036..0f8284b9e 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/archive.h +++ b/code/ryzom/tools/client/ryzom_installer/src/archive.h @@ -33,7 +33,7 @@ public: bool extract(const QString &filename, const QString &dest); bool copyServerFiles(const QString &src, const QString &dst); - bool copyConfigurationFiles(const QString &src, const QString &dst); + bool copyProfileFiles(const QString &src, const QString &dst); void stop(); bool mustStop(); @@ -80,7 +80,7 @@ protected: bool progress(const std::string &filename, uint32 currentFile, uint32 totalFiles); bool copyServerFiles(); - bool copyConfigurationFiles(); + bool copyProfileFiles(); bool copyFiles(const FilesToCopy &files); static void getFilesList(const QString &srcDir, const QString &dstDir, const QStringList &filter, FilesToCopy &files); diff --git a/code/ryzom/tools/client/ryzom_installer/src/configfile.cpp b/code/ryzom/tools/client/ryzom_installer/src/configfile.cpp index f0b8c7577..0f73bd96e 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/configfile.cpp +++ b/code/ryzom/tools/client/ryzom_installer/src/configfile.cpp @@ -25,11 +25,11 @@ #endif const CServer NoServer; -const CConfiguration NoConfiguration; +const CProfile NoProfile; CConfigFile *CConfigFile::s_instance = NULL; -CConfigFile::CConfigFile(QObject *parent):QObject(parent), m_defaultServer(0), m_defaultConfiguration(0), m_use64BitsClient(false) +CConfigFile::CConfigFile(QObject *parent):QObject(parent), m_defaultServer(0), m_defaultProfile(0), m_use64BitsClient(false) { s_instance = this; @@ -92,26 +92,26 @@ bool CConfigFile::load(const QString &filename) settings.endGroup(); } - settings.beginGroup("configurations"); - int configurationsCounts = settings.value("size").toInt(); - m_defaultConfiguration = settings.value("default").toInt(); + settings.beginGroup("profiles"); + int profilesCounts = settings.value("size").toInt(); + m_defaultProfile = settings.value("default").toInt(); settings.endGroup(); - m_configurations.resize(configurationsCounts); + m_profiles.resize(profilesCounts); - for(int i = 0; i < configurationsCounts; ++i) + for(int i = 0; i < profilesCounts; ++i) { - CConfiguration &configuration = m_configurations[i]; + CProfile &profile = m_profiles[i]; - settings.beginGroup(QString("configuration_%1").arg(i)); + settings.beginGroup(QString("profile_%1").arg(i)); - configuration.id = settings.value("id").toInt(); - configuration.name = settings.value("name").toString(); - configuration.account = settings.value("account").toString(); - configuration.server = settings.value("server").toString(); - configuration.executable = settings.value("executable").toString(); - configuration.parameters = settings.value("parameters").toString(); - configuration.comments = settings.value("comments").toString(); + profile.id = settings.value("id").toInt(); + profile.name = settings.value("name").toString(); + profile.account = settings.value("account").toString(); + profile.server = settings.value("server").toString(); + profile.executable = settings.value("executable").toString(); + profile.parameters = settings.value("parameters").toString(); + profile.comments = settings.value("comments").toString(); settings.endGroup(); } @@ -162,24 +162,24 @@ bool CConfigFile::save() const settings.endGroup(); } - settings.beginGroup("configurations"); - settings.setValue("size", m_configurations.size()); - settings.setValue("default", m_defaultConfiguration); + settings.beginGroup("profiles"); + settings.setValue("size", m_profiles.size()); + settings.setValue("default", m_defaultProfile); settings.endGroup(); - for(int i = 0; i < m_configurations.size(); ++i) + for(int i = 0; i < m_profiles.size(); ++i) { - const CConfiguration &configuration = m_configurations[i]; + const CProfile &profile = m_profiles[i]; - settings.beginGroup(QString("configuration_%1").arg(i)); + settings.beginGroup(QString("profile_%1").arg(i)); - settings.setValue("id", configuration.id); - settings.setValue("name", configuration.name); - settings.setValue("account", configuration.account); - settings.setValue("server", configuration.server); - settings.setValue("executable", configuration.executable); - settings.setValue("parameters", configuration.parameters); - settings.setValue("comments", configuration.comments); + settings.setValue("id", profile.id); + settings.setValue("name", profile.name); + settings.setValue("account", profile.account); + settings.setValue("server", profile.server); + settings.setValue("executable", profile.executable); + settings.setValue("parameters", profile.parameters); + settings.setValue("comments", profile.comments); settings.endGroup(); } @@ -217,37 +217,37 @@ const CServer& CConfigFile::getServer(const QString &id) const return getServer(); } -int CConfigFile::getConfigurationsCount() const +int CConfigFile::getProfilesCount() const { - return m_configurations.size(); + return m_profiles.size(); } -CConfiguration CConfigFile::getConfiguration(int i) const +CProfile CConfigFile::getProfile(int i) const { - if (i < 0) i = m_defaultConfiguration; + if (i < 0) i = m_defaultProfile; - if (i >= m_configurations.size()) return NoConfiguration; + if (i >= m_profiles.size()) return NoProfile; - return m_configurations.at(i); + return m_profiles.at(i); } -void CConfigFile::setConfiguration(int i, const CConfiguration &configuration) +void CConfigFile::setProfile(int i, const CProfile &profile) { - m_configurations[i] = configuration; + m_profiles[i] = profile; } -int CConfigFile::addConfiguration(const CConfiguration &configuration) +int CConfigFile::addProfile(const CProfile &profile) { - m_configurations.append(configuration); + m_profiles.append(profile); - return m_configurations.size()-1; + return m_profiles.size()-1; } -void CConfigFile::removeConfiguration(int i) +void CConfigFile::removeProfile(int i) { - m_configurations.removeAt(i); + m_profiles.removeAt(i); - // TODO: decalle all configurations and move files + // TODO: decalle all profiles and move files } bool CConfigFile::has64bitsOS() @@ -255,9 +255,9 @@ bool CConfigFile::has64bitsOS() return QSysInfo::currentCpuArchitecture() == "x86_64"; } -int CConfigFile::getDefaultConfiguration() const +int CConfigFile::getDefaultProfile() const { - return m_defaultConfiguration; + return m_defaultProfile; } int CConfigFile::getDefaultServer() const @@ -267,7 +267,7 @@ int CConfigFile::getDefaultServer() const bool CConfigFile::isRyzomInstallerConfigured() const { - return m_configurations.size() > 0; + return m_profiles.size() > 0; } QString CConfigFile::getInstallationDirectory() const @@ -290,12 +290,12 @@ void CConfigFile::setSrcServerDirectory(const QString &directory) m_srcDirectory = directory; } -QString CConfigFile::getConfigurationDirectory() const +QString CConfigFile::getProfileDirectory() const { return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); } -QString CConfigFile::getSrcConfigurationDirectory() const +QString CConfigFile::getSrcProfileDirectory() const { if (QFile::exists(getSrcServerDirectory() + "/client.cfg")) return getSrcServerDirectory(); @@ -425,7 +425,7 @@ bool CConfigFile::isRyzomClientInstalledIn(const QString &directory) const QString CConfigFile::getClientFullPath() const { - QString path = getConfiguration().executable; + QString path = getProfile().executable; if (!path.isEmpty()) return path; @@ -439,13 +439,13 @@ QString CConfigFile::getSrcServerClientBNPFullPath() const CConfigFile::InstallationStep CConfigFile::getNextStep() const { - // get last used configuration - const CConfiguration &configuration = getConfiguration(); + // get last used profile + const CProfile &profile = getProfile(); // get server used by it or default server - CServer server = getServer(configuration.server); + CServer server = getServer(profile.server); - // no or wrong configuration + // no or wrong profile if (server.id.isEmpty()) { // get last used server @@ -542,16 +542,16 @@ CConfigFile::InstallationStep CConfigFile::getNextStep() const } } - // no default configuration - if (configuration.id < 0) + // no default profile + if (profile.id < 0) { - return CreateConfiguration; + return CreateProfile; } - // migration configuration - if (!getSrcServerDirectory().isEmpty() && QFile::exists(getSrcConfigurationDirectory() + "/client.cfg") && !QFile::exists(QString("%1/%2/client.cfg").arg(getConfigurationDirectory()).arg(configuration.name))) + // migration profile + if (!getSrcServerDirectory().isEmpty() && QFile::exists(getSrcProfileDirectory() + "/client.cfg") && !QFile::exists(QString("%1/%2/client.cfg").arg(getProfileDirectory()).arg(profile.name))) { - return CopyConfigurationFiles; + return CopyProfileFiles; } if (!QFile::exists(QStandardPaths::writableLocation(QStandardPaths::DesktopLocation) + "/Ryzom.lnk")) diff --git a/code/ryzom/tools/client/ryzom_installer/src/configfile.h b/code/ryzom/tools/client/ryzom_installer/src/configfile.h index 1b5e0a111..acff20b3d 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/configfile.h +++ b/code/ryzom/tools/client/ryzom_installer/src/configfile.h @@ -40,9 +40,9 @@ struct CServer extern const CServer NoServer; -struct CConfiguration +struct CProfile { - CConfiguration() + CProfile() { id = -1; } @@ -56,7 +56,7 @@ struct CConfiguration QString comments; }; -extern const CConfiguration NoConfiguration; +extern const CProfile NoProfile; /** * Config file management and other stuff related to location of files/directories. @@ -78,9 +78,9 @@ public: DownloadClient, ExtractDownloadedClient, CopyServerFiles, - CopyConfigurationFiles, + CopyProfileFiles, ExtractBnpClient, - CreateConfiguration, + CreateProfile, CreateShortcuts, Done }; @@ -98,14 +98,14 @@ public: const CServer& getServer(int i = -1) const; const CServer& getServer(const QString &id) const; - int getConfigurationsCount() const; - CConfiguration getConfiguration(int i = -1) const; - void setConfiguration(int i, const CConfiguration &configuration); - int addConfiguration(const CConfiguration &configuration); - void removeConfiguration(int i); + int getProfilesCount() const; + CProfile getProfile(int i = -1) const; + void setProfile(int i, const CProfile &profile); + int addProfile(const CProfile &profile); + void removeProfile(int i); int getDefaultServer() const; - int getDefaultConfiguration() const; + int getDefaultProfile() const; bool isRyzomInstallerConfigured() const; @@ -115,8 +115,8 @@ public: QString getSrcServerDirectory() const; void setSrcServerDirectory(const QString &directory); - QString getConfigurationDirectory() const; - QString getSrcConfigurationDirectory() const; + QString getProfileDirectory() const; + QString getSrcProfileDirectory() const; static bool has64bitsOS(); @@ -147,10 +147,10 @@ public: private: int m_defaultServer; - int m_defaultConfiguration; + int m_defaultProfile; QVector m_servers; - QVector m_configurations; + QVector m_profiles; QString m_installationDirectory; QString m_srcDirectory; diff --git a/code/ryzom/tools/client/ryzom_installer/src/mainwindow.cpp b/code/ryzom/tools/client/ryzom_installer/src/mainwindow.cpp index 81f2d925c..1fccb6fcb 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/mainwindow.cpp +++ b/code/ryzom/tools/client/ryzom_installer/src/mainwindow.cpp @@ -92,8 +92,8 @@ void CMainWindow::processNextStep() // default server const CServer &server = config->getServer(); - // default configuration - const CConfiguration &configuration = config->getConfiguration(); + // default profile + const CProfile &configuration = config->getProfile(); switch(CConfigFile::getInstance()->getNextStep()) { @@ -126,9 +126,9 @@ void CMainWindow::processNextStep() m_archive->copyServerFiles(config->getSrcServerDirectory(), config->getInstallationDirectory() + "/" + server.id); break; - case CConfigFile::CopyConfigurationFiles: + case CConfigFile::CopyProfileFiles: displayProgressBar(); - m_archive->copyConfigurationFiles(config->getSrcConfigurationDirectory(), config->getConfigurationDirectory() + "/0"); + m_archive->copyProfileFiles(config->getSrcProfileDirectory(), config->getProfileDirectory() + "/0"); break; case CConfigFile::ExtractBnpClient: @@ -136,7 +136,7 @@ void CMainWindow::processNextStep() m_archive->extract(config->getSrcServerClientBNPFullPath(), config->getInstallationDirectory() + "/" + server.id); break; - case CConfigFile::CreateConfiguration: + case CConfigFile::CreateProfile: displayProgressBar(); break; diff --git a/code/ryzom/tools/client/ryzom_installer/src/configurationsdialog.cpp b/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.cpp similarity index 84% rename from code/ryzom/tools/client/ryzom_installer/src/configurationsdialog.cpp rename to code/ryzom/tools/client/ryzom_installer/src/profilesdialog.cpp index ec7fe079f..279a703b5 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/configurationsdialog.cpp +++ b/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.cpp @@ -15,22 +15,22 @@ // along with this program. If not, see . #include "stdpch.h" -#include "configurationsdialog.h" +#include "profilesdialog.h" #ifdef DEBUG_NEW #define new DEBUG_NEW #endif -CConfigurationsDialog::CConfigurationsDialog():QDialog() +CProfilesDialog::CProfilesDialog():QDialog() { setupUi(this); } -CConfigurationsDialog::~CConfigurationsDialog() +CProfilesDialog::~CProfilesDialog() { } -void CConfigurationsDialog::accept() +void CProfilesDialog::accept() { // TODO: add save code diff --git a/code/ryzom/tools/client/ryzom_installer/src/configurationsdialog.h b/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.h similarity index 78% rename from code/ryzom/tools/client/ryzom_installer/src/configurationsdialog.h rename to code/ryzom/tools/client/ryzom_installer/src/profilesdialog.h index c236b62c7..1ba25c5e0 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/configurationsdialog.h +++ b/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.h @@ -14,18 +14,18 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -#ifndef CONFIGURATIONSDIALOG_H -#define CONFIGURATIONSDIALOG_H +#ifndef PROFILESDIALOG_H +#define PROFILESDIALOG_H -#include "ui_configurations.h" +#include "ui_profiles.h" -class CConfigurationsDialog : public QDialog, public Ui::ConfigurationsDialog +class CProfilesDialog : public QDialog, public Ui::ConfigurationsDialog { Q_OBJECT public: - CConfigurationsDialog(); - virtual ~CConfigurationsDialog(); + CProfilesDialog(); + virtual ~CProfilesDialog(); private slots: void accept(); diff --git a/code/ryzom/tools/client/ryzom_installer/ui/configurations.ui b/code/ryzom/tools/client/ryzom_installer/ui/profiles.ui similarity index 100% rename from code/ryzom/tools/client/ryzom_installer/ui/configurations.ui rename to code/ryzom/tools/client/ryzom_installer/ui/profiles.ui From 022ca9622d44fceeacf8e51d288a55b149d7f41a Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 27 Feb 2016 18:44:09 +0100 Subject: [PATCH 03/32] Changed: Extract Windows and UNIX attributes from 7z headers --- .../client/ryzom_installer/src/archive.cpp | 50 +++++++++---------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/code/ryzom/tools/client/ryzom_installer/src/archive.cpp b/code/ryzom/tools/client/ryzom_installer/src/archive.cpp index bc40c3061..287f559dc 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/archive.cpp +++ b/code/ryzom/tools/client/ryzom_installer/src/archive.cpp @@ -34,21 +34,24 @@ #include -#define FILE_ATTRIBUTE_READONLY 1 -#define FILE_ATTRIBUTE_HIDDEN 2 -#define FILE_ATTRIBUTE_SYSTEM 4 -#define FILE_ATTRIBUTE_DIRECTORY 16 -#define FILE_ATTRIBUTE_ARCHIVE 32 -#define FILE_ATTRIBUTE_DEVICE 64 -#define FILE_ATTRIBUTE_NORMAL 128 -#define FILE_ATTRIBUTE_TEMPORARY 256 -#define FILE_ATTRIBUTE_SPARSE_FILE 512 -#define FILE_ATTRIBUTE_REPARSE_POINT 1024 -#define FILE_ATTRIBUTE_COMPRESSED 2048 +#define FILE_ATTRIBUTE_READONLY 0x1 +#define FILE_ATTRIBUTE_HIDDEN 0x2 +#define FILE_ATTRIBUTE_SYSTEM 0x4 +#define FILE_ATTRIBUTE_DIRECTORY 0x10 +#define FILE_ATTRIBUTE_ARCHIVE 0x20 +#define FILE_ATTRIBUTE_DEVICE 0x40 +#define FILE_ATTRIBUTE_NORMAL 0x80 +#define FILE_ATTRIBUTE_TEMPORARY 0x100 +#define FILE_ATTRIBUTE_SPARSE_FILE 0x200 +#define FILE_ATTRIBUTE_REPARSE_POINT 0x400 +#define FILE_ATTRIBUTE_COMPRESSED 0x800 #define FILE_ATTRIBUTE_OFFLINE 0x1000 #define FILE_ATTRIBUTE_ENCRYPTED 0x4000 #define FILE_ATTRIBUTE_UNIX_EXTENSION 0x8000 /* trick for Unix */ +#define FILE_ATTRIBUTE_WINDOWS 0x5fff +#define FILE_ATTRIBUTE_UNIX 0xffff0000 + bool Set7zFileAttrib(const QString &filename, uint32 fileAttributes) { bool attrReadOnly = (fileAttributes & FILE_ATTRIBUTE_READONLY != 0); @@ -63,23 +66,18 @@ bool Set7zFileAttrib(const QString &filename, uint32 fileAttributes) bool attrReparsePoint = (fileAttributes & FILE_ATTRIBUTE_REPARSE_POINT != 0); bool attrCompressed = (fileAttributes & FILE_ATTRIBUTE_COMPRESSED != 0); bool attrOffline = (fileAttributes & FILE_ATTRIBUTE_OFFLINE != 0); - bool attrEncryoted = (fileAttributes & FILE_ATTRIBUTE_ENCRYPTED != 0); + bool attrEncrypted = (fileAttributes & FILE_ATTRIBUTE_ENCRYPTED != 0); bool attrUnix = (fileAttributes & FILE_ATTRIBUTE_UNIX_EXTENSION != 0); + uint32 unixAttributes = (fileAttributes & FILE_ATTRIBUTE_UNIX) >> 16; + uint32 windowsAttributes = fileAttributes & FILE_ATTRIBUTE_WINDOWS; + qDebug() << "attribs" << QByteArray::fromRawData((const char*)&fileAttributes, 4).toHex(); #ifdef Q_OS_WIN - SetFileAttributesW((wchar_t*)filename.utf16(), fileAttributes); - -// QFile::set - -// QFileDevice::Permissions:: - - // attribs "2080ed81" -// QFile::setPermissions(destPath, QFileDevice::Permissions); - + SetFileAttributesW((wchar_t*)filename.utf16(), windowsAttributes); #else - const char * name = filename.toUtf8().constData(); + const char *name = filename.toUtf8().constData(); struct stat stat_info; if (lstat(name, &stat_info)!=0) @@ -88,9 +86,9 @@ bool Set7zFileAttrib(const QString &filename, uint32 fileAttributes) return false; } - if (fileAttributes & FILE_ATTRIBUTE_UNIX_EXTENSION) + if (attrUnix) { - stat_info.st_mode = fileAttributes >> 16; + stat_info.st_mode = unixAttributes; if (S_ISLNK(stat_info.st_mode)) { @@ -103,14 +101,14 @@ bool Set7zFileAttrib(const QString &filename, uint32 fileAttributes) else if (S_ISREG(stat_info.st_mode)) { nlwarning("##DBG chmod-2(%s,%o)", (const char *)name, (unsigned)stat_info.st_mode & gbl_umask.mask); - chmod(name,stat_info.st_mode & gbl_umask.mask); + chmod(name, stat_info.st_mode & gbl_umask.mask); } else if (S_ISDIR(stat_info.st_mode)) { // user/7za must be able to create files in this directory stat_info.st_mode |= (S_IRUSR | S_IWUSR | S_IXUSR); nlwarning("##DBG chmod-3(%s,%o)", (const char *)name, (unsigned)stat_info.st_mode & gbl_umask.mask); - chmod(name,stat_info.st_mode & gbl_umask.mask); + chmod(name, stat_info.st_mode & gbl_umask.mask); } } else if (!S_ISLNK(stat_info.st_mode)) From 64841ed85a090fdd16d39ca7d62fee6295033513 Mon Sep 17 00:00:00 2001 From: kervala Date: Tue, 1 Mar 2016 13:48:51 +0100 Subject: [PATCH 04/32] Changed: Renamed configurations to profiles --- .../client/ryzom_installer/src/mainwindow.cpp | 16 +++++++++--- .../client/ryzom_installer/src/mainwindow.h | 1 + .../ryzom_installer/src/profilesdialog.h | 2 +- .../client/ryzom_installer/ui/mainwindow.ui | 14 +++++----- .../client/ryzom_installer/ui/profiles.ui | 26 +++++++++---------- 5 files changed, 34 insertions(+), 25 deletions(-) diff --git a/code/ryzom/tools/client/ryzom_installer/src/mainwindow.cpp b/code/ryzom/tools/client/ryzom_installer/src/mainwindow.cpp index 1fccb6fcb..d316bc6c9 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/mainwindow.cpp +++ b/code/ryzom/tools/client/ryzom_installer/src/mainwindow.cpp @@ -19,6 +19,7 @@ #include "downloader.h" #include "archive.h" #include "wizarddialog.h" +#include "profilesdialog.h" #include "configfile.h" #include "config.h" @@ -67,6 +68,8 @@ CMainWindow::CMainWindow():QMainWindow(), m_archive(NULL), m_statusLabel(NULL) connect(m_archive, SIGNAL(extractSuccess(qint64)), SLOT(onExtractSuccess(qint64))); connect(m_archive, SIGNAL(extractFail(QString)), SLOT(onExtractFail(QString))); + connect(actionProfiles, SIGNAL(triggered()), SLOT(onProfiles())); + connect(actionAboutQt, SIGNAL(triggered()), SLOT(onAboutQt())); connect(actionAbout, SIGNAL(triggered()), SLOT(onAbout())); @@ -75,10 +78,6 @@ CMainWindow::CMainWindow():QMainWindow(), m_archive(NULL), m_statusLabel(NULL) statusBar()->addWidget(m_statusLabel); // setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); - -// processNextStep(); - - m_archive->extract("d:/Ryzom/bonjourx.7z", ""); } CMainWindow::~CMainWindow() @@ -175,6 +174,8 @@ void CMainWindow::showEvent(QShowEvent *e) #endif e->accept(); + + processNextStep(); } void CMainWindow::closeEvent(QCloseEvent *e) @@ -208,6 +209,13 @@ void CMainWindow::onStopClicked() } } +void CMainWindow::onProfiles() +{ + CProfilesDialog dialog; + + dialog.exec(); +} + void CMainWindow::onAbout() { QString br("
"); diff --git a/code/ryzom/tools/client/ryzom_installer/src/mainwindow.h b/code/ryzom/tools/client/ryzom_installer/src/mainwindow.h index bf14b8b4d..7e03f9a80 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/mainwindow.h +++ b/code/ryzom/tools/client/ryzom_installer/src/mainwindow.h @@ -41,6 +41,7 @@ public slots: void onResumeClicked(); void onStopClicked(); + void onProfiles(); void onAbout(); void onAboutQt(); diff --git a/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.h b/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.h index 1ba25c5e0..5da0df89b 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.h +++ b/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.h @@ -19,7 +19,7 @@ #include "ui_profiles.h" -class CProfilesDialog : public QDialog, public Ui::ConfigurationsDialog +class CProfilesDialog : public QDialog, public Ui::ProfilesDialog { Q_OBJECT diff --git a/code/ryzom/tools/client/ryzom_installer/ui/mainwindow.ui b/code/ryzom/tools/client/ryzom_installer/ui/mainwindow.ui index ec7e52ad7..ee9bd7b0e 100644 --- a/code/ryzom/tools/client/ryzom_installer/ui/mainwindow.ui +++ b/code/ryzom/tools/client/ryzom_installer/ui/mainwindow.ui @@ -159,10 +159,10 @@ p, li { white-space: pre-wrap; } &Settings - - + + - + @@ -189,17 +189,17 @@ p, li { white-space: pre-wrap; } About... - + - &Configurations + &Profiles - + &Directories - + &Quit diff --git a/code/ryzom/tools/client/ryzom_installer/ui/profiles.ui b/code/ryzom/tools/client/ryzom_installer/ui/profiles.ui index 82cd5f9e0..8a5f40df8 100644 --- a/code/ryzom/tools/client/ryzom_installer/ui/profiles.ui +++ b/code/ryzom/tools/client/ryzom_installer/ui/profiles.ui @@ -1,7 +1,7 @@ - ConfigurationsDialog - + ProfilesDialog + 0 @@ -17,16 +17,16 @@ - + - + - List of configurations: + List of profiles: - + 0 @@ -45,7 +45,7 @@ - + @@ -67,14 +67,14 @@ - + - Configuration: + Profile: - + 0 @@ -254,7 +254,7 @@ - configurationsListView + profilesListView addButton deleteButton accountEdit @@ -272,7 +272,7 @@ buttonBox accepted() - ConfigurationsDialog + ProfilesDialog accept() @@ -288,7 +288,7 @@ buttonBox rejected() - ConfigurationsDialog + ProfilesDialog reject() From 6af7a93174a33ae4ee0d3468f45db61cf13f4bfe Mon Sep 17 00:00:00 2001 From: kervala Date: Tue, 1 Mar 2016 14:00:06 +0100 Subject: [PATCH 05/32] Changed: Slots for buttons --- .../client/ryzom_installer/src/profilesdialog.cpp | 11 +++++++++++ .../tools/client/ryzom_installer/src/profilesdialog.h | 3 +++ 2 files changed, 14 insertions(+) diff --git a/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.cpp b/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.cpp index 279a703b5..bdc97ab7f 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.cpp +++ b/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.cpp @@ -24,6 +24,9 @@ CProfilesDialog::CProfilesDialog():QDialog() { setupUi(this); + + connect(addButton, SIGNAL(clicked()), SLOT(onAddProfile)); + connect(deleteButton, SIGNAL(clicked()), SLOT(onDeleteProfile)); } CProfilesDialog::~CProfilesDialog() @@ -36,3 +39,11 @@ void CProfilesDialog::accept() QDialog::accept(); } + +void CProfilesDialog::onAddProfile() +{ +} + +void CProfilesDialog::onDeleteProfile() +{ +} diff --git a/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.h b/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.h index 5da0df89b..2dd8a608d 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.h +++ b/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.h @@ -29,6 +29,9 @@ public: private slots: void accept(); + + void onAddProfile(); + void onDeleteProfile(); }; #endif From 78587d3d47dbbb2625e1825ea076a8969785cdaa Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 5 Mar 2016 12:33:19 +0100 Subject: [PATCH 06/32] Changed: Use "arguments" instead of "parameters" --- code/ryzom/tools/client/ryzom_installer/src/configfile.cpp | 4 ++-- code/ryzom/tools/client/ryzom_installer/src/configfile.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/code/ryzom/tools/client/ryzom_installer/src/configfile.cpp b/code/ryzom/tools/client/ryzom_installer/src/configfile.cpp index 0f73bd96e..1be9cdfe7 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/configfile.cpp +++ b/code/ryzom/tools/client/ryzom_installer/src/configfile.cpp @@ -110,7 +110,7 @@ bool CConfigFile::load(const QString &filename) profile.account = settings.value("account").toString(); profile.server = settings.value("server").toString(); profile.executable = settings.value("executable").toString(); - profile.parameters = settings.value("parameters").toString(); + profile.arguments = settings.value("arguments").toString(); profile.comments = settings.value("comments").toString(); settings.endGroup(); @@ -178,7 +178,7 @@ bool CConfigFile::save() const settings.setValue("account", profile.account); settings.setValue("server", profile.server); settings.setValue("executable", profile.executable); - settings.setValue("parameters", profile.parameters); + settings.setValue("arguments", profile.arguments); settings.setValue("comments", profile.comments); settings.endGroup(); diff --git a/code/ryzom/tools/client/ryzom_installer/src/configfile.h b/code/ryzom/tools/client/ryzom_installer/src/configfile.h index acff20b3d..649d6404e 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/configfile.h +++ b/code/ryzom/tools/client/ryzom_installer/src/configfile.h @@ -52,7 +52,7 @@ struct CProfile QString name; QString server; QString executable; - QString parameters; + QString arguments; QString comments; }; From 960b3bc5e3daaeb9e6293ad2af945d4983263a1c Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 5 Mar 2016 12:34:02 +0100 Subject: [PATCH 07/32] Changed: Create a typedef for lists of profiles and servers --- .../tools/client/ryzom_installer/src/configfile.h | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/code/ryzom/tools/client/ryzom_installer/src/configfile.h b/code/ryzom/tools/client/ryzom_installer/src/configfile.h index 649d6404e..5b2a15f6f 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/configfile.h +++ b/code/ryzom/tools/client/ryzom_installer/src/configfile.h @@ -40,6 +40,8 @@ struct CServer extern const CServer NoServer; +typedef QVector CServers; + struct CProfile { CProfile() @@ -58,6 +60,8 @@ struct CProfile extern const CProfile NoProfile; +typedef QVector CProfiles; + /** * Config file management and other stuff related to location of files/directories. * @@ -94,10 +98,16 @@ public: static CConfigFile* getInstance(); + CServers getServers() const { return m_servers; } + void setServers(const CServers &servers) { m_servers = servers; } + int getServersCount() const; const CServer& getServer(int i = -1) const; const CServer& getServer(const QString &id) const; + CProfiles getProfiles() const { return m_profiles; } + void setProfiles(const CProfiles &profiles) { m_profiles = profiles; } + int getProfilesCount() const; CProfile getProfile(int i = -1) const; void setProfile(int i, const CProfile &profile); @@ -149,8 +159,8 @@ private: int m_defaultServer; int m_defaultProfile; - QVector m_servers; - QVector m_profiles; + CServers m_servers; + CProfiles m_profiles; QString m_installationDirectory; QString m_srcDirectory; From 008794031a01e66ef5ad257d292e20a6543d6cc1 Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 5 Mar 2016 12:35:32 +0100 Subject: [PATCH 08/32] Added: Profiles model class --- .../ryzom_installer/src/profilesmodel.cpp | 48 +++++++++++++++++++ .../ryzom_installer/src/profilesmodel.h | 26 ++++++++++ 2 files changed, 74 insertions(+) create mode 100644 code/ryzom/tools/client/ryzom_installer/src/profilesmodel.cpp create mode 100644 code/ryzom/tools/client/ryzom_installer/src/profilesmodel.h diff --git a/code/ryzom/tools/client/ryzom_installer/src/profilesmodel.cpp b/code/ryzom/tools/client/ryzom_installer/src/profilesmodel.cpp new file mode 100644 index 000000000..c64c9fb0f --- /dev/null +++ b/code/ryzom/tools/client/ryzom_installer/src/profilesmodel.cpp @@ -0,0 +1,48 @@ +#include "profilesmodel.h" + +CProfilesModel::CProfilesModel(QObject *parent):QAbstractListModel(parent) +{ + m_profiles = CConfigFile::getInstance()->getProfiles(); +} + +CProfilesModel::CProfilesModel(const CProfiles &profiles, QObject *parent):QAbstractListModel(parent), m_profiles(profiles) +{ +} + +CProfilesModel::~CProfilesModel() +{ +} + +int CProfilesModel::rowCount(const QModelIndex &parent) const +{ + return m_profiles.size(); +} + +QVariant CProfilesModel::data(const QModelIndex &index, int role) const +{ + if (role != Qt::DisplayRole) return QVariant(); + + const CProfile &profile = m_profiles.at(index.row()); + + return QString("%1 (%2)").arg(profile.name).arg(profile.id); +} + +bool CProfilesModel::removeRows(int row, int count, const QModelIndex &parent) +{ + if (row < 0) return false; + + beginRemoveRows(parent, row, row + count - 1); + + m_profiles.removeAt(row); + + endRemoveRows(); + + return true; +} + +bool CProfilesModel::save() const +{ + CConfigFile::getInstance()->setProfiles(m_profiles); + + return true; +} diff --git a/code/ryzom/tools/client/ryzom_installer/src/profilesmodel.h b/code/ryzom/tools/client/ryzom_installer/src/profilesmodel.h new file mode 100644 index 000000000..78d0e61bf --- /dev/null +++ b/code/ryzom/tools/client/ryzom_installer/src/profilesmodel.h @@ -0,0 +1,26 @@ +#ifndef PROFILESMODEL_H +#define PROFILESMODEL_H + +#include "configfile.h" + +class CProfilesModel : public QAbstractListModel +{ +public: + CProfilesModel(QObject *parent); + CProfilesModel(const CProfiles &profiles, QObject *parent); + virtual ~CProfilesModel(); + + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + + bool removeRows(int row, int count, const QModelIndex & parent = QModelIndex()); + + CProfiles& getProfiles() { return m_profiles; } + + bool save() const; + +private: + CProfiles m_profiles; +}; + +#endif From 02ab16f83f04ed0e90b2ab9e44b84d5571795264 Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 5 Mar 2016 12:38:28 +0100 Subject: [PATCH 09/32] Changed: Profiles dialog code --- .../ryzom_installer/src/profilesdialog.cpp | 120 +++++++++++++++++- .../ryzom_installer/src/profilesdialog.h | 15 +++ .../client/ryzom_installer/ui/profiles.ui | 2 +- 3 files changed, 132 insertions(+), 5 deletions(-) diff --git a/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.cpp b/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.cpp index bdc97ab7f..a37180fb7 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.cpp +++ b/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.cpp @@ -16,17 +16,32 @@ #include "stdpch.h" #include "profilesdialog.h" +#include "profilesmodel.h" #ifdef DEBUG_NEW #define new DEBUG_NEW #endif -CProfilesDialog::CProfilesDialog():QDialog() +CProfilesDialog::CProfilesDialog():QDialog(), m_currentProfileIndex(-1) { setupUi(this); - connect(addButton, SIGNAL(clicked()), SLOT(onAddProfile)); - connect(deleteButton, SIGNAL(clicked()), SLOT(onDeleteProfile)); + connect(addButton, SIGNAL(clicked()), SLOT(onAddProfile())); + connect(deleteButton, SIGNAL(clicked()), SLOT(onDeleteProfile())); + connect(profilesListView, SIGNAL(clicked(QModelIndex)), SLOT(onProfileClicked(QModelIndex))); + connect(executableBrowseButton, SIGNAL(clicked()), SLOT(onExecutableBrowseClicked())); + + m_model = new CProfilesModel(this); + + profilesListView->setModel(m_model); + + QStringList servers; + servers << "Atys"; + servers << "Yubo"; + + QStringListModel *serversModel = new QStringListModel(servers, this); + + serverComboBox->setModel(serversModel); } CProfilesDialog::~CProfilesDialog() @@ -35,7 +50,7 @@ CProfilesDialog::~CProfilesDialog() void CProfilesDialog::accept() { - // TODO: add save code + m_model->save(); QDialog::accept(); } @@ -46,4 +61,101 @@ void CProfilesDialog::onAddProfile() void CProfilesDialog::onDeleteProfile() { + QMessageBox::StandardButton res = QMessageBox::question(this, tr("Confirmation"), tr("You're going to delete a profile, files won't be deleted and you'll have to do that manually.\nAre you sure to delete this profile?")); + + if (res != QMessageBox::Yes) return; + + QModelIndex index = profilesListView->currentIndex(); + + deleteProfile(index.row()); +} + +void CProfilesDialog::onProfileClicked(const QModelIndex &index) +{ + qDebug() << "clicked on" << index; + + displayProfile(index.row()); +} + +void CProfilesDialog::displayProfile(int index) +{ + if (index < 0) return; + + saveProfile(m_currentProfileIndex); + + const CProfile &profile = m_model->getProfiles()[index]; + + profileIdLabel->setText(QString::number(profile.id)); + accountEdit->setText(profile.account); + nameEdit->setText(profile.name); + serverComboBox->setCurrentIndex(0); + executablePathLabel->setText(QFileInfo(profile.executable).fileName()); + argumentsEdit->setText(profile.arguments); + commentsEdit->setPlainText(profile.comments); + directoryPathLabel->setText(CConfigFile::getInstance()->getProfileDirectory()); + + m_currentProfileIndex = index; +} + +void CProfilesDialog::saveProfile(int index) +{ + if (index < 0) return; + + CProfile &profile = m_model->getProfiles()[index]; + + profileIdLabel->setText(QString::number(profile.id)); + profile.account = accountEdit->text(); + profile.name = nameEdit->text(); +// serverComboBox->setCurrentIndex(0); + profile.arguments = argumentsEdit->text(); + profile.comments = commentsEdit->toPlainText(); +} + +void CProfilesDialog::deleteProfile(int index) +{ + if (index < 0) return; + + m_model->removeRow(index); +} + +void CProfilesDialog::addProfile() +{ + // TODO: browse all folders in AppData/Roaming/Ryzom +} + +void CProfilesDialog::onExecutableBrowseClicked() +{ + if (m_currentProfileIndex < 0) return; + + CProfile &profile = m_model->getProfiles()[m_currentProfileIndex]; + + QString file = QFileDialog::getOpenFileName(this, tr("Please choose Ryzom client executable to launch"), profile.executable, tr("Executables (*.exe)")); + + if (file.isEmpty()) return; + + profile.executable = file; + + executablePathLabel->setText(QFileInfo(profile.executable).fileName()); + + QProcess process; + process.setProcessChannelMode(QProcess::MergedChannels); + process.start(profile.executable, QStringList() << "--version", QIODevice::ReadWrite); + + if (!process.waitForStarted()) return; + + QByteArray data; + + while (process.waitForReadyRead()) data.append(process.readAll()); + + QString versionString = QString::fromUtf8(data); + + QRegExp reg("([A-Za-z0-1_.]+) ((DEV|FV) ([0-9.]+))"); + + if (reg.indexIn(versionString) > -1) + { + executableVersionLabel->setText(reg.cap(2)); + } + + // ryzom_client_dev_d.exe DEV 0.12.0.7331 (built on 2016-02-25 22:16:50) + // Copyright (C) 2004-2016 Winchgate and The Ryzom Core Community } diff --git a/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.h b/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.h index 2dd8a608d..ff73d88b9 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.h +++ b/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.h @@ -19,6 +19,8 @@ #include "ui_profiles.h" +class CProfilesModel; + class CProfilesDialog : public QDialog, public Ui::ProfilesDialog { Q_OBJECT @@ -32,6 +34,19 @@ private slots: void onAddProfile(); void onDeleteProfile(); + void onProfileClicked(const QModelIndex &index); + + void displayProfile(int index); + void saveProfile(int index); + void deleteProfile(int index); + void addProfile(); + + void onExecutableBrowseClicked(); + +private: + CProfilesModel *m_model; + + int m_currentProfileIndex; }; #endif diff --git a/code/ryzom/tools/client/ryzom_installer/ui/profiles.ui b/code/ryzom/tools/client/ryzom_installer/ui/profiles.ui index 8a5f40df8..3cf0ccf50 100644 --- a/code/ryzom/tools/client/ryzom_installer/ui/profiles.ui +++ b/code/ryzom/tools/client/ryzom_installer/ui/profiles.ui @@ -224,7 +224,7 @@ - + FV 3.0.0 From a4e564bae5b4e3110bf352c3fd0e633d0966f2f4 Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 12 Mar 2016 21:53:20 +0100 Subject: [PATCH 10/32] Added: ServersModel to manage servers list --- .../ryzom_installer/src/serversmodel.cpp | 82 +++++++++++++++++++ .../client/ryzom_installer/src/serversmodel.h | 51 ++++++++++++ 2 files changed, 133 insertions(+) create mode 100644 code/ryzom/tools/client/ryzom_installer/src/serversmodel.cpp create mode 100644 code/ryzom/tools/client/ryzom_installer/src/serversmodel.h diff --git a/code/ryzom/tools/client/ryzom_installer/src/serversmodel.cpp b/code/ryzom/tools/client/ryzom_installer/src/serversmodel.cpp new file mode 100644 index 000000000..7cda521de --- /dev/null +++ b/code/ryzom/tools/client/ryzom_installer/src/serversmodel.cpp @@ -0,0 +1,82 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include "stdpch.h" +#include "serversmodel.h" + +CServersModel::CServersModel(QObject *parent):QAbstractListModel(parent) +{ + m_servers = CConfigFile::getInstance()->getServers(); +} + +CServersModel::CServersModel(const CServers &servers, QObject *parent):QAbstractListModel(parent), m_servers(servers) +{ +} + +CServersModel::~CServersModel() +{ +} + +int CServersModel::rowCount(const QModelIndex &parent) const +{ + return m_servers.size(); +} + +QVariant CServersModel::data(const QModelIndex &index, int role) const +{ + if (role != Qt::DisplayRole) return QVariant(); + + const CServer &server = m_servers.at(index.row()); + + return server.name; +} + +bool CServersModel::removeRows(int row, int count, const QModelIndex &parent) +{ + if (row < 0) return false; + + beginRemoveRows(parent, row, row + count - 1); + + m_servers.removeAt(row); + + endRemoveRows(); + + return true; +} + +bool CServersModel::save() const +{ + CConfigFile::getInstance()->setServers(m_servers); + + return true; +} + +int CServersModel::getIndexFromServerID(const QString &serverId) const +{ + for(int i = 0; i < m_servers.size(); ++i) + { + if (m_servers[i].id == serverId) return i; + } + + return -1; +} + +QString CServersModel::getServerIDFromIndex(int index) const +{ + if (index < 0 || index >= m_servers.size()) return ""; + + return m_servers[index].id; +} diff --git a/code/ryzom/tools/client/ryzom_installer/src/serversmodel.h b/code/ryzom/tools/client/ryzom_installer/src/serversmodel.h new file mode 100644 index 000000000..27f5daed3 --- /dev/null +++ b/code/ryzom/tools/client/ryzom_installer/src/serversmodel.h @@ -0,0 +1,51 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#ifndef SERVERSMODEL_H +#define SERVERSMODEL_H + +#include "configfile.h" + +/** + * Servers model + * + * \author Cedric 'Kervala' OCHS + * \date 2016 + */ +class CServersModel : public QAbstractListModel +{ +public: + CServersModel(QObject *parent); + CServersModel(const CServers &servers, QObject *parent); + virtual ~CServersModel(); + + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + + bool removeRows(int row, int count, const QModelIndex & parent = QModelIndex()); + + CServers& getServers() { return m_servers; } + + bool save() const; + + int getIndexFromServerID(const QString &serverId) const; + QString getServerIDFromIndex(int index) const; + +private: + CServers m_servers; +}; + +#endif From 245b32782f4a604f32448c28606f81be22c78269 Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 12 Mar 2016 21:53:51 +0100 Subject: [PATCH 11/32] Changed: Use checkboxes for shortcuts --- .../client/ryzom_installer/ui/profiles.ui | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/code/ryzom/tools/client/ryzom_installer/ui/profiles.ui b/code/ryzom/tools/client/ryzom_installer/ui/profiles.ui index 3cf0ccf50..1e3e42768 100644 --- a/code/ryzom/tools/client/ryzom_installer/ui/profiles.ui +++ b/code/ryzom/tools/client/ryzom_installer/ui/profiles.ui @@ -6,8 +6,8 @@ 0 0 - 501 - 351 + 583 + 368 @@ -146,6 +146,20 @@ + + + + Client version: + + + + + + + FV 3.0.0 + + + @@ -206,16 +220,16 @@ - + - + Desktop - + Start Menu @@ -223,20 +237,6 @@ - - - - FV 3.0.0 - - - - - - - Client version: - - - @@ -264,8 +264,8 @@ argumentsEdit commentsEdit directoryButton - shortcutsDesktopButton - shortcutsMenuButton + desktopShortcutCheckBox + menuShortcutCheckBox From 4c8d7884ea746d61c5f7050bd4c8e6215b608b8c Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 12 Mar 2016 21:54:44 +0100 Subject: [PATCH 12/32] Changed: Read/write shortcuts --- code/ryzom/tools/client/ryzom_installer/src/configfile.cpp | 4 ++++ code/ryzom/tools/client/ryzom_installer/src/configfile.h | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/code/ryzom/tools/client/ryzom_installer/src/configfile.cpp b/code/ryzom/tools/client/ryzom_installer/src/configfile.cpp index 1be9cdfe7..b3b41cfde 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/configfile.cpp +++ b/code/ryzom/tools/client/ryzom_installer/src/configfile.cpp @@ -112,6 +112,8 @@ bool CConfigFile::load(const QString &filename) profile.executable = settings.value("executable").toString(); profile.arguments = settings.value("arguments").toString(); profile.comments = settings.value("comments").toString(); + profile.desktopShortcut = settings.value("desktop_shortcut").toBool(); + profile.menuShortcut = settings.value("menu_shortcut").toBool(); settings.endGroup(); } @@ -180,6 +182,8 @@ bool CConfigFile::save() const settings.setValue("executable", profile.executable); settings.setValue("arguments", profile.arguments); settings.setValue("comments", profile.comments); + settings.setValue("desktop_shortcut", profile.desktopShortcut); + settings.setValue("menu_shortcut", profile.menuShortcut); settings.endGroup(); } diff --git a/code/ryzom/tools/client/ryzom_installer/src/configfile.h b/code/ryzom/tools/client/ryzom_installer/src/configfile.h index 5b2a15f6f..32bfb99c6 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/configfile.h +++ b/code/ryzom/tools/client/ryzom_installer/src/configfile.h @@ -47,6 +47,8 @@ struct CProfile CProfile() { id = -1; + desktopShortcut = false; + menuShortcut = false; } int id; @@ -56,6 +58,8 @@ struct CProfile QString executable; QString arguments; QString comments; + bool desktopShortcut; + bool menuShortcut; }; extern const CProfile NoProfile; From c559bf1288946c9255704542bbcf9f6d0cf07f31 Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 12 Mar 2016 21:56:23 +0100 Subject: [PATCH 13/32] Changed: Minor changes --- .../ryzom/tools/client/ryzom_installer/src/profilesdialog.h | 6 ++++++ .../tools/client/ryzom_installer/src/profilesmodel.cpp | 2 +- code/ryzom/tools/client/ryzom_installer/src/profilesmodel.h | 6 ++++++ .../ryzom/tools/client/ryzom_installer/src/settingsdialog.h | 6 ++++++ 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.h b/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.h index ff73d88b9..d4425d062 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.h +++ b/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.h @@ -21,6 +21,12 @@ class CProfilesModel; +/** + * Dialog displayed when editing existing profiles. + * + * \author Cedric 'Kervala' OCHS + * \date 2016 + */ class CProfilesDialog : public QDialog, public Ui::ProfilesDialog { Q_OBJECT diff --git a/code/ryzom/tools/client/ryzom_installer/src/profilesmodel.cpp b/code/ryzom/tools/client/ryzom_installer/src/profilesmodel.cpp index c64c9fb0f..13c9e00da 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/profilesmodel.cpp +++ b/code/ryzom/tools/client/ryzom_installer/src/profilesmodel.cpp @@ -24,7 +24,7 @@ QVariant CProfilesModel::data(const QModelIndex &index, int role) const const CProfile &profile = m_profiles.at(index.row()); - return QString("%1 (%2)").arg(profile.name).arg(profile.id); + return QString("%1 (#%2)").arg(profile.name).arg(profile.id); } bool CProfilesModel::removeRows(int row, int count, const QModelIndex &parent) diff --git a/code/ryzom/tools/client/ryzom_installer/src/profilesmodel.h b/code/ryzom/tools/client/ryzom_installer/src/profilesmodel.h index 78d0e61bf..283be0c24 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/profilesmodel.h +++ b/code/ryzom/tools/client/ryzom_installer/src/profilesmodel.h @@ -3,6 +3,12 @@ #include "configfile.h" +/** + * Profiles model + * + * \author Cedric 'Kervala' OCHS + * \date 2016 + */ class CProfilesModel : public QAbstractListModel { public: diff --git a/code/ryzom/tools/client/ryzom_installer/src/settingsdialog.h b/code/ryzom/tools/client/ryzom_installer/src/settingsdialog.h index 1dd33f88f..f8e3144f7 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/settingsdialog.h +++ b/code/ryzom/tools/client/ryzom_installer/src/settingsdialog.h @@ -19,6 +19,12 @@ #include "ui_settings.h" +/** + * Settings dialog + * + * \author Cedric 'Kervala' OCHS + * \date 2016 + */ class CSettingsDialog : public QDialog, public Ui::SettingsDialog { Q_OBJECT From ebe627cce65f9a5173004a9797e75d0ebe0955b9 Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 12 Mar 2016 21:57:24 +0100 Subject: [PATCH 14/32] Changed: Methods to convert index from/to profile ID --- .../ryzom_installer/src/profilesmodel.cpp | 17 +++++++++++++++++ .../client/ryzom_installer/src/profilesmodel.h | 3 +++ 2 files changed, 20 insertions(+) diff --git a/code/ryzom/tools/client/ryzom_installer/src/profilesmodel.cpp b/code/ryzom/tools/client/ryzom_installer/src/profilesmodel.cpp index 13c9e00da..1977b423b 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/profilesmodel.cpp +++ b/code/ryzom/tools/client/ryzom_installer/src/profilesmodel.cpp @@ -46,3 +46,20 @@ bool CProfilesModel::save() const return true; } + +int CProfilesModel::getIndexFromProfileID(int profileId) const +{ + for(int i = 0; i < m_profiles.size(); ++i) + { + if (m_profiles[i].id == profileId) return i; + } + + return -1; +} + +int CProfilesModel::getProfileIDFromIndex(int index) const +{ + if (index < 0 || index >= m_profiles.size()) return -1; + + return m_profiles[index].id; +} diff --git a/code/ryzom/tools/client/ryzom_installer/src/profilesmodel.h b/code/ryzom/tools/client/ryzom_installer/src/profilesmodel.h index 283be0c24..ea02f618c 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/profilesmodel.h +++ b/code/ryzom/tools/client/ryzom_installer/src/profilesmodel.h @@ -25,6 +25,9 @@ public: bool save() const; + int getIndexFromProfileID(int profileId) const; + int getProfileIDFromIndex(int index) const; + private: CProfiles m_profiles; }; From b4c0c8f9212467fde37635e9ae2f5bf9aa6b92bc Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 12 Mar 2016 21:58:05 +0100 Subject: [PATCH 15/32] Changed: Use CServersModel --- .../client/ryzom_installer/src/profilesdialog.cpp | 12 ++++++------ .../client/ryzom_installer/src/profilesdialog.h | 2 ++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.cpp b/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.cpp index a37180fb7..9632c69fe 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.cpp +++ b/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.cpp @@ -17,6 +17,7 @@ #include "stdpch.h" #include "profilesdialog.h" #include "profilesmodel.h" +#include "serversmodel.h" #ifdef DEBUG_NEW #define new DEBUG_NEW @@ -32,16 +33,15 @@ CProfilesDialog::CProfilesDialog():QDialog(), m_currentProfileIndex(-1) connect(executableBrowseButton, SIGNAL(clicked()), SLOT(onExecutableBrowseClicked())); m_model = new CProfilesModel(this); + m_serversModel = new CServersModel(this); profilesListView->setModel(m_model); + serverComboBox->setModel(m_serversModel); - QStringList servers; - servers << "Atys"; - servers << "Yubo"; + int index = m_model->getIndexFromProfileID(CConfigFile::getInstance()->getDefaultProfile()); - QStringListModel *serversModel = new QStringListModel(servers, this); - - serverComboBox->setModel(serversModel); + profilesListView->setCurrentIndex(m_model->index(index, 0)); + displayProfile(index); } CProfilesDialog::~CProfilesDialog() diff --git a/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.h b/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.h index d4425d062..78fe14bbc 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.h +++ b/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.h @@ -20,6 +20,7 @@ #include "ui_profiles.h" class CProfilesModel; +class CServersModel; /** * Dialog displayed when editing existing profiles. @@ -51,6 +52,7 @@ private slots: private: CProfilesModel *m_model; + CServersModel *m_serversModel; int m_currentProfileIndex; }; From 1f999fc420fd7260792c44ca952b68ff080253f9 Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 12 Mar 2016 21:58:53 +0100 Subject: [PATCH 16/32] Changed: Read version from client and display it --- .../ryzom_installer/src/profilesdialog.cpp | 75 +++++++++++++------ .../ryzom_installer/src/profilesdialog.h | 2 + 2 files changed, 56 insertions(+), 21 deletions(-) diff --git a/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.cpp b/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.cpp index 9632c69fe..5134902fc 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.cpp +++ b/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.cpp @@ -93,6 +93,10 @@ void CProfilesDialog::displayProfile(int index) argumentsEdit->setText(profile.arguments); commentsEdit->setPlainText(profile.comments); directoryPathLabel->setText(CConfigFile::getInstance()->getProfileDirectory()); + desktopShortcutCheckBox->setChecked(profile.desktopShortcut); + menuShortcutCheckBox->setChecked(profile.menuShortcut); + + updateExecutableVersion(index); m_currentProfileIndex = index; } @@ -123,6 +127,55 @@ void CProfilesDialog::addProfile() // TODO: browse all folders in AppData/Roaming/Ryzom } +void CProfilesDialog::updateExecutableVersion(int index) +{ + if (index < 0) return; + + const CProfile &profile = m_model->getProfiles()[index]; + + QString executable = profile.executable; + + // file empty, use default one + if (executable.isEmpty()) + { + executable = CConfigFile::getInstance()->getInstallationDirectory() + "/" + profile.server + "/"; + +#if defined(Q_OS_WIN32) + executable += "ryzom_client_r.exe"; +#elif defined(Q_OS_APPLE) + executable += "Ryzom.app/Contents/MacOS/Ryzom"; +#else + executable += "ryzom_client"; +#endif + } + + // file doesn't exist + if (!QFile::exists(executable)) return; + + // launch executable with --version argument + QProcess process; + process.setProcessChannelMode(QProcess::MergedChannels); + process.start(executable, QStringList() << "--version", QIODevice::ReadWrite); + + if (!process.waitForStarted()) return; + + QByteArray data; + + // read all output + while (process.waitForReadyRead()) data.append(process.readAll()); + + // convert output to string + QString versionString = QString::fromUtf8(data); + + // parse version from output + QRegExp reg("([A-Za-z0-1_.]+) ((DEV|FV) ([0-9.]+))"); + + if (reg.indexIn(versionString) > -1) + { + executableVersionLabel->setText(reg.cap(2)); + } +} + void CProfilesDialog::onExecutableBrowseClicked() { if (m_currentProfileIndex < 0) return; @@ -137,25 +190,5 @@ void CProfilesDialog::onExecutableBrowseClicked() executablePathLabel->setText(QFileInfo(profile.executable).fileName()); - QProcess process; - process.setProcessChannelMode(QProcess::MergedChannels); - process.start(profile.executable, QStringList() << "--version", QIODevice::ReadWrite); - - if (!process.waitForStarted()) return; - - QByteArray data; - - while (process.waitForReadyRead()) data.append(process.readAll()); - - QString versionString = QString::fromUtf8(data); - - QRegExp reg("([A-Za-z0-1_.]+) ((DEV|FV) ([0-9.]+))"); - - if (reg.indexIn(versionString) > -1) - { - executableVersionLabel->setText(reg.cap(2)); - } - - // ryzom_client_dev_d.exe DEV 0.12.0.7331 (built on 2016-02-25 22:16:50) - // Copyright (C) 2004-2016 Winchgate and The Ryzom Core Community + updateExecutableVersion(m_currentProfileIndex); } diff --git a/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.h b/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.h index 78fe14bbc..231ab554a 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.h +++ b/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.h @@ -48,6 +48,8 @@ private slots: void deleteProfile(int index); void addProfile(); + void updateExecutableVersion(int index); + void onExecutableBrowseClicked(); private: From 63dddb4df80fb6ce945d47c4f1acb2881cd0ecf3 Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 12 Mar 2016 21:59:29 +0100 Subject: [PATCH 17/32] Changed: Disable widgets if no profile selected --- .../tools/client/ryzom_installer/src/profilesdialog.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.cpp b/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.cpp index 5134902fc..9d0f0a4ed 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.cpp +++ b/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.cpp @@ -79,6 +79,15 @@ void CProfilesDialog::onProfileClicked(const QModelIndex &index) void CProfilesDialog::displayProfile(int index) { + bool enabled = index > -1; + + profileIdLabel->setEnabled(enabled); + accountEdit->setEnabled(enabled); + nameEdit->setEnabled(enabled); + serverComboBox->setEnabled(enabled); + argumentsEdit->setEnabled(enabled); + commentsEdit->setEnabled(enabled); + if (index < 0) return; saveProfile(m_currentProfileIndex); From a72e76be2f3577e3bc990d0df159222f6867fc50 Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 12 Mar 2016 22:00:12 +0100 Subject: [PATCH 18/32] Changed: Update index in servers list --- .../tools/client/ryzom_installer/src/profilesdialog.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.cpp b/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.cpp index 9d0f0a4ed..62eabc346 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.cpp +++ b/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.cpp @@ -97,7 +97,7 @@ void CProfilesDialog::displayProfile(int index) profileIdLabel->setText(QString::number(profile.id)); accountEdit->setText(profile.account); nameEdit->setText(profile.name); - serverComboBox->setCurrentIndex(0); + serverComboBox->setCurrentIndex(m_serversModel->getIndexFromServerID(profile.server)); executablePathLabel->setText(QFileInfo(profile.executable).fileName()); argumentsEdit->setText(profile.arguments); commentsEdit->setPlainText(profile.comments); @@ -115,11 +115,10 @@ void CProfilesDialog::saveProfile(int index) if (index < 0) return; CProfile &profile = m_model->getProfiles()[index]; - - profileIdLabel->setText(QString::number(profile.id)); + profile.account = accountEdit->text(); profile.name = nameEdit->text(); -// serverComboBox->setCurrentIndex(0); + profile.server = m_serversModel->getServerIDFromIndex(serverComboBox->currentIndex()); profile.arguments = argumentsEdit->text(); profile.comments = commentsEdit->toPlainText(); } From 1623961c20adad393dfd3a14151d3dce06075514 Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 12 Mar 2016 22:00:43 +0100 Subject: [PATCH 19/32] Changed: Save when clicking on OK --- code/ryzom/tools/client/ryzom_installer/src/profilesdialog.cpp | 2 ++ code/ryzom/tools/client/ryzom_installer/src/profilesmodel.cpp | 1 + 2 files changed, 3 insertions(+) diff --git a/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.cpp b/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.cpp index 62eabc346..1904c604b 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.cpp +++ b/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.cpp @@ -50,6 +50,8 @@ CProfilesDialog::~CProfilesDialog() void CProfilesDialog::accept() { + saveProfile(m_currentProfileIndex); + m_model->save(); QDialog::accept(); diff --git a/code/ryzom/tools/client/ryzom_installer/src/profilesmodel.cpp b/code/ryzom/tools/client/ryzom_installer/src/profilesmodel.cpp index 1977b423b..64f15c5d5 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/profilesmodel.cpp +++ b/code/ryzom/tools/client/ryzom_installer/src/profilesmodel.cpp @@ -43,6 +43,7 @@ bool CProfilesModel::removeRows(int row, int count, const QModelIndex &parent) bool CProfilesModel::save() const { CConfigFile::getInstance()->setProfiles(m_profiles); + CConfigFile::getInstance()->save(); return true; } From 97b85a790d5a08f3e864956b74f3bea7abcadcd7 Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 12 Mar 2016 22:01:03 +0100 Subject: [PATCH 20/32] Changed: Update shorcuts --- code/ryzom/tools/client/ryzom_installer/src/profilesdialog.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.cpp b/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.cpp index 1904c604b..ff42f12ec 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.cpp +++ b/code/ryzom/tools/client/ryzom_installer/src/profilesdialog.cpp @@ -96,6 +96,7 @@ void CProfilesDialog::displayProfile(int index) const CProfile &profile = m_model->getProfiles()[index]; + // update all widgets with content of profile profileIdLabel->setText(QString::number(profile.id)); accountEdit->setText(profile.account); nameEdit->setText(profile.name); @@ -123,6 +124,8 @@ void CProfilesDialog::saveProfile(int index) profile.server = m_serversModel->getServerIDFromIndex(serverComboBox->currentIndex()); profile.arguments = argumentsEdit->text(); profile.comments = commentsEdit->toPlainText(); + profile.desktopShortcut = desktopShortcutCheckBox->isChecked(); + profile.menuShortcut = menuShortcutCheckBox->isChecked(); } void CProfilesDialog::deleteProfile(int index) From 8a726b62da4cffcd875ba4e421944926bcf53bb0 Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 14 May 2016 18:47:28 +0200 Subject: [PATCH 21/32] Changed: Renamed configurationsComboBox to profilesComboBox --- code/ryzom/tools/client/ryzom_installer/ui/mainwindow.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/ryzom/tools/client/ryzom_installer/ui/mainwindow.ui b/code/ryzom/tools/client/ryzom_installer/ui/mainwindow.ui index ee9bd7b0e..644a3a2c2 100644 --- a/code/ryzom/tools/client/ryzom_installer/ui/mainwindow.ui +++ b/code/ryzom/tools/client/ryzom_installer/ui/mainwindow.ui @@ -119,7 +119,7 @@ p, li { white-space: pre-wrap; } 0 - + Atys From 17bec77777d859233b118df9c2551d276b2f98e8 Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 14 May 2016 18:47:45 +0200 Subject: [PATCH 22/32] Changed: Hide help button --- code/ryzom/tools/client/ryzom_installer/src/wizarddialog.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/code/ryzom/tools/client/ryzom_installer/src/wizarddialog.cpp b/code/ryzom/tools/client/ryzom_installer/src/wizarddialog.cpp index 1cb13c58a..0444487cd 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/wizarddialog.cpp +++ b/code/ryzom/tools/client/ryzom_installer/src/wizarddialog.cpp @@ -44,6 +44,8 @@ QString qBytesToHumanReadable(qint64 bytes) CWizardDialog::CWizardDialog():QDialog() { + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + setupUi(this); currentDirectoryRadioButton->setVisible(false); From 66c8269aadbe63f5ff68ebab86a072f8404056b1 Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 14 May 2016 18:48:06 +0200 Subject: [PATCH 23/32] Changed: Compile shortcuts code only under Windows --- code/ryzom/tools/client/ryzom_installer/src/utils.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/code/ryzom/tools/client/ryzom_installer/src/utils.cpp b/code/ryzom/tools/client/ryzom_installer/src/utils.cpp index aa060a1de..b5a85e3b0 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/utils.cpp +++ b/code/ryzom/tools/client/ryzom_installer/src/utils.cpp @@ -47,6 +47,7 @@ wchar_t* qToWide(const QString &str) return (wchar_t*)str.utf16(); } +#ifdef Q_OS_WIN32 // CreateLink - Uses the Shell's IShellLink and IPersistFile interfaces // to create and store a shortcut to the specified object. @@ -180,3 +181,5 @@ HRESULT ResolveIt(HWND hwnd, const QString &linkFile, QString &path) return hres; } + +#endif From f73fa8ee24c6ff19f0df861483f36c8974b8f6ad Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 14 May 2016 18:48:32 +0200 Subject: [PATCH 24/32] Changed: Display error message when no installer.ini file found --- code/ryzom/tools/client/ryzom_installer/src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/ryzom/tools/client/ryzom_installer/src/main.cpp b/code/ryzom/tools/client/ryzom_installer/src/main.cpp index c78021ae5..fd6aaf894 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/main.cpp +++ b/code/ryzom/tools/client/ryzom_installer/src/main.cpp @@ -83,7 +83,7 @@ int main(int argc, char *argv[]) if (step == CConfigFile::DisplayNoServerError) { - // display error + QMessageBox::critical(NULL, QApplication::tr("Error"), QApplication::tr("Unable to find installer.ini")); return 1; } From 4b6a17bcb2b1ba18154daada985b17a863a13172 Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 14 May 2016 18:50:27 +0200 Subject: [PATCH 25/32] Changed: Clean files --- .../client/ryzom_installer/src/archive.cpp | 28 +++++++++++++++ .../client/ryzom_installer/src/archive.h | 1 + .../client/ryzom_installer/src/configfile.cpp | 35 ++++++++++++++++++- .../client/ryzom_installer/src/configfile.h | 3 ++ .../client/ryzom_installer/src/mainwindow.cpp | 5 +++ 5 files changed, 71 insertions(+), 1 deletion(-) diff --git a/code/ryzom/tools/client/ryzom_installer/src/archive.cpp b/code/ryzom/tools/client/ryzom_installer/src/archive.cpp index 287f559dc..5d32a4c39 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/archive.cpp +++ b/code/ryzom/tools/client/ryzom_installer/src/archive.cpp @@ -355,6 +355,34 @@ bool CArchive::copyProfileFiles() return copyFiles(files); } +bool CArchive::cleanServerFiles(const QString &directory) +{ + QDir dir(directory); + + // directory doesn't exist + if (!dir.exists()) return false; + + if (!dir.cd("data") && dir.exists()) return false; + + // temporary files + QStringList files = dir.entryList(QStringList() << "*.string_cache" << "*.packed_sheets" << "*.packed" << "*.pem", QDir::Files); + + foreach(const QString &file, files) + { + dir.remove(file); + } + + // fonts directory is not needed anymore + if (dir.cd("fonts") && dir.exists()) + { + dir.removeRecursively(); + } + + emit done(); + + return true; +} + bool CArchive::copyServerFiles(const QString &src, const QString &dst) { if (src.isEmpty() || dst.isEmpty()) return false; diff --git a/code/ryzom/tools/client/ryzom_installer/src/archive.h b/code/ryzom/tools/client/ryzom_installer/src/archive.h index 0f8284b9e..202ad664f 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/archive.h +++ b/code/ryzom/tools/client/ryzom_installer/src/archive.h @@ -34,6 +34,7 @@ public: bool extract(const QString &filename, const QString &dest); bool copyServerFiles(const QString &src, const QString &dst); bool copyProfileFiles(const QString &src, const QString &dst); + bool cleanServerFiles(const QString &directory); void stop(); bool mustStop(); diff --git a/code/ryzom/tools/client/ryzom_installer/src/configfile.cpp b/code/ryzom/tools/client/ryzom_installer/src/configfile.cpp index b3b41cfde..ffc77a9d4 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/configfile.cpp +++ b/code/ryzom/tools/client/ryzom_installer/src/configfile.cpp @@ -420,13 +420,41 @@ bool CConfigFile::isRyzomClientInstalledIn(const QString &directory) const // client_default.cfg doesn't exist if (!dir.exists("client_default.cfg")) return false; - if (!dir.exists(getServer().clientFilename)) return false; + QString clientFilename = getServer().clientFilename; + + // check if client is defined and exists + if (!clientFilename.isEmpty() && !dir.exists(clientFilename)) return false; // TODO: more checks return true; } +bool CConfigFile::foundTemporaryFiles(const QString &directory) const +{ + QDir dir(directory); + + // directory doesn't exist + if (!dir.exists()) return false; + + if (!dir.cd("data") && dir.exists()) return false; + + // temporary files + if (!dir.entryList(QStringList() << "*.string_cache" << "*.packed_sheets" << "*.packed" << "*.pem", QDir::Files).isEmpty()) return true; + + // fonts directory is not needed anymore + if (dir.cd("fonts") && dir.exists()) return true; + + return false; +} + +bool CConfigFile::shouldCreateDesktopShortcut() const +{ + const CProfile &profile = getProfile(); + + return profile.desktopShortcut && !QFile::exists(QStandardPaths::writableLocation(QStandardPaths::DesktopLocation) + "/Ryzom.lnk"); +} + QString CConfigFile::getClientFullPath() const { QString path = getProfile().executable; @@ -527,6 +555,11 @@ CConfigFile::InstallationStep CConfigFile::getNextStep() const // client is not extracted from BNP if (!isRyzomClientInstalledIn(serverDirectory)) { + if (foundTemporaryFiles(serverDirectory)) + { + return CleanFiles; + } + if (QFile::exists(getSrcServerClientBNPFullPath())) { return ExtractBnpClient; diff --git a/code/ryzom/tools/client/ryzom_installer/src/configfile.h b/code/ryzom/tools/client/ryzom_installer/src/configfile.h index 32bfb99c6..ae01ee2af 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/configfile.h +++ b/code/ryzom/tools/client/ryzom_installer/src/configfile.h @@ -87,6 +87,7 @@ public: ExtractDownloadedClient, CopyServerFiles, CopyProfileFiles, + CleanFiles, ExtractBnpClient, CreateProfile, CreateShortcuts, @@ -144,6 +145,8 @@ public: bool isRyzomInstalledIn(const QString &directory) const; bool areRyzomDataInstalledIn(const QString &directory) const; bool isRyzomClientInstalledIn(const QString &directory) const; + bool foundTemporaryFiles(const QString &directory) const; + bool shouldCreateDesktopShortcut() const; // installation choices bool use64BitsClient() const; diff --git a/code/ryzom/tools/client/ryzom_installer/src/mainwindow.cpp b/code/ryzom/tools/client/ryzom_installer/src/mainwindow.cpp index d316bc6c9..b1e7952be 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/mainwindow.cpp +++ b/code/ryzom/tools/client/ryzom_installer/src/mainwindow.cpp @@ -135,6 +135,11 @@ void CMainWindow::processNextStep() m_archive->extract(config->getSrcServerClientBNPFullPath(), config->getInstallationDirectory() + "/" + server.id); break; + case CConfigFile::CleanFiles: + hideProgressBar(); + m_archive->cleanServerFiles(config->getInstallationDirectory() + "/" + server.id); + break; + case CConfigFile::CreateProfile: displayProgressBar(); break; From cee43e05df3e66970f9f056e387c133bfbb9c54a Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 14 May 2016 18:51:33 +0200 Subject: [PATCH 26/32] Changed: Play button --- .../client/ryzom_installer/src/mainwindow.cpp | 29 +++++++++++++++++++ .../client/ryzom_installer/src/mainwindow.h | 3 ++ 2 files changed, 32 insertions(+) diff --git a/code/ryzom/tools/client/ryzom_installer/src/mainwindow.cpp b/code/ryzom/tools/client/ryzom_installer/src/mainwindow.cpp index b1e7952be..452d78237 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/mainwindow.cpp +++ b/code/ryzom/tools/client/ryzom_installer/src/mainwindow.cpp @@ -22,6 +22,7 @@ #include "profilesdialog.h" #include "configfile.h" #include "config.h" +#include "profilesmodel.h" #include "seven_zip.h" @@ -70,6 +71,9 @@ CMainWindow::CMainWindow():QMainWindow(), m_archive(NULL), m_statusLabel(NULL) connect(actionProfiles, SIGNAL(triggered()), SLOT(onProfiles())); + connect(playButton, SIGNAL(clicked()), SLOT(onPlayClicked())); + connect(configureButton, SIGNAL(clicked()), SLOT(onConfigureClicked())); + connect(actionAboutQt, SIGNAL(triggered()), SLOT(onAboutQt())); connect(actionAbout, SIGNAL(triggered()), SLOT(onAbout())); @@ -170,6 +174,8 @@ void CMainWindow::displayConfigurationsChoices() { downloadFrame->setVisible(false); configurationFrame->setVisible(true); + + profilesComboBox->setModel(new CProfilesModel(this)); } void CMainWindow::showEvent(QShowEvent *e) @@ -214,6 +220,28 @@ void CMainWindow::onStopClicked() } } +void CMainWindow::onPlayClicked() +{ + int profileIndex = profilesComboBox->currentIndex(); + + if (profileIndex < 0) return; + + CProfile profile = CConfigFile::getInstance()->getProfile(profileIndex); + + if (profile.executable.isEmpty()) return; + + QStringList arguments; + arguments << "-p"; + arguments << QString::number(profileIndex); + arguments << profile.arguments; + + bool started = QProcess::startDetached(profile.executable, arguments); +} + +void CMainWindow::onConfigureClicked() +{ +} + void CMainWindow::onProfiles() { CProfilesDialog dialog; @@ -391,6 +419,7 @@ void CMainWindow::onExtractSuccess(qint64 total) resumeButton->setVisible(false); stopButton->setVisible(false); +} processNextStep(); } diff --git a/code/ryzom/tools/client/ryzom_installer/src/mainwindow.h b/code/ryzom/tools/client/ryzom_installer/src/mainwindow.h index 7e03f9a80..707d7efcf 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/mainwindow.h +++ b/code/ryzom/tools/client/ryzom_installer/src/mainwindow.h @@ -41,6 +41,9 @@ public slots: void onResumeClicked(); void onStopClicked(); + void onPlayClicked(); + void onConfigureClicked(); + void onProfiles(); void onAbout(); void onAboutQt(); From c5a35e08289e08f8c8af368a6995e66b7620cf26 Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 14 May 2016 18:52:09 +0200 Subject: [PATCH 27/32] Changed: New done() signal to go to next step --- .../client/ryzom_installer/src/archive.cpp | 4 ++++ .../client/ryzom_installer/src/archive.h | 3 +++ .../client/ryzom_installer/src/mainwindow.cpp | 21 +++++++++++++++++-- .../client/ryzom_installer/src/mainwindow.h | 4 ++++ 4 files changed, 30 insertions(+), 2 deletions(-) diff --git a/code/ryzom/tools/client/ryzom_installer/src/archive.cpp b/code/ryzom/tools/client/ryzom_installer/src/archive.cpp index 5d32a4c39..8ae958e7e 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/archive.cpp +++ b/code/ryzom/tools/client/ryzom_installer/src/archive.cpp @@ -460,6 +460,7 @@ bool CArchive::copyFiles(const FilesToCopy &files) } emit extractSuccess(totalSize); + emit done(); return true; } @@ -619,6 +620,7 @@ bool CArchive::extract7z() { case SZ_OK: emit extractSuccess(totalUncompressed); + emit done(); return true; case SZ_ERROR_INTERRUPTED: @@ -720,6 +722,7 @@ bool CArchive::extractZip() } emit extractSuccess(totalSize); + emit done(); return true; } @@ -744,6 +747,7 @@ bool CArchive::progress(const std::string &filename, uint32 currentSize, uint32 if (currentSize == totalSize) { emit extractSuccess((qint64)totalSize); + emit done(); } return true; diff --git a/code/ryzom/tools/client/ryzom_installer/src/archive.h b/code/ryzom/tools/client/ryzom_installer/src/archive.h index 202ad664f..a442056e1 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/archive.h +++ b/code/ryzom/tools/client/ryzom_installer/src/archive.h @@ -61,6 +61,9 @@ signals: // emitted when an error occurs void extractFail(const QString &error); + // emitted when done and should process next step + void done(); + protected: struct FileToCopy diff --git a/code/ryzom/tools/client/ryzom_installer/src/mainwindow.cpp b/code/ryzom/tools/client/ryzom_installer/src/mainwindow.cpp index 452d78237..f8d6b6ed6 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/mainwindow.cpp +++ b/code/ryzom/tools/client/ryzom_installer/src/mainwindow.cpp @@ -68,6 +68,7 @@ CMainWindow::CMainWindow():QMainWindow(), m_archive(NULL), m_statusLabel(NULL) connect(m_archive, SIGNAL(extractProgress(qint64, QString)), SLOT(onExtractProgress(qint64, QString))); connect(m_archive, SIGNAL(extractSuccess(qint64)), SLOT(onExtractSuccess(qint64))); connect(m_archive, SIGNAL(extractFail(QString)), SLOT(onExtractFail(QString))); + connect(m_archive, SIGNAL(done()), SLOT(onDone())); connect(actionProfiles, SIGNAL(triggered()), SLOT(onProfiles())); @@ -122,6 +123,7 @@ void CMainWindow::processNextStep() case CConfigFile::ExtractDownloadedClient: displayProgressBar(); + // TODO break; case CConfigFile::CopyServerFiles: @@ -145,11 +147,15 @@ void CMainWindow::processNextStep() break; case CConfigFile::CreateProfile: - displayProgressBar(); + hideProgressBar(); + config->createDefaultProfile(); + onDone(); break; case CConfigFile::CreateShortcuts: - displayProgressBar(); + hideProgressBar(); + config->createDefaultShortcuts(); + onDone(); break; default: @@ -170,6 +176,15 @@ void CMainWindow::displayProgressBar() stopButton->setVisible(false); } +void CMainWindow::hideProgressBar() +{ + downloadFrame->setVisible(false); + configurationFrame->setVisible(false); + + resumeButton->setVisible(false); + stopButton->setVisible(false); +} + void CMainWindow::displayConfigurationsChoices() { downloadFrame->setVisible(false); @@ -421,6 +436,8 @@ void CMainWindow::onExtractSuccess(qint64 total) stopButton->setVisible(false); } +void CMainWindow::onDone() +{ processNextStep(); } diff --git a/code/ryzom/tools/client/ryzom_installer/src/mainwindow.h b/code/ryzom/tools/client/ryzom_installer/src/mainwindow.h index 707d7efcf..aa2be5659 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/mainwindow.h +++ b/code/ryzom/tools/client/ryzom_installer/src/mainwindow.h @@ -66,6 +66,8 @@ public slots: void onExtractSuccess(qint64 total); void onExtractFail(const QString &error); + void onDone(); + protected: void showEvent(QShowEvent *e); void closeEvent(QCloseEvent *e); @@ -73,6 +75,8 @@ protected: void processNextStep(); void displayProgressBar(); + void hideProgressBar(); + void displayConfigurationsChoices(); QWinTaskbarButton *m_button; From a85c1b0b15eff8622f245ac56bce14c42589f679 Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 14 May 2016 18:52:40 +0200 Subject: [PATCH 28/32] Changed: Don't redefine FILE_ATTRIBUTE_* if already defined --- .../client/ryzom_installer/src/archive.cpp | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/code/ryzom/tools/client/ryzom_installer/src/archive.cpp b/code/ryzom/tools/client/ryzom_installer/src/archive.cpp index 8ae958e7e..10caa7262 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/archive.cpp +++ b/code/ryzom/tools/client/ryzom_installer/src/archive.cpp @@ -34,19 +34,57 @@ #include +#ifndef FILE_ATTRIBUTE_READONLY #define FILE_ATTRIBUTE_READONLY 0x1 +#endif +#ifndef FILE_ATTRIBUTE_HIDDEN #define FILE_ATTRIBUTE_HIDDEN 0x2 +#endif + +#ifndef FILE_ATTRIBUTE_SYSTEM #define FILE_ATTRIBUTE_SYSTEM 0x4 +#endif + +#ifndef FILE_ATTRIBUTE_DIRECTORY #define FILE_ATTRIBUTE_DIRECTORY 0x10 +#endif + +#ifndef FILE_ATTRIBUTE_ARCHIVE #define FILE_ATTRIBUTE_ARCHIVE 0x20 +#endif + +#ifndef FILE_ATTRIBUTE_DEVICE #define FILE_ATTRIBUTE_DEVICE 0x40 +#endif + +#ifndef FILE_ATTRIBUTE_NORMAL #define FILE_ATTRIBUTE_NORMAL 0x80 +#endif + +#ifndef FILE_ATTRIBUTE_TEMPORARY #define FILE_ATTRIBUTE_TEMPORARY 0x100 +#endif + +#ifndef FILE_ATTRIBUTE_SPARSE_FILE #define FILE_ATTRIBUTE_SPARSE_FILE 0x200 +#endif + +#ifndef FILE_ATTRIBUTE_REPARSE_POINT #define FILE_ATTRIBUTE_REPARSE_POINT 0x400 +#endif + +#ifndef FILE_ATTRIBUTE_COMPRESSED #define FILE_ATTRIBUTE_COMPRESSED 0x800 +#endif + +#ifndef FILE_ATTRIBUTE_OFFLINE #define FILE_ATTRIBUTE_OFFLINE 0x1000 +#endif + +#ifndef FILE_ATTRIBUTE_ENCRYPTED #define FILE_ATTRIBUTE_ENCRYPTED 0x4000 +#endif + #define FILE_ATTRIBUTE_UNIX_EXTENSION 0x8000 /* trick for Unix */ #define FILE_ATTRIBUTE_WINDOWS 0x5fff From e94374db7db8aba922ca68eb07d86798d87579bb Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 14 May 2016 18:52:53 +0200 Subject: [PATCH 29/32] Changed: Include Windows.h only under Windows --- code/ryzom/tools/client/ryzom_installer/src/archive.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/code/ryzom/tools/client/ryzom_installer/src/archive.cpp b/code/ryzom/tools/client/ryzom_installer/src/archive.cpp index 10caa7262..425e4f379 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/archive.cpp +++ b/code/ryzom/tools/client/ryzom_installer/src/archive.cpp @@ -32,7 +32,9 @@ #include +#ifdef Q_OS_WIN #include +#endif #ifndef FILE_ATTRIBUTE_READONLY #define FILE_ATTRIBUTE_READONLY 0x1 From a63cea2a591650fdd262c0f8bd385d72e9474e33 Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 14 May 2016 18:53:09 +0200 Subject: [PATCH 30/32] Changed: Check for memory leaks --- code/ryzom/tools/client/ryzom_installer/src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/ryzom/tools/client/ryzom_installer/src/main.cpp b/code/ryzom/tools/client/ryzom_installer/src/main.cpp index fd6aaf894..d4ee9926e 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/main.cpp +++ b/code/ryzom/tools/client/ryzom_installer/src/main.cpp @@ -50,7 +50,7 @@ int main(int argc, char *argv[]) { #if defined(_MSC_VER) && defined(_DEBUG) -// _CrtSetDbgFlag (_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); + _CrtSetDbgFlag (_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); #endif NLMISC::CApplicationContext appContext; From 994e90e58fd1cef35f5076f1483bc6a146f3ec84 Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 14 May 2016 18:53:35 +0200 Subject: [PATCH 31/32] Changed: Create default profile --- .../client/ryzom_installer/src/configfile.cpp | 36 +++++++++++++++++-- .../client/ryzom_installer/src/configfile.h | 3 ++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/code/ryzom/tools/client/ryzom_installer/src/configfile.cpp b/code/ryzom/tools/client/ryzom_installer/src/configfile.cpp index ffc77a9d4..d62a58372 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/configfile.cpp +++ b/code/ryzom/tools/client/ryzom_installer/src/configfile.cpp @@ -585,13 +585,15 @@ CConfigFile::InstallationStep CConfigFile::getNextStep() const return CreateProfile; } + QString clientCfg = QString("%1/%2/client.cfg").arg(getProfileDirectory()).arg(profile.id); + // migration profile - if (!getSrcServerDirectory().isEmpty() && QFile::exists(getSrcProfileDirectory() + "/client.cfg") && !QFile::exists(QString("%1/%2/client.cfg").arg(getProfileDirectory()).arg(profile.name))) + if (!getSrcServerDirectory().isEmpty() && QFile::exists(getSrcProfileDirectory() + "/client.cfg") && !QFile::exists(clientCfg)) { return CopyProfileFiles; } - if (!QFile::exists(QStandardPaths::writableLocation(QStandardPaths::DesktopLocation) + "/Ryzom.lnk")) + if (shouldCreateDesktopShortcut()) { // TODO: check they point to getClientFullPath() return CreateShortcuts; @@ -599,3 +601,33 @@ CConfigFile::InstallationStep CConfigFile::getNextStep() const return Done; } + +bool CConfigFile::createDefaultProfile() +{ + CServer server = getServer(getDefaultServer()); + + CProfile profile; + + profile.id = 0; + profile.executable = getClientFullPath(); + profile.name = QString("Ryzom (%1)").arg(server.name); + profile.server = server.id; + profile.comments = "Default profile created by Ryzom Installer"; + +#ifdef Q_OS_WIN32 + profile.desktopShortcut = QFile::exists(QStandardPaths::writableLocation(QStandardPaths::DesktopLocation) + "/Ryzom.lnk"); +#endif + + // TODO + // profile.menuShortcut + + addProfile(profile); + save(); + + return true; +} + +bool CConfigFile::createDefaultShortcuts() +{ + return true; +} diff --git a/code/ryzom/tools/client/ryzom_installer/src/configfile.h b/code/ryzom/tools/client/ryzom_installer/src/configfile.h index ae01ee2af..3426681bc 100644 --- a/code/ryzom/tools/client/ryzom_installer/src/configfile.h +++ b/code/ryzom/tools/client/ryzom_installer/src/configfile.h @@ -162,6 +162,9 @@ public: InstallationStep getNextStep() const; + bool createDefaultProfile(); + bool createDefaultShortcuts(); + private: int m_defaultServer; int m_defaultProfile; From 0fac8984c2dc20ccdf85850b4642b697b04ab8dc Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 14 May 2016 18:53:57 +0200 Subject: [PATCH 32/32] Changed: Only compile installer if WITH_RYZOM_INSTALLER set --- code/CMakeModules/nel.cmake | 2 +- code/ryzom/tools/client/CMakeLists.txt | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/code/CMakeModules/nel.cmake b/code/CMakeModules/nel.cmake index 51ffaf950..70306482b 100644 --- a/code/CMakeModules/nel.cmake +++ b/code/CMakeModules/nel.cmake @@ -321,7 +321,7 @@ MACRO(NL_SETUP_RYZOM_DEFAULT_OPTIONS) OPTION(WITH_RYZOM_CLIENT "Build Ryzom Core Client" ON ) OPTION(WITH_RYZOM_TOOLS "Build Ryzom Core Tools" ON ) OPTION(WITH_RYZOM_SERVER "Build Ryzom Core Services" ON ) - OPTION(WITH_RYZOM_SOUND "Enable Ryzom Core Sound" ON ) + OPTION(WITH_RYZOM_INSTALLER "Build Ryzom Installer" OFF) ### # Optional support diff --git a/code/ryzom/tools/client/CMakeLists.txt b/code/ryzom/tools/client/CMakeLists.txt index 7fa015c47..7d7abf08f 100644 --- a/code/ryzom/tools/client/CMakeLists.txt +++ b/code/ryzom/tools/client/CMakeLists.txt @@ -3,8 +3,12 @@ IF(WITH_RYZOM_CLIENT) IF(WITH_QT OR WITH_QT5) ADD_SUBDIRECTORY(client_config_qt) - ADD_SUBDIRECTORY(ryzom_installer) + + IF(WITH_RYZOM_INSTALLER) + ADD_SUBDIRECTORY(ryzom_installer) + ENDIF() ENDIF() + ENDIF() IF(WITH_RYZOM_TOOLS)