From 0c6671a4f9de1752157a57e032dfe84b2d10221d Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sat, 29 Jan 2022 13:47:58 +0530 Subject: [PATCH] Removed python implementation --- libspeechd.so | 1 + libspeechd.so.2 | 1 + libspeechd.so.2.6.0 | Bin 0 -> 117896 bytes stardew-access/LinuxSpeech/__init__.py | 0 stardew-access/LinuxSpeech/_test.py | 9 - stardew-access/LinuxSpeech/_test_interrupt.py | 14 - .../LinuxSpeech/speechd/__init__.py | 17 - stardew-access/LinuxSpeech/speechd/client.py | 1186 ----------------- stardew-access/LinuxSpeech/speechd/paths.py | 1 - stardew-access/LinuxSpeech/wrapper.py | 18 - stardew-access/ScreenReader.cs | 27 - stardew-access/stardew-access.csproj | 3 +- 12 files changed, 3 insertions(+), 1274 deletions(-) create mode 120000 libspeechd.so create mode 120000 libspeechd.so.2 create mode 100755 libspeechd.so.2.6.0 delete mode 100644 stardew-access/LinuxSpeech/__init__.py delete mode 100644 stardew-access/LinuxSpeech/_test.py delete mode 100644 stardew-access/LinuxSpeech/_test_interrupt.py delete mode 100644 stardew-access/LinuxSpeech/speechd/__init__.py delete mode 100644 stardew-access/LinuxSpeech/speechd/client.py delete mode 100644 stardew-access/LinuxSpeech/speechd/paths.py delete mode 100644 stardew-access/LinuxSpeech/wrapper.py diff --git a/libspeechd.so b/libspeechd.so new file mode 120000 index 0000000..4880363 --- /dev/null +++ b/libspeechd.so @@ -0,0 +1 @@ +libspeechd.so.2.6.0 \ No newline at end of file diff --git a/libspeechd.so.2 b/libspeechd.so.2 new file mode 120000 index 0000000..4880363 --- /dev/null +++ b/libspeechd.so.2 @@ -0,0 +1 @@ +libspeechd.so.2.6.0 \ No newline at end of file diff --git a/libspeechd.so.2.6.0 b/libspeechd.so.2.6.0 new file mode 100755 index 0000000000000000000000000000000000000000..e0b3b35836b904d4975b55174b9f653b7515868a GIT binary patch literal 117896 zcmeFadt6mj8aKYr11Lq$ci)DXESg}?}SDJu#!Pef5u zO*J+#)igCzja{bfj#+AVvYVBa-E5!Gw5H6;;{ATtTKlj;DChUb`}w@T&ura$t>;silQYwvwFmt|&8v)gQ%c{sE)HA0>V5|gG1Ht%SoNz+obC@oD()P@LJr=saB z@g_;EnQ9&hlUo?G$rA3E&YUdqW@@b`+N)6YI;1%RB;HJ|^}UB&L6@N2j8$9NHQC0Z= zrM(l2-C=j5_pI?ic0X@ByXwf)qG z?!2zb>+Lt^Ay4g}(tO+h7UpTtdT5E)cki~lIlr53x4mEM+1;=3wL8zbU?F+=ac1G1fs;oz&Y3u85l}~tO6MS@APW?H4$|{fei70oIM2tKhjS^; zWjG6Q7U49H<;bkSc>&H+6KP4ykT1tsf%6XvU#Ze6m99a$7Uz1Lm*A|y$>VaFXd94T zi8G+!I;7X&Y`}S)!rh4UPbz;4($X;pcdhX+-T3{VD~|pl^`aNb)+9Lk&VJ~wZwB7- z>SsyE#>dUtJ7dC~udTfNk(Y0c37>lF?%~V&vMrk9trsFz4NWR{&9Zs;fhc08vW{vJ9qgC zTYi{*^9wzm`}My0aewPGyL{c+$sb)X*w-`fnOEL8@|WPt`jOZT2Y3C)RzIRPkd-oY zo9o?{8EcdHl?tE+W7w=s4 z#^`Q0CSCcTNq^qD@1+}-PyFH!Hx;)0H2?hKs0hcw|9f---*CxURVJ`JD5gO8lcY;>dmH_j}LLuy@(W4abubFFEp;8%I5Kmojmu z$whGF9-WkwVqkQJpV_oygw=T=!1!*8|sC zyBBq#*U&E7J=_I-K^OEDUFaLv1s{%|&g5Lx1)k%lGx{gG;QwA1_&d9he|s17w|0Sl zvkN@qKxcAhA((cC-_`}avkU$kyTEh0>Wu#_UFgf;-WmPfUGyWG)x0=69uET18J`Ec zkaI#8@*M7hJTsq6y(Ru}wtb;193Oo*Mye{C217k5Fwpo?~Y>SDaxx}fKL)|ouN zb|FvCF8XUg7yK{ig3pIt=ry(r`ft0?Yb50N;_P^obs^87F7SWv0w2)@p6iT+j*SBT z2hPsyxvvZSYhB>4?xNk6F6@wrg;@`+o0i7K4vuaz(fVSMNc>i{;4#-ZkBWZmrg^4H zLat4Cd?EU!o96XP*yK~K@J*TR@u^~5chizfB)%E_$K!UVl1DAVxQ60!rE0gqCt-8_ z<5GN%tdRIYGSTJ=f9R$)s{P1l)aUV%un*){3$Z;Ct-UAg1F6rIh@+t65u@a(>7duW z%5IHnacJs$T-hy|6D|&OO}GMbcGvo7O?OCql1#Mk6`!rMCE^B!->T#k3rFBM5Ag_^ z_BmU^X1fW>&J`K$@v%y;h7R&HEBgJ@CH-kK(Kaf4v+6I-$2>+Vy*zUyZ2H63s$aII zOS~yhoRYJlgWaA`d~)VVdQ;!?%znI3;<@hT5mfXIxe_+%D@;4jm3UL0H&naH9qi*% za(dDvJ*tc2EJg3hlCUZN0>!_%gTATCJ~hhDBb0r1EB-Z8B_GbOJT@u&S8#)X!<7Gs z;*;FLKIbWa$jO%UrvE>s_-tJ$@k(6n8q+=J<=hbq^N#uQvRmQs=ce^_jHJF zcbM{b@Y`8Res7j+@HEx#O^Q!Lhj^Q=_*8TlryG@CPj%2MUh&zg`u9@BKT`3@=`i25 zC_6M3OTt;ulgHi4o;k{%+^6$cqU2wyLfQ9ANRa$XzX?5iaR4S-iS5cI=EU&b@K&z-)v8pJ)FmGjbRnb~vF6@A+ zE^CJ@C@(9_t1MX#Ekc9>wiGQX742_scjl!BYc7ObpDmN~LnE$Q>BD~nd;t*S1o zDp^^SS6H%2QqZ;)uy9o|RL-viO_eed8kg2=uY6$<>{h-mq(nh}SwT?;Ej!X&P+n4| zQI@KJ(X8bzH((gDy}K1T9LP^sG@Y8R2toFDs47eP+C${ zR+U#Go1@i*RC#oQkYp0IDj)5d^(3mIq^e+r1y@?VvW=uPzifFm6bhBY%_^#^lpO@N zQ-Ov4QffG#=xMep(54MT2#XB`LFKx#sue|*C6!VSSQk!T1TP1N4y_4XWmUN>WF`-- zg|K4OH_rzyo!AIoZusJAuyqp@QSDl3qiolXL<>jHTLq6UYFBFdcsu0k^3XVF z6CpHu+M_y%z=|DEh!J^~=0mnSQLvErhV*V5pKYy5UR}_3(1UGL@D6AprRkpJ++oy( zWbA-%rq&c^U$x)DoFo=1GvzX8- z^4DNhv97FuEwJhG3QTlGRh$k>%8JXik}{a0Oaw3rxUxb_E$xd5Y0L9VxSqg_QI6?f zsUs1)8xXgv6kg^ zQ75mwxL8)mm(5q^uPbhwqE@SkS+Z0Upr9`;U%s3(w=l6LhvSNpDgIR)APoT9y@wM zC_gTg8z0Jz11%1__=iQR$}yFPLtvcZ8m3BFCd=(&Cn0N-o>)mMaV8}pmaqkD8zvRg zaGZEf`2Wy9P00Ef_8{hSk?MXWfE?NQn*+FhiN}3cvWB4 zcWSms$y4&PhxnP##j&gxNAm=kHmT>{CcZ}DH{T)4O}q*3y_KFxxUYm@-CE3i%5;cb+8;^`^ok<=)2#7p0iaIyuDEmY`8 zwcr~bgXTZS<2@u~?X2o@DqXdXIpD7cwt&vx$*G!oe*QQ1ZkUmys#dUY11c>h?v*Oy^C;{So z%dEKWZIl4L*IMuy7JQ8bpJ~DWFZ(|W{Lcdav%vo>@IMRu&jSDd zv%r7cgMRYY9@PDHVejGDqkm&lmA$puU;DhiLnzxi;ZvYnhklN$dvF?ZgvHJ=xW5(0 z(Dw=3Kpboq@E*c;!c78xi7>a;!L0&*jxe{>!A1c;K{$+XgMc3*%&l^;M!^3d%w=J) zLcn(r=GHj4RKT|q#yt%!m?Plp3G>!MFipT$5$2XTm@MEc2y?3(^a}VQ!qJ310$xR! zTi~E3;0p-52_N|th@r~}bITjtFW~bCbE_L{7Vuoc+~NkC1U!Q24;IV|cB@NaH_*BB&iUunLoIsdc(BM)5_an@$XD~;=JqUBl8B7y!Bw=nf zgUJH66Xq5(=oRo!Wq`T040;6oEn#jcgPMT9BFrsg@W?OJ|5L(V!utjMKH+%6%>v#- zIDv4JfL|iaEn#r0fS)7GTRg!=0Y5>QTf<<3fFB~vtzWQ4!2cl3tzED}z;_Vl)-AYH zz_$|SmMxeg;OhxtfFm|LD;lYnOs=2j=TRlrjTbBhyf6!0X%+}Z>i1U!~7 zw=}^T0iQ~kTb5vjfD;IFs}fu);C_U;MG58zxCdcwO@e6xjwH-mXTf9v+X-`v67&lA zCv1;baBC9u2>4sV+>!(}0e?l9Tan1@de!@!yd@JEB!Z`xIp70F9 zX#&2AFmEvglLdSQ;cUWQ0bfLTCSea?fBlqoK=|v!RxvsKSXD2C#s@Btp)sO$zk6^E zXtfll@L&CbBmV7QpXJ|v%;C4~^uO_IRV)~6R1Eai{l)IV(B7=i_**lj z7wT%&ZANU!f=D{xz?Y)KmI_FT1A%7qvF)pPkyS^&XlU-k$sQY(i zBr>>a%!nI%#<@SWwq^yMZ#k9x{dHA|dUoJT<6#UAe;sQC4jbXLTV-9zIJ{MJ z{dH%jJ`o8n*@2_kC_jMm+GDNN+x>MHr}{UxR)rZmz}3GiUF^C2b?X!L%#=gMGe5Sr zKEt_$EzCismVu_7!KD$UDd6|z%({_@##pvl2k{I2Bcw9b9?V9l6K1qtOqro45YQ_ObE|e5T`h%b57WR?FE~pZ4 zj2a2eYmXJIo&hIS&8n&DG=l_2o`~{e(ZkXtX-x6DRB>|8N7aKd{r7 z0)2OJvekmiAYF08E(e2HN>gwil3fmt=%61LqZT5$2W!6EtiW6FyMr*J#V7QXCgTn0 zNA~$j(gXg$E`Q(!;Sc`0*uAG6MV27Z!i@?Q$kk zBGk)F`P_K*2lVky#B!9?r5csc z^jW%!aSN=6K_$n7>fcWEf!nz^T!ISjN2lA}kM-G^k?7J=4*A{B45sHhivzid&dnK# z;l^$-%&zmcM8G~X+|Mi?rTLC#EVk)YQ{l^gIE2xgveX(j|LHHR{!o~QetFn9Q}lDL zFoX1n>RvR1@&@qR2Hjg*J}{i%!13fe$2vkg5j!hzK#aS<#|Gm!Iskp*-f#~TL4O$C zzz!WyUy}i=!g<_Reua|kz_(%;(M$TF8|%6y`U1N)zUbbZh?N;jxj!Zs(pDD ze+uz-n7jiQt_5G+mYfy)ibO z8=KZ9&D{8T)ev+ZWXleG@&xK@t@Dx?Zl`_+btj;{n4efRnV5WX3`One?*+sjl##o3 zmu~C@GNsjc5cV@hB5w(}4Gi`p=KBM``0FmyjgS7r>X&$ow`Im_yuwW2@RHyTWH3HD z&S%!qQ9or7)=@?P{qn4wyk0P}*=f7d#FPa!krk5`QZH1)e&&#S!)|5*hg0?-rVJ;C zcSWOeAPN7fOGQyn7Eyjg_f$x!Gge6^h#a3GXPl3wvoK|fNiPLWFUl4HPE0n^ZgrQS z(blSG!SFS#0*wz~L=4!C|Dh0yDF==SM={755sImYmJ#SLQLdtj(SrIu&eAP)^ut{! zLY-c~A~0Ty7P1hMAN>{)EASQCd>$)Dj=jJxMDW*11%ZsaX{Eqn6%%$CM`3`hz+%qi z`^{m66-fp-KS|rvPnjS%?=%(*&fkLb1>lUfBF>m1+V~C>p)#7>6WmNstdIT* zYnUfqh7lmsnIJ_A3s~b@NQ(JAH@kkhpALuCIGGq|WkwX1&$>-STBn98a~sbF*QF{40a7@VpE7@IFf^hIMuKs%Jn{#gGhSp%oTIj(xGpno;|EoJYmeEg#xGJv zUV$Dl=W7(WH_)jUp&uW{HjBOR13JfC_rYHN`ZZ7nf#x!ygX)a2?8&;KwTx&5{#}`3 zMxiw8K*i?&3a-}P`h=b>yK!wSer~^Up2l}o#{wpABs_{oK44%S14&mwiELw}Amg^;+0`LABaHE8M zxhYK|T-Hx{U^IT#?+?6b?4{a)!(zV0A`fBCs0IpEF_yR@yAli#>8kpH%J>N*tv+YV zn~D6D5g)BuQ$0YE*-fu!||rqTiRtccMIllsrcdDl1ql zbG--&kdc;oTM)FfOgnkLfV|Rg>LEL)=zkaaBds?9N`QFoAUn|$ltN& z>xk=uU8lH%js~3G1Mae@DB1PSmh7R@4xSE+tFq=wx6Dv z8;@2^&kp=xxM5O(Y4i6n!r$H3ydVaDDSVSFfIkt!3gD2jxCO&ZL#N?d<19>?&l%LYw-?uXjO7pq4i*i|9)+I{sI ziNBt{_p+BWZJ6Z_XBB+s-+sVhoGzT=g_cJ||Lx+E3*iHXG+zITZqm*RxLxr2ty|cy zV0Ytfh;91AkyFv%;{_s(li+uR6BX<`2biycu^2YB-PSG%3xlIIhs^Hl;;KUsV-fxIA8t6oUuVK@yfOB6Kb~CS8Fs_+( zllK`=*(EdEzxJzhU29Nld2PhP#f&#W1CD z8ZIh+&(E$e_j43}Cl?`@xDR^TbpZF)^XX`uE@<(Ck?O?s~1dnJP?=tQ~fpI##ge+PL8^L~pj@EzLr0 z-2})WO;;tF67AfA4yQNx+u9k6b{5if96z7qVS}u67Am#8XvWzGCEgfL?8C-fArI`~ z1wNPvNZDfm;aCN?*~0_$@7UvYG1g6cyzzyUvkQCdL9xXiC-{@_AY0Wm80A#uG`FKS zsX2q6vM?6Xrqk4j*jPVdwA2&+b{6Dmk^4Z{#Ub&=aANN_=8E>I|4`^V{_{5d2`4Jp zqCe2Tr$6Lt^M_A9`)&Qv0{pS_k>sZv4DsXs>_5>F9dyp2&O2m$E_RoP?ZyIR8=it_ zyv3c7=`H6x73&`EOl*0Y<@ccc3A~H1;TBbTO*?0?rx}E`pEZt|kURGes5KI?RDz!X1NO)5gmG zMtw40(BFxB5R$$T^ewN2%D2n%KZnX|SpGn${I9b7kD>CVEWajHez`2K3zes#Jb0m4 zNK)Q>S-vq;?q&Jep>m%rzbsU)v3x|RJYJTs3zhGOcEKo=Q|Zutq;_|!WxomkYD$1V z-PPt_PZ7T11pnF!^zZmrIT~#9uQ^~r&*)-&t@^ZWe9a5|2R$_lEp#?}W27;db_e(R z^uj5-;Yn*|X9qsTOmS!)JRz``cdaMfHble~xJAEsEEGOP^J-kWPL!lKY~taq80DwJ zJT31V`#ynGZ2?(4zrfl8?6-pH@9B?rxILbOTYcGquVLHUyuz94r$j?I<1(nsg(~iw z;n~3-F#ezyZUq~s?Wd_Sthe*IFva4-kkQ%qG`f-Zu_t$suYQ^`$TZ%G8T#Q|+T(~^ zdR$F=%B4YlbT>!>f!<`o+cY~k`r9o;K7n*6+>hwM{}LErF`qjC zOX)?q1j=V`lkb$1m+$z#uH}0TY=1+(>(EL^`>mYSX1{?U`5OB}zGWa+ z_Pg?7$vTsvK6qA0L#-nyk)-Ah-H)VS?PQ4 z%aNe&J^7w$#nEy|&dcC{J{}J}* zvUdD)+xYkWulb*1^8W@I$loCRr5*q804ez&fA7DwPcxn$2>Wl8{FBN5jt{K%xuK2! zx&Jl)3X^||8=2z84No9D5x0o5 z6G!r{mGyS`hQx)SNom3kA{80=G_N6?PsqOW2j0!*)9*d*4SaTP42RyhzlM%BcNu;I&tWJ21v=3Wo?d^zCQNLM0|t7e z9KsDr8m{e=U9V*1uhr%2A8wJ8MdIG)rZBPoBZyKV@5CX zuAed=6#l@5#AYgIlejE}+e_RbC_sY!0L*?(amaq1fgu4Nuk--j3#6-`GD6igSocAR zyISFn0LQLJZ~q`{ydm)zP${_M{9-2kmz`Gsh>+#+ZYc4Je+}#L{@{ThzlEypYcqOF~MJ-otWy|2AgQ&!R0gX zdd;{Egu&-QV?O_VwmZ<-b=4l@3K#*;0EC~mKmhP14}Z#@l%vMmkir+(N#SOQkq+bV zeH`s^Q9tmLzperSY^T#$^aguKvWWtPc(Vq9sRXZF8_%FHo1+1bBQD8?S04i3>_DqA zA6|i!5i|$Z%QeP>(*0OwoM31*PD*Q|L{Boxu-9RLG!53%8xpjb2`t zqt{h9LDpN5JqogrT>o-I*)1H7^;3Svpy9X@17$OCb=7e|zXE#@O{WH%*CA=}&Er=4 z8ZW;t0_j_>Y_{et>!@%T;c(@8Y z?-p6*dPvjq@)H9ka)U$;CGt@){1^P#xE5xD2*Exg>P0sr>OHYiwwo*I4U*|>wLW?~ ziye~P1X=tdi-Qmx#dolnkMkfxZ&^Hz#V?__Ened8{o|m4gEV1b71!=2yGKSLJ;%5S zEuMES?-OC*4Ct`39Dq=RX_t@maeuaMGDcTxRX5|?SK&8r8U3X+iIgTnO0zjy*l-r5 znM`Tg#A&GgHOzhG&k%_BQ+v^7fum}b%=@Vi`EAYee(JWIxwEIw&73!HXV~pN(E9^n z{H81JIb|V%&fmj##Iq3aSq+qt&t7hK2jyclZoX%-Eo)}Z?76wVSzt6B+pO8SS<|x8 zeLKT`P9uXrn1OUx*a2K~vu4iiwXT8Gg51AW3 zuO4Q6LMq5%K4HZB)5Q%al^ay!ITW{iY&?lXZCUrI^d(CNc9eI$Q27_EiKV#42WS#+ zL5Hs?%C4uFKNwkXE+zf1h!2qdJb5!U`7F_qe#8m+P>bVNZQ&Dz5MBM9<(pJ>SR8hJEoIBk(;RZM{YBL#@uX9a&h(*FmO~J$R(W znDH8zZEU*q0;SFvsm>xeWiCQQHm0fi3F|Nxr8><*2l0$*J}Oc)q0Sk`0)QPx**K1} zwywcLYoTA8J_)GP@{-XXiPGn3<1a6_8(q{-{vP1B{pm3Xrc@|Jv~BaJ7cg=W_NKEB zL;iG&a3bMPzrJYkr|U#dcj-^7Pz-;%5Q&nvGk@X>XYP$@h;mYLzO6n_?5z@jtC>`u z75F9apDMiaJ7IMcP5h;C3zX(U!3Kh?`jr?ehm9{_m7_bH^8NgyJHYZaIOfqEcK1du zx{mJ9-5ZD5nDMwb=8BBhz3~cUYCk)xdMsHy2exf5A;bDLsHoLkWGkz_qUIvIR`ti= z8Vq2Cmmd6=GlU0o`6j#kLt!iU%3))f5OdvKsW|p2y4tu+ z)EDoXCy4rMo2bo(v1mCPkMd-d$E;N@XsdGgC0b@}J@ww8%PK!l5+eQx<55J>%EY!R zPl+n-&0K!aN*mrsfI&;o%W5mFEop7lHmYiaMKzCm!+o+^y{xvtTJ1$BqJAebOI0&K zeRPM{z2Q<>ZGo(ImbKdTZPoh7Y61QLF36VJCKO@S;56XGbEXFRQ*{qa2@@g^quHKZ zj|e)%yHI%&%SWG3Zh2oHPa?7T?$&O~#LG3pIj@H2WaC8;yuOze_?6vV0CBnF{9qT- z`t_QPYu);h4&y3}89B4MHx!{7R(n`u;SHqcs^{*s!=r!jJqs(g8lM~!e!bxqxE=lc zx}CJ!VPiNLFj(*@uT%XDr4JY(V5DXQj+yWGiin}roH4q3=;NbDv(t0zd~v2Ypttl4 z&2O6p&R_c+e%tV&d(*{e*!`$ua&+~9nu~3b)%$BMwvVoUf0sQva`b4_c7W2^@>ZL@ zF%At_j=p;NZaEHfz$0Z3;x>@jt>WGs8b`*7f3+#g4C|=t~6dRYmKxT)H)kuST(dp83EuY9V_%={R09<{w zueSytBf)$G5YA3~i($V1@`KHk4!3;ph7fK{WFzO_&{Q?iJv<{Z$2~kdajAQFZeoRd z_?E;PP`ZcTmDnKC`w|;P`bgqdkv^5!gtVP=;5Q6h8eQP8mn&L4uEvcMY_iZ`2Da?j z(0O4OXR^Y0jPoI?m|E~|HEtt6XB+|o3Ucng{(0H$ShJaLHpSr%1?4xVi5)7i?lo?N zLF9aRPImoee#FwiJ5(K0oSffUjpxu^c%L?vHypcX1ilqFQt=-|;^S{Lcda?^pmId^A7nSb(ojXJ)4RGxB^H8QfWEleCI_t-7pa zZJzqZr}(y}%BPH-h>uAok518M&GydC%*j4i8(JA1srj?!<<6dau6KH7t~Wh9D|1$E zURH)isO?*_0vuYY&GqGGYB^cC>3(g&?Ckk7Gqr5rtm*T)y_!8gH)nos-ptt<^Rs~l zQ*S%eJc(N{J1ad?<>#F{E7zYnFKb?&!d8kFD;Je48FeN6PC}*JvS%QJJXk)nbHM*Y!~=(o#3Zs zcEw4SbjC}TbjB^+pOu}FM~-Gw=#931Ay6q@GH)(?5mfEH3ZGfW=gaZYSZx}<9$!@G zttw{+@`K|=-jziw%U7)%t#NFGMpOFC3~kgX@!?ve{0cdS6v**?@=+^`s#cU20$owQ zs)|(NTe_nv@~c+hFJk(ttI9<^Z(&I#Kb2po<%&$aKSfzy^y&89|1;Rs3SeU?35}m zzFl5ky1K}#zS$3d^A=VY(RHMyU>lRZyt=dyu z6ZI#tE9CLJ_MhL*O6H#$P$mN7RnS3w;W=qI?8WabEB_klW!uY3Ru`3d@sZ^<<*P0< zrSle+7gbWz%A%r`)N5H00)ehF7w9TluWCg}r8gh{ zQ76B$yv&pW_e1$rX!Nh>KJ{s9>V^0Kz364^#Og|N;jHMDBe#+qMteh2h8T9mkF%?) zi<({4#@{IYy>X=^72nB*KK!tC{z}=OBBqS=$`5lV4OhJ>hwMr6#W$?I_?mZd5l1WR zCqM3O;S>rXYRaGG!z`G+=FvBP6MY7ToO({&V_*Iebw5^_wkS8j75GV z&JDSnndd=irUI{T|13@}@;u@31wZ{Eyotd?y3 z9cS=w^)5kq&ZDiZ9tXbj{8Vcz|K61M>DJcuNRywz-6o_>NIyraJ=@xP4Cz*+{JTgE z&$YJJAZUrkgiACgsprt@z_ar!{ffKNRyGK z;ReNGq|Hb-Bi)a53(_Mk5ZA9wCs}5U{#v$F0TPKT=9zj}xl=qo7 zAg#eIfM(*6I(6W2+cFNR2kA(pIY@JnE=9^OMpYnPk8~^2yO1^^-G;Oo=_^Q+aVz&2 z(ln&qa0?;_sTXMjQa@qbsIEe~6zOK96-c)rZARKmJZ{4uCmyLM67nHUM7kg8LZnOa zcxMCB3Zyr)9O->5N4lSQJTy9vve1Sjs`dhyahGSV8PiwWcPjs~PnNS{Z#^aI#| zFw(>rl<#Y8%|Tj&bUo7jNbfmHcsQ>(xvR^efUP zqys$AD+qfat@#T2AWdt5{g8SOVm#rYP&3jTq~61@6Vf!K_aR-1v&>dsWINGoxYf)O<}&6_N!W@w)IXu^ zjF>e0)g9W-h|>HZPea`*)O`!FgLSW^+}BX|dgs)bq^qg>rf^?OYOM z$xsO-AtRg}#k*O&`Kcqeq#&cm?vZ?%8msg^@8HGyp2va58Atf~L+wBm6GSUIvcR?-VV+*;J#cF+I6vqbfU5#d{M}2mb*(+CtMQZ( zQ{m{z@q~fiVLbVwlEG^qc-@0Jw;Xx)VZ9w8>}vY$Cg%+F=Gl=kUU)J}WE>LuV;=q- zb*yqG0=M6SO9Z=A;PwG`n&N%6J)6EYC&mkTrwfjv-zJ4;=*Q3V{=0%jZJw;8yD zC&6zk=$u$Ln03kTdEhh)j()HgxZ~ir0D0-ZGbrEmn6!)JIFTv~8J!r2$(H)$83)`* z3yw1KZ;&Pd_ggx%?Of1tjq@<_vTZSz8Nt;c6$4hZT?5*;EbXxE8-Q!J;F5v62e?;& zJ74isF&blelXH4Z@+IiX(6qooOgZ+U&N0-vM%B66uH>k&&j}d;a|qkiu^w8Cb(qzz z1Atp-!BGzW{l#41{u?<8QKtrVF6}5s&cz`y{E(S)G=TOkOPj>q1zfWQM>)0u_sV}E z2j`cAsB;W;u0kGjUs`n}gP)9*x~^%ZSa*uQ&v`cI(3$iXjFlCTar#bp^eZv?*-`dS zV&Y`j^zD2FY$#s_>egUQYPH=$;MQAkl(`hRwZNUM?O3M~b?!o)8<4m766C6N&cqll zksU850Vu;d{M+6htbxrs9el+OjoOX-B-GKd{#=hbV!ULB^v{#yS*Z^?(vAl$*VrdR zyA-sapCs+oppC;?{bcy^I|11zN&7iyYfh4u>+eTSl6Ep^jgz2dpYY4F1F-)v`$Ub! z-|WXNsI&RMtkaA--~AVL*q=^}fu-0BoripTAG2q``P#o7j>#orvi1dkeBv` z8>|g2t+b^;-wc{9pb>uuT&x3rV_n_oa3t6}T?qK29INax_c*4-Y{j_^>+&YY{Fp|E z7{|xaMk@Ap8$xo-j7eG$lRQoAG8RjL(&hdn3H!P&pw+oP74dO~oKL;>>6QSAHEC_T z0LlGDA?j9O&-gdh&IxOG?o<$z+O6DGyN#&(IqE+BTXm~C)I}Uj1N}bKU4gyn9#vQE zO{Q^glF7XZ+T9dB1Dl)6<+9DXC7CAm_F|vv#D4a_l|2J(E=Jw8s4M7wfa#s3X3Wf`c z;|&EP#K?nAg#$xU9BHL8#m|1kVgCKN{tDJr+E1nC-;on<0Rd;0fB&o8-e}m%@!(c~ z$HG{dVhJpcZK?p%qd4%uSsa)i#KH9;55)!xF9B-ULW?5@Yeyc6t){@x-}-9yisBI7 zqhM3cCRwa0vxNQ@*sp4P)`%bN@@Q4C+3pbq7puYlqk=EN{^~ET-+qW-(XYeFqNLB(rGH?Cmwket8}$W zH>va{mENt=M^(C0rSGWpfJ%?5G<>Y&+gqi>RC=09r>S&-N{dyxTBVy*dXq};R_UWE z-Ko-dRC++AM^zd=PPMPnVJbaMrPEZpK&8bhU9HkhD!oaicdPVKmF`sOJ1RY((xWO3 zAFtY1=`fX^rqXFDU7*rpm9AFlCY9c#(z{jqs7iOL^c|HRQ0Y;XhO0QX`3@<^^z`&e z-lXZX<`4I#q>N6%Un(7m0y&E07~HRFED_sxS<8BzR?FyN6U13Xs5$U-Roq!kuT4LS zkm9wwjw3|%AaAeJbxKbpx+{T+E&Lb=qMpP^4I9>HGzsisx8mmvQFkDCge47Nof*zB z{MB@=M>XCgPxdh~rnJ%vlya{?9l zBJ$DC;)0f2(Q*$WqJDrrVSD=L!N`i9zDgwx0&^c z0Q7M~nAdbJC-m_G(6z{4fr>hts0j|{hYUiR7uC`9`tAVwdVI(` z^$6G@qMsCJ$`L~Z&|6;uQ$!@j@+MrI zHbU3)!`@(vSx`_fh-D+1J{^|T3kBfRl63w2?*9Qz|DLeAzVuX)=MO~nA{V7)R}BPL zFuNdZ5LFh;O0CRRwlQ1YJquf-A3RK0*KS6p^x=d!VX^DpeB_9NYq~gGrdZXC275UVD?W`@6Ghro`Qp>&E6CN*X^d=1XRjESc1K(rKDn% zFy~D91?q5E>nX?3zsz~iH_;vke+}9HUI-Q4qbEuG*Y`laXC%8C-Wweq!CSpzyhXcZ zkLkH6ijECqV@@q98tby?UV>4S8~K>)F!$*K@ajH<0{0{VBMy6(vWNY!t_ zsBupbK$`wDba1B$z^~iT32vVNa`d%iJyie;^;Fo-ozZ(7+Fq(>z!dIjv3%Ca=d%F( zLh1_rS+t%W|7(A~go?Cw7G78JJ?-M!(I+oKX$mQ2@Jzr{55&FYdVMf zoNrMVU8yC$3z?8c%|YbrF+j{hL-s;wPxpuPA&j3hP~QEKF!I;*3sKVj-;qfuuxmYj z22Ko&RKUWmqqRsPqlp;$5o+|9id-Zw(b>cug6s5HKpZd8ONn~{ebM6r;&_R!BJLDi zdR$5zFVUNE8IlfNJUzeF-vXeIgL*x`Q(|$N?)klY4SLJ2m$T$(>|JmXr``kCobACaTeCRxTm08j22W{cr1NoV}~ zuw&0~Tdz;ZH2y~D+tVe>;U$n>yhIlgw+=nj zql`FSqSq34F`S~u6~yrpeKjsaS|DXv>_Gh)0Qz|JS?pkCgH@o8^+tAs3!!UnVl`f( zci=KKjAFb>952!DGRNMH{)9QslhG~AEn@C_=E|9CWv-UF2xuNj!$v}mgybWa{>A# zinsZD_l+%KJ_kzm?iYJG^8;b^o`ZU@D~42{tDQXu>(2tvUxRD)jCbFRyrvCu`Eczy z+(DmkdB6+n8)R*+0$AGa`a6i7u9xBb;jU9rsJniEWxKif0!w#S9^AaQ>u!YNfv$4I zka!n6bGYkM*m$%n68SN%7H}Kus>Kd;oQt2jSnPTPBkO!u514wH>oK&r+{GX1m%2Uz z=MAnOK)=})0Ox?q2N|z*9fVe`iT#lewp`G-5^G?5&6{`|m}_Z? z6}W0Ou7@y)Y_8i;+wPhR6FFUxAk#Ik$Jqx=qP~W#eaDMsc+?)S>pM#Py!hR{9Y9OLM0K0x;Jt@F@ozC;%LSEfDfb3#$>9^c{AE+Yv_v22h#Kn0I zE?VSBTq6aqXVDM+6T9m`M6E|Z4HzDGBl1HoW}gihp}zz`zZ-fC80EPHc}?fo7%*D2 z=+sY#y#|c&tRpsTtUATRr2(7m?RVC6cW{ouL=W5nj#0o&SL zMHppHS0x6!?iz`X^SIte-s^f0<15Woj%!Wg5nMI6|3oN0=uyWAaEhcflK0@zwwKBK zCR{WZAK=(r?_g}%T}#mmnB~~%F8(IW=1K<#yX&tol;--0c6+^y3sq?9?OX?E}m;( z?-aXOoOt8MAOd*Lw8zdub4gkcN**aLY(IJ)E>f%bxR_c1uDf6*&2=06%;p*bbK6}r zF~+encR2#3wnPBvn{73gSjbsDaAR~oJkS9iFx(=`y+FxPNg!(BbV!{rLX zxAX<2u<~VE)JI6-$2&d&zY%fqr$sCSFa0xEBYvXJAff5E!=&-40>DNOoa4_BfJckE zAO0IZ*^z=e${=UPY^E@f6xWq>AuZ|~=ojyEl(kW%$25^@nQ((IsJ^QL#&ftxWH5>S|w5T2M zpZHwID{VX$#7rfRaSS0Ye#`+*4i~VycB6*F6#yrv>qa;;BuGROzr^7|1*u=c_!$1l zN7px^!}*>8^t%^5o-oldtBvaPm}^K?3n$QAuhT8GsJ}pjgvpL76pTnpI5UE3>-WI= z31{^twWhxcwG+~mq36Rv5`5Co`uzl^hO@il^kj5ZLb@pNYEhT7D8q3pIP@UPs7s(g zg5P6@=#eg5P8)!W^AM`a3$@pxdIOm-!;?g+QKXuFB9&A0|A?A|nGpw3s(WGfgq)b8 zLfA4&F-HKpz7b|jm@5E}emnRl%o9MIehL-L6@XXY3p*#w_fViDJq*T4SSWN#*5RF6 z!nr!9hg5wJx+dYgSQS}f~dPps(R+t^Xhqu9wgM~l550ndAzT~IM<2r4c1U(nQh zgEYYFM6++@Wg(b5-vUmHWINF;j=LDHpZKs4`a6<~P8X8J@^d?hv!t(hdYNTHtFhOC zLEP*i1Avaa5-r4fFi!d`wfA@!?5C4hI94`u)fDhanWJ}2!(@t$+E316CED=Aqoc{mgrqX z3rC6l5+QuBL!v(lQOC`I27TTZee@l%j2&8B3|a3M*uF4@lRn~1;$;*s4^Xy9x=%EV zBg^SyLzo+j%Q+O?M>?s)EnqqLfNXgQ(L#r1$Z3&DtVv|-#TbHvkILSwC6(}D=CsJ~ z(Nb&;0$HE&(tRHw0qrW?q8dbMlrMsB^yy<0OXQbGCCa2f^3PZFdROWiDiY=5BI1bj zu-Oo6NWN?<3WgJ0Fh&u|hJ7FgcmlB;;3Kuz2hnEYAEmdP9-@Be6spI<4R&jlBacu2g528|1h!HA= z3mGGV;xaHbh7t5imW!dm$fWt=Ar!%%VCfK7l z5p^F4&pp89PmUgKyA!y+c$0%rfA-l(?UbGH%O1K=$JQI&()Zt7GWO7iQuuz!@RnFT zf(`b&4tD9K#|Zp8kh!-mrv83+G7PBv*RV*O-b>IFBAE2md;iF~rO5Y_dF_<*QNgAQ z@35VTCP#7SWYhanqG2VPLl;XDZTLFCU$_Kab1L#UghEJdTjp`C$Q8&JuEDIxB@wOF z{SsWhbh&sV*M40_PyDJ)GhBjWB1#f)4yK+xPe%@iL)7}-70zvLI6sd&8rYFI>2O-N zalo933Y{kHrpKABs98S6{ppLc=$)~l2@fsgiOF)i<5_KOF8TzjPk0>OQS5u zS%7muS@lZP8bSA@nCpn6WnOhnV2t6DXL5<@4dGsQiGJM=crVWGAELXOCC+|bPR~_E zRoWZUp!{{lFCeiGR^7w)V{k$X$DOwMPyD*0Z9#Qu~VppG5ILamJjD zrR(hycN{Q(BN6i>9IH{{-0;#UoUm$z_E(AHBA-vWrrw8f`y-^bPnZbVaB!)S2Lw$< zgI=KU^gw=?8fJH1x9zfU%^pxCH1%IBCBoQg%Gga7wmT$|?2ib?>SU~LXpTfij;I!#-F??fP zV(nd{++Fn)9T9^f3}I3x6tY&QYEl(-uZ zK;vB9$>)B+f5SNrRkht-6b88$a_Q{;H&O5^E4+^i!zIp!cKJkaAmu$(;vCm)z3jX< z&p-_vb7i94G?+fJQk&-zPlC@wy#+W$yBi zC|u0X{{->haf)&MwXE_zwUZmdLFDJp7}&JmO&)_7^@{j4*pCu3{RtpQ6gS zifjR2&_p=qgyQ5BRm^*cjvlJhP$2x+M9+84)JS1q_!;shbdLpg^#AHl}=3bI32w6zbB94$$|(+hUtP~52|7j51vr~4iQcUDu5f*utWuL7aNwT0Ks61wa!`#;MuGI`nT+OcuXbCyj*t9 zkqc#OGi8F#*>85v^6;VT96mwfK1OuT^6)9_oIW_a;}o5%6S+a$(9KAf`J{vZA3ur~>L~5t9eto@BU<4L2wi zt7H>UfqpB7(yZ`gs;~kgEW_#G0GSo;rwSW!UQQgBGPA>b@OdEjQ-8wQ6JL4I=13LZ z2h7*3hON@(O79x{E0$QVsYY$uL^PWh9{$292y1SvwSEBcH#qySpxXdodSb9fsucaSEASweJcz~h9!1Za87-1N!ZA~M9R@I$_?rDmMMWFN$Pp`- zHDZ*{LG1Fps7Ts*!RrVpQ^Lh_l&uS;09i7D3EwbHI3;{6O*jhj563A?I3;`rO_&b& zES%kG!n35g?AI+7`;;_kt0gF&kJJ8|YJuMMXuGgt?o+N?s)fC;s3>RHj|vd6Gh%rh zw)s-poas``@{qLm4_6&NB78Wd-GC-9!YQO35$>b3j{trEr;v7(DXrX9jh52BjpA2v z+Ap3iS<_t?fHynp65s<0Pc1G|cKNUM8PWUlWH{1L%1>hDFemb4smOExDCgzy%Y1}c{=8Tz##2p?* zcec&dA|68Fr(rt5nheR3FCFr*ZTVn?GTWzNlWDf2E=~Iur!d=RVd7>&H=WN8l-a%r z6Ke^s9=NnPAi+cw55g%H++T)?6@}xvr!I^62V^lM>_hn(IAeJKA{Z7yfeHXG!ioLe zSCVQ2pc?ov3@ z-I-JwQ3^v0WdBlR?8r<>re?X2JEm#&?-Uiga!DIAdk@8o_l#Dn+L1Tgu+LTn++o}r zs=(DurH%eUA6l+x_$Z`F(Fh-kT(9;Q;}W&7%N3Ov3I9}p@Rl=rVwv@BSkqZl1cOi} z=*?Lp(Q^YtVFL4y!yXUfgn8eS=H=@$@8JylTvZm_4_N@WBTe#S32aBB;6~^?u#4o= z(TEX{dTUrW@^4mBiC=f4mTgM{Zs3TxyEW`I`cpr6ZEt$+Q5HS_rEaID z?FfsRPD!qN^s??}+WC`e@o3v|-94AtYo^H>?7A_i03B_9&3?T?^YPOxi6%LF4x!k7 zm!){+iN%juimyJQ*psOSJ8w^-y$HvLZ6Q3up@o1sj>y--u&->>3r+u|tjsWQSn-xO zcp@CTLJJVTMJ@Zg3Sb|`!Vnw-@Vu}Y)}SM99MFnl1=m;W!Ojy%+D;&CS`U1|O^bNa zgB^>EAx%n}ZqkbA^9Mm8`uyH7u^;&iJ@hfoe)PnD%073$Vo^eQl~{j1X)2(dk&8xd z535nWI5EV(&lvDuA_@mFX#4`+-{XX@{8jo2AG7kcs<7Lvj{KMfP)$OWgK)Z=q$*oa zP-Q)vVx+w(Ow1xxU|NB*-&t5W->g)5stZ;A9#SR38HPn-m{^-M7E49MujY`oZ7Cb8 z8Yv4K#xZgO8rX!>(M{Bv%7^^>0KY;U@2ixDiAQGqt?D40Vv+xcFty0n?v(t+oV!W| zQBd)vB>TInaXvR4o-F-9E&MZADFfSE6cML7pCY2mk6tb*ICGtA!B;9gcM}`iGfhw{ zFhn}UjX9eAQbo)4*qw@2Ztm8jg{v$rJZWj+VM`1Ar3~Gdn?dYXR;-mmF_x0WGnNLN zDW<1$)FS5F4_FwDQKgDeLkC9RSQyPbfl=QTvRUf9wmqY;Hl2k;iHeje^XE8n?3ohf z{2$WZ13s!M`vZS?HIWAR!45Y9Ika(J`bE4M|KU6bm3=ZwVGa5m3=} z6%@sa4P92TE{lS_uDWYmSy*x1wfw)|bMKpZ3FYtq|C>*4=G}JgIp2HEx#!+D^TtQ$ z`IdyXwc==wkI=_Q5W2!gC>9#!!)Q+{LaTj*PSs7l9X1g35h`p+=s-(CaWDG_wGhjQ zxfK5WY^CvWRSGLavGOw4e+IH*R%g4XQ{$h8Za#)28h?(PNR59V`F%(od+P4AMGVq% zQI4@AdTYeo+Zl&sYRm(gv*?&J2k0>il_~!hwEyjmS>z^g%oI4Wc%+Wj@yG06uSGFp z{sa>Fu7u2S#j5lsU1_f8w|hj+e77Iv91hz3k?ao<@=S0$F$gU~el}9~#;P)uot}-} ztC7DRsmG%EwZ2JjKiy zWInvwyovlT6tg}9qF z1r;a{>wsWT;opR~gs<7^4W9LvK^p%t3jZv~H55tk&r$f#NB&%-o&?t;VHt}6PM#~c zUW?i*kp$OAaly|Y39j$rVjnZa)dDB~MJAbD1=o{N(i2H=C8PW~8tYljCUM;j23zAA zW0yunZUHX?QNwyo_b0wzs{XRAlei@AZC&f(E89jRrW&z%rh=)jZV`p)B&@BXrkSps zGx4Ejb(Df>J!;n?2~2;+#WT!M{l3D*7t9c*&RB0_ zk&@X}U>b~Ee~{RvZh2uL-QrH&{Z8 zH|a{K=}?XTnQjK%#6l3|#{=zcV5g6pMEo})e+!bhi9T-sZ1jEu`B#v{O=Prm6L;uQ zOyecs+|P~q69y4={v16%L6Uj;1s6Xu!+Gio7np=3^TZ#O7=e_`{xVO~k>jWOWS;mL z*b*e`c|8#4iFW{7tFz}OJTSbwB!AKUI6qreKcAa;PuF^CtlWer6y3!4y3euPgj1~W zRc^vYkKWo%YtlabAFlE5t?<7Xa`A(Sg8#`1|INs+M-u&y(tz6u7c}4_}W&xd|V<%1unt z{YC#FU`zc!OV@h%YB$jVJf$j_w#`)dMPcF{B5@O`?ip0mYr*C!B!MZ_&5?$zTBUghYFkOa=OOdRP zG@FDe2@JMYzZkoPn>YfdZ*_mdG*I>TxrrZjZ7Z18z$v6CH*xL}Zep_sQ;ORDtOc9= z22;oCW4MW?I_(LJn9t!Q<23$LRcX{soHSD7AE%ej&sKl{|}JgizIHM zliN8PL}H=)0VHt~@h#oNr+O6An1_~$Zp_!2Xv}DWpM>nro{T1j;bH(YoTr(%n9dB{ z`9-)`gp|zwGEdhdcQumC(;c|D9mz5alsKHH$1q0gdGg$Z2ZrY+I_iF$pSM&$pPT5a zYrXkVZo(6aZXyYwQT31ICUP|ejIxH{pX24

!B!S5zVHsBdrhtMeArU%_BruJ{#UN$~Qwc84W`;0bjf)jX$?PiX_W*MD zAqh;+jWQ6Bf;R2Ftubz;8A z!UTMZngaBggcR6^OeHQ#nc0oZg}7MA%u~p0#Kmn$$?R|aQ^VXHW#4mF-ppv00irgW%?)0P(vxxza;@Ng~GIMnyQ$o z-msDAIYwjpy~6Yfi1GlEVEVnn^zX=jjMS5Wc_b|36%hHCBUr`2_q9P1tkQ77ulWd8 zBXGeRdV*CEE+!!*v#YgLGee1f(GnermEX*9TIhpJ%)H}snu^9OqEuP>w1|?%%)qYn zYXw1pc8fDvftR_yH`G$h`fH`HOTl3nNg(*z89}w$iu~P30)a=uG8UlfTLr;iQ2RQP zK=36lK4FIHVs}s{2j{kTO?pm<%yWmc0P@Q0;O5;*BmE*x3 z^K4hBgMwfi(BdoVA;0q=c-@i1R|kjJ22#hTb<@@!a%I#*c7rs#v?>=0UE?P8235`; ztFhhX$bR<(5KTg|UjsY4oLpkdy8)w+#6$ksN#sPIhkON+c*vJpddOvZ6w?@M!l^ki zZ7_&f{c`lU7)j<}4K8kEhI4QqF79E5bMO~jyo!{}{?-GU9OvL0oG%}J4#c%V*FC{K z5Ba3-eKZg0F-pn@{pGUoo5xM2lV7d-9zp;jp?%r(|^wlF_8K*$tol0Map>_z8U{!#N3Cs|y^Kntd46#~;i;Izx+12W!xuis?VB_dm zwepZY$dta0)>MwGufM^+KdB&SIs$@m<23|NDt&zb9BxJu2%dDZs8;-P&psr9z$0N9 z{LIf&3WDEI`wNmlz>fgOAqfQiaFM|bA(()RF-XbmYQ63=*`8qISP(>moR(UnAo%$A zm~1~x>UIUerXx&tlLx_ewNBQ6!}E|jZaId@E{&ROAuRtstzLRAq9*(Ae2wjW&Y5(^ zSAytrBzrX2xzEWVw%d@u7fDR^ekUOtWp5$B2T4qJYfF<&)p3MrOaq1wIx#yih?wlx z=LFCO^#}~0hEqD2cF4#f_o-AOZPsS$$E^E z@|T+Ov4WaanvOErT^*o;3e&hFmY5$WXiPUNOs7MX@koN{W`*gc$S*+>ef3CK#{Gcy z9;L5$q4o|W!DZ8^?m6g~=WP!Ec(%@x0b)+&v<0xzJ+9OZG-c0r!%)}KO%n!sRJX9)sB4PgC9zYL6ZB7 zH>$D>~jHWR2BTx8eT(Tis?Mz)jcX{M1DZ1O!o2K2O~|$#6&bI=ORnM(pL3 zi#p&~$x+{iVRWGaz)KY}DMZ9vp&*g($*gcrBRqW=0IpQ!T?x@;O4gJ4^^D6E4900= zakfyeXvZBf3*0lvEYv&l`Lx+Uu?R_Kp}|S!FpH3HK$2N#R72c={8dOY3k%gy41a z;61w~-gBaOKd1*J-Wws6;5}b!yQz6cM$N4NF1`RZiAa63RyzSK(hILvOm z5@nO_+)mX)l<6QNGdo_1vH|SfiX@_pSE4+E{5GVHTYO`KM>r$jcc-FD5*!OZc$}lg zY&v30vMyuhsHNo<(0X0Au#Ta(03mGYbxiFK!t4GS^>L zsK-3Pk^Aah((vg$Z_E>%9FF-1l-+^UolYy&k$cdeA-@wz%;rSTY?j|;$jhQ9tkR#FgkW)ks4ZV|$eFk$E4f`&yF9uh*okJ-$YNY1ybG(&&J% z(Z5?ZS`uj#r*S3cDFDaEd2^&uUtgn9EgRh*X;k2ARNAu9pS?zLb-qTvp+5E+7%_i# z;7_#0VZGw(YW0Tc>w?%#RHlP-3WMAWk?N6bJ_4eHGn&s5xf}W0kz}gkoHl%fA3l63 z{-~4kJ+Gm9HkJ{aap;P65p>T289~=nqU3?SY$OrIRHDp5{w$=9_-$HwaqQzefpSd1&$b-$ z_B~=uc9$`Kw&kN;7l8HxZ_J{Z@^+4WT1nOVt~+=ub2C znBUrR{`&{eUgwSZt)0a&|AG7mNa6&(v*ix+slAX#63zPF7R}<9>qNB<*b>a0i~6}p z)~9+{s@C}!Frw{Pvka?~mV~q1q-*t=(A11|IL==dlC4oMUwgb+)n0$hBhL<21!))b6Y-a^C#q=LlRZ}()P~1txNR? z6k;xDQ^aGfS2yUk3yZNW)oqVZRozC3G6k{Zhf0(zUC}Nwg%-$&+I*-)c^B;MLlRLw zQlfm1{5MD)C;7%czN(H(z%idz)g#6v>oVp$wj7GNCqZN+8S@=`BH`zLXwEK8tzGQdjm=`0@uN8@^zGSB}5`G%_ zZAhZ3FWYTcZ#Oq7ReeRN>c3HT0Ll7556gM%jRA=f+RBHE+Gs_Kd_M8-x-s=SM>US# zU$8o9u}NyauGM<|%>`O2>l9z3>smJ2<27>Abt9z=J7RE~N-KkAAgb3Dvo%Q1sEKLY z8+j(iVb9p(R|2E~$e)2Edi|^|rt(v_lsWjQi*DOm zuQ%v6N|gKC@;idc9qNjxUccvw@`Ms)0obcT5>cK|qO3%I8B)hg-`L03>%oA2yBag? zh%w2!==FA6PLbDv_RZdy+wEM!_aO53bIdc3Kc-KwwsQkqlqk)f@GCt{!uv zEuWNl6SSZA#@uL+<(MZU&QC%Tz20QYKKml%&qoryzO$7JzDw!#F4XTtvhL8sQW<}N zkfPVylwLof6)mFITXbXUbvJOwN%tlaCgLNNt!nA{#YC?3bk;iPYgO5*)di7O?KFgB z>$+C0HhHbwc-=}V&C761t8Dpr^nr4P;@uH251g&RTxF**D*6z@zlkK@f?Q<}<+mW? zPQeXnB>Sx(qEB1CrZO4%Y$VyfU17_i#VX{NBFUO|r2=#_^0y*cDHUqWmlAO^4ucXS zEUVT@x}4v$JxiA>>@Gp=FkQ=S-NmXl#{RKEkz&MkF&d52jrbYhTaO?yUDtA3_pq)d z>5wkx2d-byA&G{bXX~$obU+E^%8CkAtseuU?iG;P6 z{ys7RRGILDv%hH+<#uqUG*BU%C64&WMI5?g2oQTE0aQ zV{|6|F$03Q(W_>tpiCk7Ok{=*oU3POlHHXvv!z#cG>k3`Y8JeKWDgSC+PWb2R z@+kZd=~}{nQ-qrM5+D578{+E#PwTeC|KS#tXf?0uTG4;s_`mwfKZ};Tt`C1BrlLKh zQRr?eHRN@-dV`0~o2Sw3Z>JF53g9poNzmhM}8 zoQ5c;(*AVZ)<+U-2Ptg(Am0ngdRY%m46iSVYWZ8boEUD=eK9jjd)vz{1PNl&oeyr{NyfD02jHMD3#~0nhR~a zm3r||Yg&=fai(s|H4V~5P8&R8u&|-P?arLsKKNXMhg%LELJLJ+2k9*1Nhb zkB@wZ|abKT7yCaO^6=S-N#phoA5WyGkiWgXj<>f!I|# zT#kGQNg!_H>F@zhhs!ikB7HN+5oxW%r?x`6k*?4Bg{7xyM1-*D8QYtdl zVs{1DT8h*$^B9DCL~$rV2>U-vLiMyG2%qN>{+}gL*IE$0iGhEmirqb$-6+8I9=rck?EV0@{)N=>1Ksop!ciRfz5lNi;hjei ze$^xVm30DfXnQ97G*ZXcjzRcvof01$p9j-;(~@}EIA4{1qbtqzyXI?v-?Suf=nbMh zkYs6l(~`hpD)M z=yK(3?!gU?Cl#W7tCYlhplp1Z>pwtKv4tnC9z^ud;O%iF5&TJO5Z83K4>n6I;I@An8vkas_DKZtfn9a;gWH#br9W(jJJdcZ~kdoQI-6bgb8**oPqKpM}vCL@1cBBtBWT6L;t)y!AHL-vr+-)Lb*@ zz>e6kp3!Z2tF27hs#|U3z;O5KT556Jd%g@ug*qFSH;0S|H|Vh#mOZD@6#dubjDcU$ z<&1&fh_-is)WpRuPU+=fsj=c|u{JeP-7&q{QXgEDr%2wcl)WnzdW}-vlld66wd%Sn zpV+ZZDSuZJ*SD%`4~*C4MEo>W&JfaYPtp}Hql4Se*W1nZ zXQt@P6>P@Og!9FWRi!h0HuR-J8BdTj+$Fl00~#@VA!mVdWpQJqulKcoxUxAl8i@i+ z?gTvp6rMzqEyi?((s!| zZZtKlBHfnCoh5C(`^a+*w?tQxeb>8tuH5qf;1I4d#_aQjZ?|0tS>}M^ zRp!Zbw?BjDeZc5&XJijk3cfYPS)91(Gxh2T+CkyRG zP^xRpo}}HUKX5^^H&`sopbj5|O;^KcR%LsUe}ZKki|9#}+0AP&>2Q4_j8Nr&Mg3u<4&xx-S~G^v%pEWoE*+^uXBwrVe;M)@ zv5eoHxK;Ii5&11hoyH{L+i7OvYM3hA)Fqv!0n^*fL_QV8Zr*uGhkD4r!HnmRJB%EH z_#deQzlgQbjQI|5U4i^cBzcYok124=b_vSyI=i+pY?#@Wc?f5>d=2;zll^#|*1Jbc z??D=uSZ~fzE$)LqyATbiY0dXuV#FOTQ5BJ=T?}bZ1#&3hsXbk6>=Sq>3O<_XjgH~k zVODpceFuJV_XSgaMB{$&x&|rr6-?Sy=O|(ji%rcoUC=u418U5n_~;Uv1^$7|cw|N) z1>R>LTwI2f%r2+XmypwT9dNN3#cPq;bw%bwT>OQZlaWaohUg5bU4LZ8;v$Qgk;t5b zi@8YF^_my1mKR+h5SZD9b-ym=L(Vs=VzmaT2g~B=veN}AYoBho01aQ%{pBH#%Yf!| zk~*kc3`dKvr3JntJu`ymG;<*KHkd)aW}}UDjn2?)JJt(Jd|sBdMwj#XW$CIs#vYDo zlb5yGM`OIIcAEclF&<~y=3Lc;&vidn6I=>{-E=w6EH3j1p4+GzGuUIJlXW9h^pn zce5(zGeVU|hG)#-4o^!6c;Na`L>TQwg z^L*7$c-8YG)vI(hiNEbtr$+=|>#IKORbL(v{2?FlM9>t$y=N*IaZhN=h`8q4bk8GQ z^8)3X>y{Ya%c`8EE~&xQinu!0y~tOwN>$LwTDR(Q`p5OUJh?lk{{4*pzSLxm%!qB6I8khmA?YH`&obw*tEZ8}3) zukbp~2QPe4I;;ThR^5m(ySqbY$h4K9eF?Q`r`NrIWZd4mnmY(aq z1)ngi>AE}{L&YqS$>)^J)5V;yv}kd?ul0~<@nyc&Q=-MZ#mg6eY-6!k4820^GECJ_ z;EN&FgSsWfS|cs70Bwti^}Md;vuCZ|dUYrCfAZQkVY=jH4b#=s=FiojEwy=qZa}Z} zv+6I)Zi6o8FKL`~p+aIX9UH6&p~qTSS}`IA>wDjllq2oH>ri+VdAWZ+S6#@|zos)X z^?%nHPW=k6W0%O(7t7S2s+|ot4el77;SXQ9({zTvjbY8vndQjrRJ8cS%C~j#?{HPN z1+n~5WGtV@5X&EgI_0`pXtgP4XXf1(A*$n6!W?g&!M#KZgKK0&4 zYh^dXm}thZOV^YRxN)aK^{!(&;ntfe&qD6Xr_4_=<@e;ez6hwMsxTeDqfnr)N*9#j z+a~h64H9hs8RTN@h)+#q^#3Tz5jm=mC4DX);x z&gO~Fp|ujBvnkKZPssx7X1v)^MEENq3C{z;0duH{`38K_-G*Z*Kg(?v0O za#bB3$}gLsuH#VL%}nC2j2gM=p!1vB?af0TuT|;|yS;1Si!!;}`%Qkd;+EmqAR+PR zCXL_ZyWd-oy%$OLdWIRt{of17Kg%+H($O&caIg0c@^2w^q70Ve1H067xXm+d#k$$y zH1Oi69o=Cx{u)VuT@OBD>?YU%U)JfGQEsdUp$a}t?}G8hA4TB0MO(NF3%$3@_cPpc zbU!*Mw_Inazj3Q{CZdk7YmjRq6~E(3)IvW1T=Spp&D_%N zIA2WWhICV=uy3SW>fKylaw)(hBF_xwrncV*13y5A!`uZx@j3bZktN7BAe~!RVwgK) zIrFz6djnfh*PoZI?gz-eh$LWL0HQJWCJole7m!^yQjZw9f;GzP(-OtcVywCZ^2ZIY(lHYBBZ>lTuo|s>gZ*NDDOa{5g;=NsofT2X5eBPGmjxN z9~Wgv?o*l$e;&qqMQ8W~@bAEnEK<|zG-2ygUCm#g>4GuTswY#kR!4ncQJ#VLwQfrP z9QR+HsYWK!2i`|X5;scGHx2K~lVnd!%yKQsCE#@tnWrRcaB(9ul;l2K+=JvMYC0mx zNjgJG9#^dTpJhHmS5uPDv?OwP9%K6@Sqiebx~WJ~q%)o*(ua~*kAnri`13WVF4xet zk3hFeS93MZQq@-QRKxm7m-j~bdAeL~3dYzybZ5>w7T5$0B7c(SDrpIeh=l5He;?r1 zXq|Z;Z6AnGHLP+Ujn_RIk*L?b!cU{xC-+Z^27YYL3+~+ubu}X$YrU^+Zxo2$M7F(g zZ|LfXRi{QoYiZS1@Ksr3JfqP@O-mA^PBuEzR7(PM&D#Se4PlThHQR^MvW>AjJEP`q z`Bs)miyQYKR;EA5)B5MlH_Us*@s8_=<>imEUKpi^;Op!vwRYK`p-alIK#e8zp+?y% ztnSuAb33kW?rH~(h1;+ecA}#w4~1%QebPAaGYGwKogqIrHTGc>9<--f#a?_C?x0y{ z?)2=!0%JU%Y~3`Jm)QQPH+cES(XN6IvX+PMJ_XHq63Kc%H{_ar3z`VH1rd8|iJ%>e z-`aJb(OtOm$GxO8T=~(L+B%-IcCJ=Evgp_^NakMRlZEe>wFn-!F2-E=D!X{%7l=>u(~Ym0(r-qmzc}q&3GW^8g_@_ zCz>PNVQur{t>nkYKme`%&4xE@^d-iNQqAfcPML|ruIXWdm z?39%J_)cbL{1DfLDI5roPHTH&kYw8?`8vYG42%-T^Um0!!@#33)salEFjL4-oDu+Q zY|ECAt@Fu3A|4gN)^a6i4%<>%GMFi>C7NcwfRS_}`es0sjF~*&dTzsNA z%RQ?ti_=ar+O{3l)|F;Ux4tqRWXfI6sd!?GA0*Mk(W?aF|6L?)HwyMJ7Ds z$t^fSu$GQ48bFGkCPSZ^Z2HNZ)~bwitunaKPWiJc*?}_b3aHc3P9{VdwCwi!+2xqUHK2jSP(HZ6j>3I{4HH^WD<|#7& zLqIg>*W{tzj7rPVGV-v9E{N_Z%^4m+o09vcXoTMuP7Iuq;zuUalTpZPn;UrqskUmi zN43|Yj*d*CJM6?D1)OOg3?N3R$!c0+oW&0|ixTX z&=Aj^3|*ZmUEx5_qQWSoiX#AvHbj7*rQwiL1;*^QCS@ePbEu9uanCoFoAGqSXU8hd zL-S6kI7f3J0@BBZ(jlT^_LP(~6r76|5mnVwP!i!qCQXex&)*OaH;S|o&OCE2ixlhm zR-MbmIyDklFlovBrnt`=wVeg^G>rK)zs!`H4s=GwtG%&k;D^o!Ko)Er{@0;B-;lwX zFRxP30|5k5F;Y1aPYCWodKT$Tq_2=1G)_da%v7sm#xRr^@{iBVF!{r3)=*x}_Ws#9 zxN;^jJCFY^;J@W8y9)ns`wC3m!hd)2-yivJ8~&M3^4~7}3%rbfHp}ccSSAnW;N2qY z3;b(y5dVU_Qy6p*mIT}LUo!ut;h%Ln|Mkbem@)V#$M9AMmKF2gx%d~X#=qDGW-nrP z1^->ce`{HG2d}o^pXr)LjP=PN3Jg~M(cTQ0ZtxM;v=@gbxK`j$yc-N`z(unggBrZY z$0wl3T;VzayF-N=|EimUium1bl3_Vk1)A6m6|NocSRk^z!F25eH#T4fGaXz8?BCj% zZU7yez^nwfGcMXpaNWQx*O}!eOYurKKH0SrFx=U$H6$<_{a;3f6}V=VYj+8tVkYQJ za?QZpU^uflU|(eh(*ovhv^W&cS3-gt*CmkY#!PT+vJ}|q_KMJf~%e)f-F*^_3W9q-&2r2MlK~ zPS3w55LcbE_eHhqrzzZk)EkBI~=+3EzhLm*-NXg6pl0SLP? z9YaoxcjE(glIeD^=NHdr0p_cn4Xubv1_SZME5J^Lu-ZFtsT&8?0#|XF_!5MhTI|M6 zC?>mgrekB&Ltp~x6^Ds{c42~=+e4utQLJ4P+>HjE!vTBk(k7^1XH)|pw|&6wVusoF zJoHVBciW24TlKWx6z_JTa@&E?Za36`6-RVF2|b}churofP%={H$O&AOt9a!0nx#)<=MJFo&>kY&QH>A-*&kd%(;=1v?j4|D0d(i~BoX~ve6#$F!{J_8M ziNO_#!fH~p8zhU(t}q?27YEZ6<9Hl0N}aEgq9Qd#MM6z5wKWMRc!FvzlJ5qAt-znI zAPv_eIjn)xG|FI&1^8zwNh8${VJ>GLey25V%Ry{A$xvb2oO!7Uf4jSgI_8UZI11 zHs(D6=+cEIz>_vmHYL(5H{o{X0>$u6^Z-_~3&TRmEM$7X8ek_U6^9|XUAh6ZX8!-B zm1?KbuiFVbXvnVfwA*f^+jc05Msuh@z;3K?;i@sxcM7$qZooCBSg>j+7Bvs<2ZC6W zyiYvYt8Frb!Z_FJ>zd;fp@KZ*BqJVT75VlbsL^~`OwQR zzb!!Es|FDC@xVdoMZB9rKN07qtVX*pX1OVGZZ8?##90RBE0h1Y{>_b21<_*Uz}n8y zdb#Wm&OnS1f>;+J76?NT-7X#76E(puLJ+j@gny62D6P0GO{<@xh1Dh*X|Dl36S$Tk zvjmV$dk_?MXo0KoU|QlBa=O12)e>u^&QY$5M0$0jP;~4_-u)wEYCdK%J z6VeF(xUky~T-wDzUY4GKomvqu-AubnCYy}e5?E2p@dj+Mi-KWT$wb!~0V(Ys!Oj7i zvAr425g=n_GXw42-~iV=%?(an;cu!@h+xlXP%GB8Vd2`g|8 zguf5#yp@oUDE4dxR>{iZavc<_4+Da~UJ^y4c!L|8>Y5o7iaF=r$Ik3;7tu9a{av$8 z1xR$G!LvZ68N9G6I8x}p}F|I;eI8_M?X1VP|j9Dv> zN+X;OBAqw<-)Rg&oDk2zVh4DGSl)vKZ5Tw03hx=LcHdSN-b0b>=4Axp|0vV1Mk|KA zC&?q`YRQoI^IP^76XWh6ih&e_7E1^yqEcvxikuLethZ4J+fnXePGrKd_P{)z3(+Bi}NupoyU}3kd-Ih`AD~OHSBhU@l;U+X> z_qU~Q%p|w{OvIjWPb7sE#7*)@PDDgRA32g$pHubDQ}Z8KxKWhla|E`;GX%a z+m)eXe!SZiB$Dj5VYjQ@NK8jD__DFOpxh2@RMEOD(O63nhqq-MJ{z*dyFJcwlV-Uc z%26;8b)DR#k#1KkJS-R~;B=kfrp$7?s1`&kWrB<4lI>IJAMNl43B|E-Lk>$n0<;bl ztK3911=hsIjj=1Oo)B}T+qu|{eN%@=c0zRBNWlymTi6BWIo7pqa?PR2NE2o&C3w#b zqC7A%akebf!Eib)>%k?MQNLMuUu{TgS?qm5gT?em9bK!RYhED#&v30?ay{BLhq=~d ztZTwF7Np8FH^y5#yf2TmqU?QHrB!j(cs0HMhXI%SNcarj`}$F<0XKQA3F2hP!+p=8 zgsrj{F}DUMWCs4;5)<#MVyy_J<^P{KKUb5m45@lX9X<`4We3Rne4jhx|B@+T+`iVx@zCc}J-|X?)ccNmd6|dei zGO&;KE4B9&kpHi|dKgbW0@8KhC>*HQd_~!sbab|KaVt&no}+U-wieM>aoub^|54vg zW3vOnb-~ZD0-uP>POcdoBvYC-S@AkIn;~f9fAGDFjp%XbpVVO%Qym6 zZnn7~JT#Q^Ai!76>FH$I#ofv4voGWglExTmeUV9d;CEM!=q-c2e!^{|Lz>S*frv zn)Yu7tjJ!uEFIg;utB$Ua@Qfs(v*p;O&q}XG7Ek z?Cr}Kb0YxDA=|z&26BigdW8NQV2YyOpX8@#>H|o8GHDMG(vyYq05kA|qWbKzR%C~f zY`!KtgkNO0OvsKCvctgdZpPl^_fTt4R+%LI>rzd7g-PPSE>+Z5 zm?Zz}QcZsqAdazrUdj`^$35J5gznLcvD8$DkHq9M%b!GKuzEs^iY01>JFhF7I;8J1Av%nWXO3l%qJgdgcJqG1*?N^pU-2x9VLOh5wK80O%AKEm_>E{y(n z^ZPS8#M;Z33BT-Z2Ch(KfAEo|4Y{zP-%WOf@cUoIFMEUE1kLX!KEmsS@ZWzw;Wa|| z3q_c{Bly1SBfCk+e)9XtZV<8`DYEPhveZ$3B+<^^;|=lq{XNzV@%Q_bzyBji_r=kt z(%Y8gxBq`9ze|&ULXm$_lm91<%mOBNM_8%#4)+Q-n1_OqNpjXKyWtlW?$9T)RysFs zc*y~Q6}SwSILG3-2W0&m{qS>j0K^X5$bE>~mUsm?PGht+%I$*l3M$G;JZHDtH_1Vp z6?j?pUs%mza3;|t`z#w*#3i;svvKr>|a1y0v;bt8T1-)h1KBY-l-HDTh6gPP?PS+Na<&`*><2g$=grLYa z%T|l;JwTW5C$xB9Ee8|ayD=n6Bn93a>Eya2-7#nl7S!2+9(o`Kuo9lugWJhlWbYRY zu5trq=sQZcp0jf0O4(#WCi{+M7S>gs%HZ%Nb_YkqrVwqNctNiXQClOJxgeh&z^@(6 z#Kw6sj_1&$!;1KgAZuR@oI2V+!nZrPUw{D2-hx{Myi)|+cSWI-c01d+<$=9$d|(S%3Y#2x2hWABAu`2ohfE&C+dmsC64VjiMz=E$_GUF7IuyiD zxT%N_^Ce=aBizn7U!CoCo+*T9*^e&c-HDH>;fYi;ZaslGG|cU!`4*|F&2D($ypC#(ej;04|}MULO?m2O;?n^uUz ziL)FBGxR17UhG6p501CH+4tKM+%B-uKKPINSo^JuH%vfHihUnWgP{R9bOlY^R?^pT zZo()Iap^MLLj%&H%xD3CZh~_{&0gFFTE>&oj;>t=gAx;Z&J7lV|7-@yib26G2Jkdd zHvteVAC;YPzY~4z@tK%3#FS9mSx{FT4^i)x0?$$>^05ou_~FzL(R_6>gs_mvK(p-N zD7VdU)Q!TJ0UHYd0&EQQ7~&8)H4g;mu;Lp`t8HI{qadEOv^fhsf(w;E2c_!p(k<(gR6u%nX#-PcF4iF@Xc_*Wd&vxX5k8;?c7(NddbF{NUu+ zzEpv>W*N?-Mq>*U{`}71So_kYxGRBZgy@cBb8b)2;tzQGSpc5FmBSnR7IlgP^yO&h z(Pcc-w_jdroyN^f49;e*RiPX-j=sWuz>PUGqDE#2H(SByg_-eiz1D1LVrJ;$o>@7uv$EskLU{S~>l}|I10G!;WA)wABh+#7*8)sMk9@p2@I5^^V|E4+P^E z%3q)pe5z@;#Xin{eKF3`6v+;*yUn$34D1eMLhY6F-6}fI18ewW+y%rC#`oEngKM0pDGUyeC|V`8`%o=RIdOveiSzH1KAv%>Wy z!L^1*WPRB$>yhKf2f{o4-xvsnC-B~b-MAtmv^gMv<9xrk$AehZjxq565#ldwjd7v~ zW!g(NxbAj0a1%gXA=b}N3z+Hr7v{fAw=3W1=dO6V$(4IPP43Cmw9 zK~>VFI$@Qk!2!GGpz9($B0Ves!8EzpApe7%`L+fB>RN) zTwv*p+aV{^XiJCm0-^{zS-F*fJqS^ZJ&{42eE31!%4m~!3a>(V_i-#766^SpgidZp z7%W!(@hBMu0pXKl#KXGX)A6wbE|V8dKy!7&2A?m%2{yL#_=E@!i(ud#c~^KMr&^Zf zyvz`EN;-Lr_?rN!rQM$uXF z^AY&WNjvd<0tgg>YZEzyHYLh2d0KILdb(xe^ID;bs_OEZTBEeKrl!0!Tvb~Wswr7e zZiGS&;rjBD1x86#sG_lIrStC8g(w>Pi|L%8g2LRaH|Ns;Dm)9viAIEJug( z#Z_Tcm4q7`Fv3(JTDmYC<)9>NgiGoehH|ruvO`6A#YGXh@LYkq z#&BpsZCPV=Ik71V)tA>*FA0GaqoJzClT>gB*VYNuP{O%bn8--JI zCWfYGpJi0lK(ZPzg4&w$#bLaam4r*AbKcCnDMg{YDY-J{qWY?Ed1zh%hv zqcAPTLn0#cJS8W)Aj;aj^2(|jBB$xs)t4_c>KkinC{`#0>B{TE^#&)Q#mtw~*BRrc z%t*`WHzciJzx00Tebf3Z2shT0kE|@ODX*_8MYg2AbpFW2LkIU6JTR?KWm=!9{nPqX z6y)a^C5_?QhPskPGEapbh@3O<7Ah>r!=uonuvgWD%j;`Os=-WyLchMep%KcA4h8j9 zwGe%YKr$w8T>g~k6bKrjQ0*e{qg1S+uDU8rVKE^v3>pLtBm$XI6iz(!(+HK$F99~h zv$VQaBTMsGSX<5EqjpfY7>ygL^r#S_ao`K((1*@b`TNgIU%B!mu zKtt*qv~uN6RZ?E)fs6s@e#b)`eQ){mV&VimS_=B`lDhH;)cNBkD|1$^#+$RM223S<*4X?>dCEkq%F3hWTQFlv zP7%PGg33_A^t_^?S)s8k18kv?nx(?LX$9Fi6Z48>ZYJjyjh~tu%Ab-~6rzXF#kpg~ z8I{zS0sW1}nue;%n)0$VXl}jgG&MIbR5X5iUUqICJO!qzss_H4eil$Ei{h+J&YqOl zPxZi6|H#z1EVANDCHmVv*Y8n>+Y-o9HsZn0DP#iDnF~q#as_HO&>B164 z9u`9~G^b)L-RV=C}Fev=;=mZdcV{y(}xA*Qi#cy4w11 zgygvCd4{Ys4Z^Zoz)EWuEGWS$NMocRqBkBB`2{n%zH`0y0I0?a-dIuzgM%Qlv~wz` zK(g2t7EYcNfl*BXSu)1Kr)QrvB{V57duCoJH?J^fdVWFC)aiwxsRemcjPeEm3!BB1 z=_wQ^OD6|QES(<`_hVFHViZiGNFp6{qQ1NWB8BTq7jT*6j3NL4s^F@?AICr?FD$RG zjQ}#apeRR$Jj&gu#a_8KUszN$K5sfq4iXniSY%X`adj1;^CuTfonDlUU*Xg9EncO( z%vhidgAtP$K`2~O>5IjDN<$T@#EQhQOyU3!YF`W%(JU?!lGPA8A2?an>RBwWd?$firV@Gh%wcYJfOdr?YxG?5giX3=a(l&8Sgy)wxR5c*(ph+;&r7#jEg|Nta z_R5H{z8tYM<4-sr9VaqEY~=D-hKMj+AFi!7=>8+|29*!I%bF&hNDS9l&`=2_gV!Bb zD6x5jPsK$&;z6yKT*@nqhQ=EB!SMX3PsKtbVJS^#aamQRmX%5z@go>q?=A>K`Qq|Y zIBrafa!ZS`e#79R}1?aK|nIu@7|s z5vDA=98h^d-=YpXVEjPODls|K2k|8^Wt1V|II)5R^J{f13I#X{G%eLUTP>cgEVd|r zavtKPf);eetCf{klpxUax^dIS%~I+7^HtbF2(i4BED*;H?HnwcH`J)Et*K0tfAr`u zd~W`rz6<6_0ME51Tn5=fY9|C!1}QAxM%lc^2CUp_Z8)0NQ6n*rjdgXfVb5Z6^TuY+ zm{b&XBi!{warLGVS_d9VsbQgcT&_r|v?g3%?N{WnW5ra|PR1idR(uL4-f~`TZFPBx z3d~vWDU+z9@?bghtE$Ti%fpRzW3dE8%(WCofo)7>ou@>e?L|n)0CdJ!P5O!`;&Xk= zWJPr?Jc4J&I!^FRFgFKFil;-OB3?ta8aCF5Yi!{ICo2~y3fowrv{miRVXq>)nw2Ar zqc)^0l|snYRPAeFbzH_vN)fz@1mz2{U^Im0W1+8B4p@vJKgSoKEB15{AiGuB0x8c8 z<6|U2uYmxvOz)o?B5F{WmyM+%RFGXX9`jsRQZ|TtkA?FZE7V%Sjte7T`mD`^-`hY$ z7CI3}%kE*z)mmaVSnrrg2;xL}Cg&9vW{=C`buP9EletT%#X5s&S_CIpsT?6Tv}ILl z?ZFJwFQ@=(if?Nmi%Eq~HMnTio-ZCn?UFrqTO1=Ot6wyd%GG9^4So z!U~5`JdMbm$~`sWJ1krhz(kyn1a{o<;Ri`cY%(`{h51v)amN|bi z;h(Ul64$F~NF6egt~z`a zJE+6)X2X1VIWo^WzuQL(;+vz+1tzIu0e$eO7%W**5t%Q9kyB?BwFo1X8^yYd*7Pb~ zbWu``$x{?WEn4k(#066#(Ru_TJk{{jLdCNAQ?NbBFDk@_HkW5#AvpCybv}@tn>(Fe zgodsqfD(z{foiCbdc}D4L^gP7TamHhhVQ($26_{2R8sbK8&b9&Qyf% z-ZIApUhUQ(xr#kOQN?4vtq=8BNATGAA8Sj(AWD`7?fzQHBz6c&JU(beW6IR&IQpQv zV!hDorB*A^PM#I$GZ?+`q62DZMI}2oyP!yKy(FXx@iZ!gRVQ2pXUU~UqDYBqsAXyN zHXampzjOB>@ za0+-dY{Y~@3&Y~^bbKv%N<4#e2KYPP3y7Y{ikCQQT!IZlo&?WOGIgk5f|wDrt-}y? zOQ02iULbiCUF0+7EulK~pPqykt%7D+LxelMiS=w= zd2a4*BKusmrir7TJhc#eeSKHsJS?h+wXmaFFfURlz$_}kUWfiwpV`frl3(nd*+ouH zh>7PN^RZsb#OPz0mM4%#gM|0efKosK8*m7tg1!p1dnO{G5+3wNwgb460B-b=3Aq`Y z7d_@v*ZqY{bv;0923)NixGwVIOS(R_li+P6B|KY10$upb3T->pwUxM^Q0a?IRnQRi zY@AQoNXZ;}u(w#GBZB&tQKM3}U?3h1zLnN{YZYu*Z!*QgS}yHj1U^y#r8 z(V)yz;wgo#_$jp&@lLq7NK+V}9{{7}<)!oC3Gs+MrMntEoogM~RPbn1@7CbYqzg}; zCD4Kgk{bXz=od~oZ7O8U$(B=o7DUej3uXS)QlMthsHmuJY?#jjDY?^8S6>^h#eE4w z))A~9IQ(HWj{u{kr-+;%30FsKHnvx06^}5Nf{0Gr5Uh*j8dbzYMzET zz0}CS4TOxUn$qgVvhs|1RpEvfB`q=;4NG(_C}dQYmS*5KRA}(PK6RyiuqHJw?t}B? z40YeiBiWJw>1S|VYB@>nWwk@g0lk>1Q zuc}SQ2}-yOJ#ZoC-nc2K$^bMjl>PN-z!tB(tdDRg)%A^~@C-cmMpnD1Xl)s`LMYFa z!}&f{HKU-1kwbxKf9^h*6UV79IIp;@s=>cT{v(?;;XXXRW>)s~c&C)`Hx*SJsL~s{ z@&caZu|$F%UO++MBIJru2re|cxB|Rol-x5cj|^ygia;)KBd`puWTT6BlB~c+zX8{r z9lT+*8e&`DX%F-;K_Tfj0u? zOy`ZjK72>t+-O*a^EY&orX=lba-MZS_F3maSCfsD7I~#b7_7xh^_qtIFCB9({$0+_ z(>CHApEQ~4Y;f<^1$Vpmvfy4<$WIeOcq$48e$LtNm<9OvjROWRM-RN+?cV1?AiU7X zdE5D#O}=K6xoC2yt}~n_Yo`UScUreQQLaVsCTp8z7CGC{0G~+Rh$f#{l-&75we`uO zs7ynXpDeS``N{gtQd0bere>i6A5Wlhwt^VJ+-dEy%(IK6y0ij)OUWA{VWKbR%{G5) z15$r$^S>Yl`CstL804;uc|8U>X|0BoDfoMv@7VUcHlGDC1*>B2iLqmT+lXdYwln8K z@zl$C{`U;-TpPSA2x4~ySH+kiXIJc>+nJ##U#+g3`+|GedJkIz^w@o|$nA?g5R2S_ z*h9=6ioK>Sa@Vw7+ZMUCZMQSGz3ooscD8+=x%bT1(&&QgU}n+`O>Z+p(R zSu1CQ%ayZlosG%5b@nFaHqE|cw&M1V*-y-o=l79eH2!rKOySpAkIX@J(MIQyIjAnu z!W{YL>`eGH0rWmiXifz6=EOCL$gN3So2aO-O?;T;4<|mU3!Y4TCwfA@Y=*jSn!A%5d}{se#U~& z5@ke+#1ZFuBcfnm(&tGK`tzh4l2Lv`@_or7`_YC)nHqT-M!qf)^M0Kg8IEji;(aW* zPmPR%Clj|NpL&SA(7XD2^kP}v%J$c{dL5T12){XUXFQr9ZsgHZ- zhrFNiK7P*_ou0u}ArpO-XxuO@yuIRO|7ns@C>)l??C7hLad%f37 z#&O>3^)ae+ALl^NTYCYnTYGKm1+v>vhgSY^xAl6Y7e;!d*V}CHHpn0+G_)(()Z>jF z81aoBucrD0kimBbyF%3fmi10`AUzcPq2giH#$$B za7QXedp`BWROUSaO=rgmPqX{eCtRHh@vct27QI0Pt!I*gYCu0G6gYp*TRpz)VVV-7|ubXpeJi+M{XU=+U%Q zJz*U8^n9@=axeD$0y!BK0vt>`$R-EbWUi`5kmtS3>%CD}tz9O<7DH{9_I^UPTPQEYMH4o73Qgqd*4xUr|x}c#RIAp*_V$tf&?V2_%^}g z^>p0V=iLeh+1Ye%&RfT#bu3a59?P9=qu(736y6Nz~iS; ztBJ?z(RY$HP`h*VeZYxw$qVfcjshMBNBuGic>FR-+EL5cPK&T-)EeM%Rs@ebN8jtu zM@48&Y>mc)9a<8L$d7?YERy#VYbk*c6A3^}Bru&-qpzXx*Nk2h!DIF4TUc}pi}Whn z)Z;d;vbXhE-$Ov~gvZ7*XJ-y-cIK?kh32i!U6ZR&T9dmO1(9}`>ZY6xIlyT{&fjx@ z=ihUrC2(4kyD=998__}LUZE;IAjCuh5c6zj%rzALnlWo4c&r|C3yW@Hk+z-XJ+7hcT+`#) z*0!@}!195pSw3*@Kxp3Hfo~5~D7`)Ka}-2Sg6-@X@X-L^^wEGn3+x)#t)OnVKOddm*2Es?ph9dn22tydA{G^0 z5sT#g#9B%q#Gote;OWZd?EAAJ{QcRFXM48uaQ0&?dW=Or+iA*r66>w=WY*p+;ITLB zGrbOfmi2Xub+{?(;Vj_saMm+fz~dR!4kAc9EkaY)+gNv_w)0umKm7Tq2(5{|%|V6O zKQM^GLlKL}kAX-mlJ^s9DS;3J9_--Zu`BDCB^H|c)PgNz?W-a-!a85x*ALMp`$hTV zx;W&qA&}&;A@2-ZyEH&AV~4Vpl6wTcF=Q!loZbm zdJzSY4rtyq2vRf++A;`IY*FnXg|rhX4h(#5kWY0zDUQ*RQm7tBk>dEgMVO)#Lv9&L zDTY2Vlu`_Rmbqt#J~vcLG4w?gL^_~((@;t=bjwgmq1sUjwo_6Jd2Z;@q&Rvn z0g;xE_~!^{`9D=Vh$QW_y$G#optUL!L_B-hIO5$AVlM|r{2~`h?^z5=EcEP!MX~nE zh`q4Rm-qFXsrt3Fm*I~Mha`^;e`h%DW%x(TeKh>z;Yy&7hkuEJ=o~c--!`1~GW@mS zv=`Nm_QH0`UQl*4dpUYX&QX)}hzJm!qo(8X7GVmSwr$w{Vc>iJu>TB$6#p6aD|5dN z`)!z#;d#hzi`4ucfms&Uw8?5JO@Z~K$^R~fz26K)Ee><3Sq}p+g*iOw6%8sIG z$LJ_?Bt0SmL^bXByhTuk6nh38rY}A`Xf35!J9q=7*f4mbmSW@Jdr%PRfaVA2nGX!Q zmQq}++Cd6wCsOPgv=LH7f(p&ku{u%;)#E5q9G^GMlyjil&(yV_yY26eh-iQJ|8XIqdpC^# z7&^5a|F59N@o(vNYd5sJwcDd4{%E(yn3Lv_@i(PxNx}GAQZDa`CYN`;ziU)}Oz?*( zpQfPCrzv-J#Yp#{+NZ{7el_{Hy6fHSe0SG-n3F~xedyszMP%?UhRhwUhVfb)7?F!xQyLFNu-T+>X54oHB-GiKF5A58M0SIr&xRb&GyE|DOJ&inu7$`>9jTzY2 zZB)GDEcW4yzp&sh;9Z~w``@$Rd$6wx4rbiY7X>%`KdoJRcvaPvKgce1S`%&<+oE-* zR6__MFglA9=ymkLGO(YAEr&f`~yV zUs4nzBM%8JK@p*dM2eswWPWR{y-&_P$?Z3P%u%xU{_XYLuXFCX`|bq~1gMJNNWexo zA;6aS0|Xp^9|9bTzd*nRxFW!p@r&*z8Sq8`cF1cRs#=F$8VYwW4V@nc0zBx?k2@3x z;85H#0bHa$LkjPi)MrS|2>24BFia1{eTbxsyCXJ0iULFT48<_rGqh!>=xZ6eAApnY zMl9O+M(p09X#c%J2W3pBhbhbQLe_o~VuopZ?73KII2Zd_EQ@BDicz0IjghUfZA9A^ zd)!IOsL!C?hN)^Da(M{2E)Q7}3m=xmE)!<8EcO+W_DXCc;f=BHQ0bl6{V3UvQAPy# zAb>gH)w0-iWX}-llOZibxZzaGko^FhbT?wCPh$2Cp*|5h>Jy?feL}L( zCx9TtC~l8A7XuCFVm^yu(JWIjiart4?vt1{qHT*g?xbxc6n!FS_daPFd~Gnet_`k< zfe$q?tAtstig|^ky%N(%cw@{vRC*_7KT5V^lu=ECFAj!+i-VaHUag8*M;h0Gle+-S zZxiq~m>F>M!vHS~V19JLs+fit>J#CoK8e{&z+T}uO*0p!QlG@Uhr|}C+M_GTpNheo z2gB~ogSQRlhEv-H?*!na`#c&(eIEV#VD!oBLI>-aPWK7R@ge@US|8nr5}W1x1Cd(?!R4(Y3%;Hjbdi9IkOkoYZVZ}F zjWQqN1gMU#C7>1(8F0$uyx57C)glzV#Rj53MJNEa(aSbM1^_nG%Vshlg5HG$^yeR= zUyGLhr0;%ksfzr~`S5CUerG;>?95+S04}Vw3kzN?0Pt$TmIC`0!yLr&Mn24bBmbj( ze8>2au)x2C1%Uzp0dPVF0A>!p0SU7g%;)nlGR_M#%wAZqtN_3=VFtj=d@rEyeLl)R zoe%p@lbNEzyy;^<)SmngsQ!HZ7nFQKWI2$zvH+z(!Fq@m@$ZN@i(=B-i{qXUi z|EOR6j&>6q^|$%O{Wkx9p*?IKaGc76&!_SZ`=S4^(7{Ef>w4!1OPLazPx2P{VZs8T zggW7@aA0Zygcz?Uf!#D;)9{9&V>)vM3-ym-~-b+eSjAoIiU4|wka3- zn{r#J)GDIk%)Gp1dBB%JgToo)%4@kTxv=x2+`m!sH`12_mX&!Z1@hKXvNmr$1lT@6 zW7CvVQ{cs^DI0U(#m1bSIpW36oWBt8mz+;>+yk*`${FmUQfH=A<-nmTVSz(Cb6N># z1t(+xU}o1qWM(gzSLVRFmBI|ycILDY&?3wLnAskPRXIy?VE>XFm?9Y8rTcAZpGJTu3SxMpX)ds*G9!z*aeM z-$1C`N5ozbFCi*h<-j*d#G3Pt{lUvj+^=YFSW1qU@1n~!P38$+FfZBtJdlB3Sr;8!cPma z9qrS?@*=UXyl8F-fVm}AWPVl23nhZTP!a%c*zs^UeL*3tTTnQ+q<3Y_e+mP>Gyxfv zZNjdMuz10;vk-4jc8W?^UtUyO1faHPT~Y5Ru9$~%ZMV+j$vuJHGnwxBPDugPsvK-d+3EWwX_JEqs zB9kY{I&YSK!=Rh#NTD=F_h#y(K6A8+VT4oEC8UNkY&~^~!xr>0)gz%=^ejA+ORPP> zz8#=8#olJSO>Y#T*h!4(c9VXK_^3zZwo)$rYnxjqy;@h)PQBb-bPDC!cyfVElv#VG z^=+0xf6!7ZS@+7qI?`QNxJ$YQGSXnyW^S^Mi0cuUt=A(q^h5oIey`Dn>DT%-_LI5W*zZMbRwGJ0 zj`Tm#AC)Ki2X5m^M!Dvj{$=!br|h=nx516&x2>SkirWGxbrA(dUmgwi%cHMm_O9&s zt-6{y*N5_4-;!)9+l20n3XFa>g;ukaw^AVet&|g~P>)saMC$T1QkPbnCO~c44gz+h z?MVaup0q<$I+S)bQ>w3KF7yFk=({pWfGd-hWIHTmS6CU=!;~E)6f=aSc_EF(0AMj* zh*^@&6hZciu^G(YyK>%aVb5T8E;Dxqb5psC*&aX!5>eR@wQwxD=H%FesYp(wUZdn1 z5@^p}h1_$rf&IC(1DU{Cd^W___@uhVx622-*|#Z+lB~^>kZdEz?P1VOv$4RPiba!Q z(eW|8D;LaG$H$x;3pJBh!MZuA49!X9?g4Znbw?UQ+=7$2Ke&7-?esX|Iz8^|2@HKb zVJU8#BxtFZh6iM`CzIhxvyaQozN(2rQZ@1LM1~GeT#zNuf~?(H4DHTp)=+a+n?P+@ zb=2RGT{r3IB&Iq#iH86n8Uh5_6GyKPQbq%)6Qkcsp^5ZXN>d6=q?9dVF{)0EJ(Nm= zEA_)vjNxOchti1VP+CVO(2mSYnLxkHtnm?AKis zFC1Gn7T#5jg?BX8c4raVoF%s6_eY_veUYtwF{Oc;hm6M3OeP~ak$N!|;KfuJio0~$ zRyK4iO;hYpcG!(tr`0HuYJ5W?HwbqdaktE8z4KF+liuZ|Hy3)LDHoa=Nkbzk%7vm< z*4s)80$$i*CIb!9mn-`0hbu?VsFpFu$AImCNfXtcxpsnv=Ra6?a@CqB;F2DLR^dDIF*YM2X}fA#q4(cA$>NI_}Y80-LcW<8lV> z8GACZV))fwOfWKPC#Ddkn(Pj+42Qci91o@KkM(M)7NEu@t0^8JY#`A=vz%#;OLo4D0BWo^jYVt(&x& z+K}6l27ul8>Sgbmj6Q*;nWUjQb5Ewp*ZB7OOnyPu&McEBNjl$MO!JH`F_ITCk{2WJes7x>uC&2YP*i8Wl!>Iq_Y!&i63j%2M0c@~cO-L@ z_s_)mXJYh&@onk^vIq%t_@S=>GeR|xTEFzM;r+@>E%ueSG6Mv}Ma2bK71XS{J*fpq z5e+HGd^>YDjr!eaMi!A3OYaj1aLZlJ zScDFce6#N~IzaO0eB~2Oo^CcKzYX^fohSJ+(7TJr03gXoNGKALhlE5SAt~nOOYQyj zg!wd;=cjK==bIBTafaB;y@^)_CZ%SXpT0Q#yT7w4tvl{&?nOVaSe#yy9@YiWF9T}Q zR~x7aG=Qk*Ewy5NEv;0w<6jxCUxKJWc1t$yS1s9VC*wwGKA)!{jxSQCc&3ElP&NPE}Lb!7!M1 zFziS;ZVE@j&xNyv>Rk9@4}is<^&SD%doB=g!E-|cH$0z5;07X>IuZ797}R_mRuhgc zc(?DBzNQNdMNN2PI4By!PZ7nr@OMF>)_V>_AUP0mJ_2>;BOua#Y_g4%VW}>JEhPa< z!%v69uhTFUC6fV#}H+#{x7ue@g&iCabxJKRfs()TP8pY01s-_&}k_dSP+ z?y%<=NjT=Yr0aU2)&5R3e(g)bx!U(%u~pvTxhe}Qrw%F) zs}Dm%)rW1M(uS~C!=zhY4ci33W>C-lLvrgESc%OKq}8fEtNWG!_}GL=ZC8=hF=-3t`0970pTN-k5Dxu>PM)TN9-A)nn%1hLcKrY z$Ov_8L_4Z!^OE`sJC^XPRqQTNOR;T4HVdjZu(uDc-Pd}bI)V-VyfGi@?r*qXZN-lZ z6#Z}k@{=o))eG3<$onB-ZE{Dlx`K_Xyk8XjBU?wRBiQuJo0Gvms$rDciVfDh6C7QI zz3045oqjQbpNee7uRZX}EA@pmd|azfnTs-2E7m;Kf%Q%;o!E|jrC1^@ z74UwMp_XR0XR41f%diQ@*NT@$xWVFfu#KzhynH_e&7Ms4US?}1X!a7#PPqlsEubkw z9mait-VN+2RDZ@x2~~l25LDhp*^VN6aQ2*`TaL26=yccK0^la=7R!?mTobPGWEcKp zjhDIbB;dWQA6S7}kwg0EdI0}=S^cc0m0Yy{lpsfXTU~N~#q<%@8!r0AzL=&r4$c7u*6F9OVZ@*3~~TBbAE4?MyN=zMLTp8M;hmTv+tn|`^@cy~cJ ztDfcbXB_L;gTga}CIqgZg`l5g=tl{iS;tB=Zeo99h2`t15ONl2dJ_~pOVh6mLBCPc zH>3&Sd`-Vg<0g!~(fhu}J$mz$k$)zH{62U^L*vb({i2VyboFOk^3K0QG`$JwZS>wB zf<8mj2gZt&g<9C78m|~Bz>^w(K7^c?G=0Z3A*|E%vtU3^^Lk4NIY&a^9gIU*sR%x& z<$tYl6I|MiH&2N18N)c%4IT8|%=h6T=+ia5^{^=Xz1BNfn{NK(v=5@BvoBmDI_{;|dH}<3fA0SZB z-zg#FKd$A()AyRVT6H`BDg^zrn%x>KL{QH5X zZ_xhg;ck5qg1#Rf%zA3KAtCVlLf{!8@M(-=9T+E8j@Iq-YmEn{3qJlPNf(8XQ>*D4 z^Z>WpAq4#{O`o13a_LPOU5AXEpG(=uIj!;hp9y}qBvvmxLiN->-wT1?$@nc+hkj>c z#@p}^^l6&D$>rA+jhBrHwsV@s11|cfG@kC#TcL4JvdF=*!&kkLqy3H3?cc<>CUPVn zX!>N=I6SNIc-Oko>$|d!wd!{MweI&HYCJwwgq!xiN8^D<1h3P&f6BPS5S>jm^y39E zavs*Wr&w_FeE6FXa%wbvs~#st&dVX_-_i5~9uoQHzV^PxD>4K(>(VD0Z>~VfwzTte(>M}FFzmZ_6Iv(Khk)<>pl|CxF&KW6EuB=d)xxQ(?Mslvw=t8 zy-HK9Akn&FQ3(09A@E%+C*0cM8YlZg(0|N0o{r>VPI`)4iLXb_H zigQH~_pdtz!sR@Uh=;RgA>I@sJj{9;N4(>3YX8(2t zkREs32V+;zdC)B1KCqkOy}3i5byLSjtJ6uukIZ~JxKg5C1Yve$1QS5T|B!DS&NVhu z6>U!w)knvp)9JS`E{NY4{Ydb!-Cbr&+)2mD@Epa0*m^u?nWLY`e6(AYW3qJ9N|R6y z$)@vad7MftIyz+=?MnywBK~7HzOI0Ee8zRRdg>ZJ1RO0QVx19LjK5uHrStf(ZKb%X zbOgFB_MSL7>zd5%ieDy?1#Kc4sYbFh0swju2Xl8L3vp6z+sLZ!o{9cGzBa!;plEV3?oe-l!I(E4Z{cBOYjl9-t;pKpP(7+(A{+syaW~H zkqSqipsr`fQ2AT&bSYP$bMN8x@Mc*{m#1#wHZQkc4O{ZN+nzfw;zp1iCpD!mPPZLXzm$7 zQ4&VVS@=5E3?imbOq(E;5FaW8iyc}|B25OdnNeXv5W{O7#YPX8t~J^AULaW%oFl7S z7zr(++kyTP%d49mJaY97o#&K2hJqs&cD0pjoSXTLn?mrNlQ}5dl|6`c3=yNDtB->e zJNucUDA@yz7b_ho!uG?AV_JEFZeSGH!=^H8fg{*ZuoW^~3}%*|ph@NE6!r*fD!V8H z)_JpY1X{LZ^BK_&I)~TJfD?8=9uCRa&4sR&i22bd6dtfgGTRMW?)Y)2=Z7H-8Ho>! z=|h16D1b$IFc2(b1P!qgWdKn&tE z2J44(S5OiThZIC))+5nKVRVA3rBf&lZcr3Mn?@q1=o`25KIEKvB8282Eh;^?6^9TEr%sDGOdf&@9xHwXCLppa{fT1*BJ5$r5Q(FYa`;ROV=0<3_z-v)pOkfx!=#w1 zhaaYp^}56Xr*t0em{k6TXK@S`nU(U<4gTbCf#>qcG^O>~6#o*D5 zCarYwoBdKI9csSdck$_>^@5Av>=Q95UaJLNE^{o}CPMa`_P1tga-Fu~{i^dab{nh? z87IHlUt-crwqW6K>!7ODB`g>uWSgYWmNhw2y+Oh@Nit{;oI5d!{%1CT#&3 zsWALzKUvfNk^c~QY3vw&v+t%^^Lw=adRp7#&$Q!T!At%d{-dicoDPKf}XpH@4pI(LLH{V+t`T-`R z(ecWML-616ebH24 zFD$~~);~A||Li-2^1#4=#)AwIfC#WgO-m0GXZ>0^1xMsQ-Tt(}D0bh&E. - -from .client import * - diff --git a/stardew-access/LinuxSpeech/speechd/client.py b/stardew-access/LinuxSpeech/speechd/client.py deleted file mode 100644 index b009e3f..0000000 --- a/stardew-access/LinuxSpeech/speechd/client.py +++ /dev/null @@ -1,1186 +0,0 @@ -# Copyright (C) 2003-2008 Brailcom, o.p.s. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -"""Python API to Speech Dispatcher - -Basic Python client API to Speech Dispatcher is provided by the 'SSIPClient' -class. This interface maps directly to available SSIP commands and logic. - -A more convenient interface is provided by the 'Speaker' class. - -""" - -#TODO: Blocking variants for speak, char, key, sound_icon. - -import socket, sys, os, subprocess, time, tempfile - -try: - import threading -except: - import dummy_threading as threading - -from . import paths - -class CallbackType(object): - """Constants describing the available types of callbacks""" - INDEX_MARK = 'index_marks' - """Index mark events are reported when the place they were - included into the text by the client application is reached - when speaking them""" - BEGIN = 'begin' - """The begin event is reported when Speech Dispatcher starts - actually speaking the message.""" - END = 'end' - """The end event is reported after the message has terminated and - there is no longer any sound from it being produced""" - CANCEL = 'cancel' - """The cancel event is reported when a message is canceled either - on request of the user, because of prioritization of messages or - due to an error""" - PAUSE = 'pause' - """The pause event is reported after speaking of a message - was paused. It no longer produces any audio.""" - RESUME = 'resume' - """The resume event is reported right after speaking of a message - was resumed after previous pause.""" - -class SSIPError(Exception): - """Common base class for exceptions during SSIP communication.""" - -class SSIPCommunicationError(SSIPError): - """Exception raised when trying to operate on a closed connection.""" - - _additional_exception = None - - def __init__(self, description=None, original_exception=None, **kwargs): - self._original_exception = original_exception - self._description = description - super(SSIPError, self).__init__(**kwargs) - - def original_exception(self): - """Return the original exception if any - - If this exception is secondary, being caused by a lower - level exception, return this original exception, otherwise - None""" - return self._original_exception - - def set_additional_exception(self, exception): - """Set an additional exception - - See method additional_exception(). - """ - self._additional_exception = exception - - def additional_exception(self): - """Return an additional exception - - Additional exceptions araise from failed attempts to resolve - the former problem""" - return self._additional_exception - - def description(self): - """Return error description""" - return self._description - - def __str__(self): - msgs = [] - if self.description(): - msgs.append(self.description()) - if self.original_exception: - msgs.append("Original error: " + str(self.original_exception())) - if self.additional_exception: - msgs.append("Additional error: " + str(self.additional_exception())) - return "\n".join(msgs) - -class SSIPResponseError(Exception): - def __init__(self, code, msg, data): - Exception.__init__(self, "%s: %s" % (code, msg)) - self._code = code - self._msg = msg - self._data = data - - def code(self): - """Return the server response error code as integer number.""" - return self._code - - def msg(self): - """Return server response error message as string.""" - return self._msg - - -class SSIPCommandError(SSIPResponseError): - """Exception raised on error response after sending command.""" - - def command(self): - """Return the command string which resulted in this error.""" - return self._data - - -class SSIPDataError(SSIPResponseError): - """Exception raised on error response after sending data.""" - - def data(self): - """Return the data which resulted in this error.""" - return self._data - - -class SpawnError(Exception): - """Indicates failure in server autospawn.""" - -class CommunicationMethod(object): - """Constants describing the possible methods of connection to server.""" - UNIX_SOCKET = 'unix_socket' - """Unix socket communication using a filesystem path""" - INET_SOCKET = 'inet_socket' - """Inet socket communication using a host and port""" - -class _SSIP_Connection(object): - """Implemantation of low level SSIP communication.""" - - _NEWLINE = b"\r\n" - _END_OF_DATA_MARKER = b'.' - _END_OF_DATA_MARKER_ESCAPED = b'..' - _END_OF_DATA = _NEWLINE + _END_OF_DATA_MARKER + _NEWLINE - _END_OF_DATA_ESCAPED = _NEWLINE + _END_OF_DATA_MARKER_ESCAPED + _NEWLINE - # Constants representing \r\n. and \r\n.. - _RAW_DOTLINE = _NEWLINE + _END_OF_DATA_MARKER - _ESCAPED_DOTLINE = _NEWLINE + _END_OF_DATA_MARKER_ESCAPED - - _CALLBACK_TYPE_MAP = {700: CallbackType.INDEX_MARK, - 701: CallbackType.BEGIN, - 702: CallbackType.END, - 703: CallbackType.CANCEL, - 704: CallbackType.PAUSE, - 705: CallbackType.RESUME, - } - - def __init__(self, communication_method, socket_path, host, port): - """Init connection: open the socket to server, - initialize buffers, launch a communication handling - thread. - """ - - if communication_method == CommunicationMethod.UNIX_SOCKET: - socket_family = socket.AF_UNIX - socket_connect_args = socket_path - elif communication_method == CommunicationMethod.INET_SOCKET: - assert host and port - socket_family = socket.AF_INET - socket_connect_args = (socket.gethostbyname(host), port) - else: - raise ValueError("Unsupported communication method") - - try: - self._socket = socket.socket(socket_family, socket.SOCK_STREAM) - self._socket.connect(socket_connect_args) - except socket.error as ex: - raise SSIPCommunicationError("Can't open socket using method " - + communication_method, - original_exception = ex) - - self._buffer = b"" - self._com_buffer = [] - self._callback = None - self._ssip_reply_semaphore = threading.Semaphore(0) - self._communication_thread = \ - threading.Thread(target=self._communication, kwargs={}, - name="SSIP client communication thread", - daemon=True) - self._communication_thread.start() - - def close(self): - """Close the server connection, destroy the communication thread.""" - # Read-write shutdown here is necessary, otherwise the socket.recv() - # function in the other thread won't return at last on some platforms. - try: - self._socket.shutdown(socket.SHUT_RDWR) - except socket.error: - pass - self._socket.close() - # Wait for the other thread to terminate - self._communication_thread.join() - - def _communication(self): - """Handle incomming socket communication. - - Listens for all incomming communication on the socket, dispatches - events and puts all other replies into self._com_buffer list in the - already parsed form as (code, msg, data). Each time a new item is - appended to the _com_buffer list, the corresponding semaphore - 'self._ssip_reply_semaphore' is incremented. - - This method is designed to run in a separate thread. The thread can be - interrupted by closing the socket on which it is listening for - reading.""" - - while True: - try: - code, msg, data = self._recv_message() - except IOError: - # If the socket has been closed, exit the thread - sys.exit() - if code//100 != 7: - # This is not an index mark nor an event - self._com_buffer.append((code, msg, data)) - self._ssip_reply_semaphore.release() - continue - # Ignore the event if no callback function has been registered. - if self._callback is not None: - type = self._CALLBACK_TYPE_MAP[code] - if type == CallbackType.INDEX_MARK: - kwargs = {'index_mark': data[2]} - else: - kwargs = {} - # Get message and client ID of the event - msg_id, client_id = map(int, data[:2]) - self._callback(msg_id, client_id, type, **kwargs) - - - def _readline(self): - """Read one whole line from the socket. - - Blocks until the line delimiter ('_NEWLINE') is read. - - """ - pointer = self._buffer.find(self._NEWLINE) - while pointer == -1: - try: - d = self._socket.recv(1024) - except: - raise IOError - if len(d) == 0: - raise IOError - self._buffer += d - pointer = self._buffer.find(self._NEWLINE) - line = self._buffer[:pointer] - self._buffer = self._buffer[pointer+len(self._NEWLINE):] - return line.decode('utf-8') - - def _recv_message(self): - """Read server response or a callback - and return the triplet (code, msg, data).""" - data = [] - c = None - while True: - line = self._readline() - assert len(line) >= 4, "Malformed data received from server!" - code, sep, text = line[:3], line[3], line[4:] - assert code.isalnum() and (c is None or code == c) and \ - sep in ('-', ' '), "Malformed data received from server!" - if sep == ' ': - msg = text - return int(code), msg, tuple(data) - data.append(text) - - def _recv_response(self): - """Read server response from the communication thread - and return the triplet (code, msg, data).""" - # TODO: This check is dumb but seems to work. The main thread - # hangs without it, when the Speech Dispatcher connection is lost. - if not self._communication_thread.is_alive(): - raise SSIPCommunicationError - self._ssip_reply_semaphore.acquire() - # The list is sorted, read the first item - response = self._com_buffer[0] - del self._com_buffer[0] - return response - - def send_command(self, command, *args): - """Send SSIP command with given arguments and read server response. - - Arguments can be of any data type -- they are all stringified before - being sent to the server. - - Returns a triplet (code, msg, data), where 'code' is a numeric SSIP - response code as an integer, 'msg' is an SSIP rsponse message as string - and 'data' is a tuple of strings (all lines of response data) when a - response contains some data. - - 'SSIPCommandError' is raised in case of non 2xx return code. See SSIP - documentation for more information about server responses and codes. - - 'IOError' is raised when the socket was closed by the remote side. - - """ - if __debug__: - if command in ('SET', 'CANCEL', 'STOP',): - assert args[0] in (Scope.SELF, Scope.ALL) \ - or isinstance(args[0], int) - cmd = ' '.join((command,) + tuple(map(str, args))) - try: - self._socket.send(cmd.encode('utf-8') + self._NEWLINE) - except socket.error: - raise SSIPCommunicationError("Speech Dispatcher connection lost.") - code, msg, data = self._recv_response() - if code//100 != 2: - raise SSIPCommandError(code, msg, cmd) - return code, msg, data - - def send_data(self, data): - """Send multiline data and read server response. - - Returned value is the same as for 'send_command()' method. - - 'SSIPDataError' is raised in case of non 2xx return code. See SSIP - documentation for more information about server responses and codes. - - 'IOError' is raised when the socket was closed by the remote side. - - """ - data = data.encode('utf-8') - # Escape the end-of-data marker even if present at the beginning - # The start of the string is also the start of a line. - if data.startswith(self._END_OF_DATA_MARKER): - l = len(self._END_OF_DATA_MARKER) - data = self._END_OF_DATA_MARKER_ESCAPED + data[l:] - - # Escape the end of data marker at the start of each subsequent - # line. We can do that by simply replacing \r\n. with \r\n.., - # since the start of a line is immediately preceded by \r\n, - # when the line is not the beginning of the string. - data = data.replace(self._RAW_DOTLINE, self._ESCAPED_DOTLINE) - - try: - self._socket.send(data + self._END_OF_DATA) - except socket.error: - raise SSIPCommunicationError("Speech Dispatcher connection lost.") - code, msg, response_data = self._recv_response() - if code//100 != 2: - raise SSIPDataError(code, msg, data) - return code, msg, response_data - - def set_callback(self, callback): - """Register a callback function for handling asynchronous events. - - Arguments: - callback -- a callable object (function) which will be called to - handle asynchronous events (arguments described below). Passing - `None' results in removing the callback function and ignoring - events. Just one callback may be registered. Attempts to register - a second callback will result in the former callback being - replaced. - - The callback function must accept three positional arguments - ('message_id', 'client_id', 'event_type') and an optional keyword - argument 'index_mark' (when INDEX_MARK events are turned on). - - Note, that setting the callback function doesn't turn the events on. - The user is responsible to turn them on by sending the appropriate `SET - NOTIFICATION' command. - - """ - self._callback = callback - -class _CallbackHandler(object): - """Internal object which handles callbacks.""" - - def __init__(self, client_id): - self._client_id = client_id - self._callbacks = {} - self._lock = threading.Lock() - - def __call__(self, msg_id, client_id, type, **kwargs): - if client_id != self._client_id: - # TODO: does that ever happen? - return - self._lock.acquire() - try: - try: - callback, event_types = self._callbacks[msg_id] - except KeyError: - pass - else: - if event_types is None or type in event_types: - callback(type, **kwargs) - if type in (CallbackType.END, CallbackType.CANCEL): - del self._callbacks[msg_id] - finally: - self._lock.release() - - def add_callback(self, msg_id, callback, event_types): - self._lock.acquire() - try: - self._callbacks[msg_id] = (callback, event_types) - finally: - self._lock.release() - -class Scope(object): - """An enumeration of valid SSIP command scopes. - - The constants of this class should be used to specify the 'scope' argument - for the 'Client' methods. - - """ - SELF = 'self' - """The command (mostly a setting) applies to current connection only.""" - ALL = 'all' - """The command applies to all current Speech Dispatcher connections.""" - - -class Priority(object): - """An enumeration of valid SSIP message priorities. - - The constants of this class should be used to specify the 'priority' - argument for the 'Client' methods. For more information about message - priorities and their interaction, see the SSIP documentation. - - """ - IMPORTANT = 'important' - TEXT = 'text' - MESSAGE = 'message' - NOTIFICATION = 'notification' - PROGRESS = 'progress' - - -class PunctuationMode(object): - """Constants for selecting a punctuation mode. - - The mode determines which characters should be read. - - """ - ALL = 'all' - """Read all punctuation characters.""" - NONE = 'none' - """Don't read any punctuation character at all.""" - SOME = 'some' - """Only some of the user-defined punctuation characters are read.""" - MOST = 'most' - """Only most of the user-defined punctuation characters are read. - - The set of characters is specified in Speech Dispatcher configuration. - - """ - -class DataMode(object): - """Constants specifying the type of data contained within messages - to be spoken. - - """ - TEXT = 'text' - """Data is plain text.""" - SSML = 'ssml' - """Data is SSML (Speech Synthesis Markup Language).""" - - -class SSIPClient(object): - """Basic Speech Dispatcher client interface. - - This class provides a Python interface to Speech Dispatcher functionality - over an SSIP connection. The API maps directly to available SSIP commands. - Each connection to Speech Dispatcher is represented by one instance of this - class. - - Many commands take the 'scope' argument, thus it is shortly documented - here. It is either one of 'Scope' constants or a number of connection. By - specifying the connection number, you are applying the command to a - particular connection. This feature is only meant to be used by Speech - Dispatcher control application, however. More datails can be found in - Speech Dispatcher documentation. - - """ - - DEFAULT_HOST = '127.0.0.1' - """Default host for server connections.""" - DEFAULT_PORT = 6560 - """Default port number for server connections.""" - DEFAULT_SOCKET_PATH = "speech-dispatcher/speechd.sock" - """Default name of the communication unix socket""" - - def __init__(self, name, component='default', user='unknown', address=None, - autospawn=None, - # Deprecated -> - host=None, port=None, method=None, socket_path=None): - """Initialize the instance and connect to the server. - - Arguments: - name -- client identification string - component -- connection identification string. When one client opens - multiple connections, this can be used to identify each of them. - user -- user identification string (user name). When multi-user - acces is expected, this can be used to identify their connections. - address -- server address as specified in Speech Dispatcher - documentation (e.g. "unix:/run/user/joe/speech-dispatcher/speechd.sock" - or "inet:192.168.0.85:6561") - autospawn -- a flag to specify whether the library should - try to start the server if it determines its not already - running or not - - Deprecated arguments: - method -- communication method to use, one of the constants defined in class - CommunicationMethod - socket_path -- for CommunicationMethod.UNIX_SOCKET, socket - path in filesystem. By default, this is $XDG_RUNTIME_DIR/speech-dispatcher/speechd.sock - where $XDG_RUNTIME_DIR is determined using the XDG Base Directory - Specification. - host -- for CommunicationMethod.INET_SOCKET, server hostname - or IP address as a string. If None, the default value is - taken from SPEECHD_HOST environment variable (if it - exists) or from the DEFAULT_HOST attribute of this class. - port -- for CommunicationMethod.INET_SOCKET method, server - port as number or None. If None, the default value is - taken from SPEECHD_PORT environment variable (if it - exists) or from the DEFAULT_PORT attribute of this class. - - For more information on client identification strings see Speech - Dispatcher documentation. - """ - - _home = os.path.expanduser("~") - _runtime_dir = os.environ.get('XDG_RUNTIME_DIR', os.environ.get('XDG_CACHE_HOME', os.path.join(_home, '.cache'))) - _sock_path = os.path.join(_runtime_dir, self.DEFAULT_SOCKET_PATH) - # Resolve connection parameters: - connection_args = {'communication_method': CommunicationMethod.UNIX_SOCKET, - 'socket_path': _sock_path, - 'host': self.DEFAULT_HOST, - 'port': self.DEFAULT_PORT, - } - # Respect address method argument and SPEECHD_ADDRESS environemt variable - _address = address or os.environ.get("SPEECHD_ADDRESS") - - if _address: - connection_args.update(self._connection_arguments_from_address(_address)) - # Respect the old (deprecated) key arguments and environment variables - # TODO: Remove this section in 0.8 release - else: - # Read the environment variables - env_speechd_host = os.environ.get("SPEECHD_HOST") - try: - env_speechd_port = int(os.environ.get("SPEECHD_PORT")) - except: - env_speechd_port = None - env_speechd_socket_path = os.environ.get("SPEECHD_SOCKET") - # Prefer old (deprecated) function arguments, but if - # not specified and old (deprecated) environment variable - # is set, use the value of the environment variable - if method: - connection_args['method'] = method - if port: - connection_args['port'] = port - elif env_speechd_port: - connection_args['port'] = env_speechd_port - if socket_path: - connection_args['socket_path'] = socket_path - elif env_speechd_socket_path: - connection_args['socket_path'] = env_speechd_socket_path - self._connect_with_autospawn(connection_args, autospawn) - self._initialize_connection(user, name, component) - - def _connect_with_autospawn(self, connection_args, autospawn): - """Establish new connection (and/or autospawn server)""" - try: - self._conn = _SSIP_Connection(**connection_args) - except SSIPCommunicationError as ce: - # Suppose server might not be running, try the autospawn mechanism - if autospawn != False: - # Autospawn is however not guaranteed to start the server. The server - # will decide, based on it's configuration, whether to honor the request. - try: - self._server_spawn(connection_args) - except SpawnError as se: - ce.set_additional_exception(se) - raise ce - self._conn = _SSIP_Connection(**connection_args) - else: - raise - - def _initialize_connection(self, user, name, component): - """Initialize connection -- Set client name, get id, register callbacks etc.""" - full_name = '%s:%s:%s' % (user, name, component) - self._conn.send_command('SET', Scope.SELF, 'CLIENT_NAME', full_name) - code, msg, data = self._conn.send_command('HISTORY', 'GET', 'CLIENT_ID') - self._client_id = int(data[0]) - self._callback_handler = _CallbackHandler(self._client_id) - self._conn.set_callback(self._callback_handler) - for event in (CallbackType.INDEX_MARK, - CallbackType.BEGIN, - CallbackType.END, - CallbackType.CANCEL, - CallbackType.PAUSE, - CallbackType.RESUME): - self._conn.send_command('SET', 'self', 'NOTIFICATION', event, 'on') - - def _connection_arguments_from_address(self, address): - """Parse a Speech Dispatcher address line and return a dictionary - of connection arguments""" - connection_args = {} - address_params = address.split(":") - try: - _method = address_params[0] - except: - raise SSIPCommunicationErrror("Wrong format of server address") - connection_args['communication_method'] = _method - if _method == CommunicationMethod.UNIX_SOCKET: - try: - connection_args['socket_path'] = address_params[1] - except IndexError: - pass # The additional parameters was not set, let's stay with defaults - elif _method == CommunicationMethod.INET_SOCKET: - try: - connection_args['host'] = address_params[1] - connection_args['port'] = int(address_params[2]) - except ValueError: # Failed conversion to int - raise SSIPCommunicationError("Third parameter of inet_socket address must be a port number") - except IndexError: - pass # The additional parameters was not set, let's stay with defaults - else: - raise SSIPCommunicationError("Unknown communication method in address."); - return connection_args - - def __del__(self): - """Close the connection""" - self.close() - - def _server_spawn(self, connection_args): - """Attempts to spawn the speech-dispatcher server.""" - # Check whether we are not connecting to a remote host - # TODO: This is a hack. inet sockets specific code should - # belong to _SSIPConnection. We do not however have an _SSIPConnection - # yet. - if connection_args['communication_method'] == 'inet_socket': - addrinfos = socket.getaddrinfo(connection_args['host'], - connection_args['port']) - # Check resolved addrinfos for presence of localhost - ip_addresses = [addrinfo[4][0] for addrinfo in addrinfos] - localhost=False - for ip in ip_addresses: - if ip.startswith("127.") or ip == "::1": - connection_args['host'] = ip - localhost=True - if not localhost: - # The hostname didn't resolve on localhost in neither case, - # do not spawn server on localhost... - raise SpawnError( - "Can't start server automatically (autospawn), requested address %s " - "resolves on %s which seems to be a remote host. You must start the " - "server manually or choose another connection address." % (connection_args['host'], - str(ip_addresses),)) - cmd = os.getenv("SPEECHD_CMD") - if not cmd: - cmd = paths.SPD_SPAWN_CMD - if os.path.exists(cmd): - connection_params = [] - for param, value in connection_args.items(): - if param not in ["host",]: - connection_params += ["--"+param.replace("_","-"), str(value)] - - server = subprocess.Popen([cmd, "--spawn"]+connection_params, - stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - stdout_reply, stderr_reply = server.communicate() - retcode = server.wait() - if retcode != 0: - raise SpawnError("Server refused to autospawn, stating this reason: %s" % (stderr_reply,)) - return server.pid - else: - raise SpawnError("Can't find Speech Dispatcher spawn command %s" - % (cmd)) - - def set_priority(self, priority): - """Set the priority category for the following messages. - - Arguments: - priority -- one of the 'Priority' constants. - - """ - assert priority in (Priority.IMPORTANT, Priority.MESSAGE, - Priority.TEXT, Priority.NOTIFICATION, - Priority.PROGRESS), priority - self._conn.send_command('SET', Scope.SELF, 'PRIORITY', priority) - - def set_data_mode(self, value): - """Set the data mode for further speech commands. - - Arguments: - value - one of the constants defined by the DataMode class. - - """ - if value == DataMode.SSML: - ssip_val = 'on' - elif value == DataMode.TEXT: - ssip_val = 'off' - else: - raise ValueError( - 'Value "%s" is not one of the constants from the DataMode class.' % \ - value) - self._conn.send_command('SET', Scope.SELF, 'SSML_MODE', ssip_val) - - def speak(self, text, callback=None, event_types=None): - """Say given message. - - Arguments: - text -- message text to be spoken. This may be either a UTF-8 - encoded byte string or a Python unicode string. - callback -- a callback handler for asynchronous event notifications. - A callable object (function) which accepts one positional argument - `type' and one keyword argument `index_mark'. See below for more - details. - event_types -- a tuple of event types for which the callback should - be called. Each item must be one of `CallbackType' constants. - None (the default value) means to handle all event types. This - argument is irrelevant when `callback' is not used. - - The callback function will be called whenever one of the events occurs. - The event type will be passed as argument. Its value is one of the - `CallbackType' constants. In case of an index mark event, additional - keyword argument `index_mark' will be passed and will contain the index - mark identifier as specified within the text. - - The callback function should not perform anything complicated and is - not allowed to issue any further SSIP client commands. An attempt to - do so would lead to a deadlock in SSIP communication. - - This method is non-blocking; it just sends the command, given - message is queued on the server and the method returns immediately. - - """ - self._conn.send_command('SPEAK') - result = self._conn.send_data(text) - if callback: - msg_id = int(result[2][0]) - # TODO: Here we risk, that the callback arrives earlier, than we - # add the item to `self._callback_handler'. Such a situation will - # lead to the callback being ignored. - self._callback_handler.add_callback(msg_id, callback, event_types) - return result - - def char(self, char): - """Say given character. - - Arguments: - char -- a character to be spoken. Either a Python unicode string or - a UTF-8 encoded byte string. - - This method is non-blocking; it just sends the command, given - message is queued on the server and the method returns immediately. - - """ - self._conn.send_command('CHAR', char.replace(' ', 'space')) - - def key(self, key): - """Say given key name. - - Arguments: - key -- the key name (as defined in SSIP); string. - - This method is non-blocking; it just sends the command, given - message is queued on the server and the method returns immediately. - - """ - self._conn.send_command('KEY', key) - - def sound_icon(self, sound_icon): - """Output given sound_icon. - - Arguments: - sound_icon -- the name of the sound icon as defined by SSIP; string. - - This method is non-blocking; it just sends the command, given message - is queued on the server and the method returns immediately. - - """ - self._conn.send_command('SOUND_ICON', sound_icon) - - def cancel(self, scope=Scope.SELF): - """Immediately stop speaking and discard messages in queues. - - Arguments: - scope -- see the documentation of this class. - - """ - self._conn.send_command('CANCEL', scope) - - - def stop(self, scope=Scope.SELF): - """Immediately stop speaking the currently spoken message. - - Arguments: - scope -- see the documentation of this class. - - """ - self._conn.send_command('STOP', scope) - - def pause(self, scope=Scope.SELF): - """Pause speaking and postpone other messages until resume. - - This method is non-blocking. However, speaking can continue for a - short while even after it's called (typically to the end of the - sentence). - - Arguments: - scope -- see the documentation of this class. - - """ - self._conn.send_command('PAUSE', scope) - - def resume(self, scope=Scope.SELF): - """Resume speaking of the currently paused messages. - - This method is non-blocking. However, speaking can continue for a - short while even after it's called (typically to the end of the - sentence). - - Arguments: - scope -- see the documentation of this class. - - """ - self._conn.send_command('RESUME', scope) - - def list_output_modules(self): - """Return names of all active output modules as a tuple of strings.""" - code, msg, data = self._conn.send_command('LIST', 'OUTPUT_MODULES') - return data - - def list_synthesis_voices(self): - """Return names of all available voices for the current output module. - - Returns a tuple of tripplets (name, language, variant). - - 'name' is a string, 'language' is an ISO 639-1 Alpha-2/3 language code - and 'variant' is a string. Language and variant may be None. - - """ - try: - code, msg, data = self._conn.send_command('LIST', 'SYNTHESIS_VOICES') - except SSIPCommandError: - return () - def split(item): - name, lang, variant = tuple(item.rsplit('\t', 3)) - return (name, lang or None, variant or None) - return tuple([split(item) for item in data]) - - def set_language(self, language, scope=Scope.SELF): - """Switch to a particular language for further speech commands. - - Arguments: - language -- two/three letter language code according to RFC 1766 as string, possibly with a region qualification. - scope -- see the documentation of this class. - - """ - assert isinstance(language, str) - self._conn.send_command('SET', scope, 'LANGUAGE', language) - - def get_language(self): - """Get the current language.""" - code, msg, data = self._conn.send_command('GET', 'LANGUAGE') - if data: - return data[0] - return None - - def set_output_module(self, name, scope=Scope.SELF): - """Switch to a particular output module. - - Arguments: - name -- module (string) as returned by 'list_output_modules()'. - scope -- see the documentation of this class. - - """ - self._conn.send_command('SET', scope, 'OUTPUT_MODULE', name) - - def get_output_module(self): - """Get the current output module.""" - code, msg, data = self._conn.send_command('GET', 'OUTPUT_MODULE') - if data: - return data[0] - return None - - def set_pitch(self, value, scope=Scope.SELF): - """Set the pitch for further speech commands. - - Arguments: - value -- integer value within the range from -100 to 100, with 0 - corresponding to the default pitch of the current speech synthesis - output module, lower values meaning lower pitch and higher values - meaning higher pitch. - scope -- see the documentation of this class. - - """ - assert isinstance(value, int) and -100 <= value <= 100, value - self._conn.send_command('SET', scope, 'PITCH', value) - - def get_pitch(self): - """Get the current pitch.""" - code, msg, data = self._conn.send_command('GET', 'PITCH') - if data: - return data[0] - return None - - def set_pitch_range(self, value, scope=Scope.SELF): - """Set the pitch range for further speech commands. - - Arguments: - value -- integer value within the range from -100 to 100, with 0 - corresponding to the default pitch range of the current speech synthesis - output module, lower values meaning lower pitch range and higher values - meaning higher pitch range. - scope -- see the documentation of this class. - - """ - assert isinstance(value, int) and -100 <= value <= 100, value - self._conn.send_command('SET', scope, 'PITCH_RANGE', value) - - def set_rate(self, value, scope=Scope.SELF): - """Set the speech rate (speed) for further speech commands. - - Arguments: - value -- integer value within the range from -100 to 100, with 0 - corresponding to the default speech rate of the current speech - synthesis output module, lower values meaning slower speech and - higher values meaning faster speech. - scope -- see the documentation of this class. - - """ - assert isinstance(value, int) and -100 <= value <= 100 - self._conn.send_command('SET', scope, 'RATE', value) - - def get_rate(self): - """Get the current speech rate (speed).""" - code, msg, data = self._conn.send_command('GET', 'RATE') - if data: - return data[0] - return None - - def set_volume(self, value, scope=Scope.SELF): - """Set the speech volume for further speech commands. - - Arguments: - value -- integer value within the range from -100 to 100, with 100 - corresponding to the default speech volume of the current speech - synthesis output module, lower values meaning softer speech. - scope -- see the documentation of this class. - - """ - assert isinstance(value, int) and -100 <= value <= 100 - self._conn.send_command('SET', scope, 'VOLUME', value) - - def get_volume(self): - """Get the speech volume.""" - code, msg, data = self._conn.send_command('GET', 'VOLUME') - if data: - return data[0] - return None - - def set_punctuation(self, value, scope=Scope.SELF): - """Set the punctuation pronounciation level. - - Arguments: - value -- one of the 'PunctuationMode' constants. - scope -- see the documentation of this class. - - """ - assert value in (PunctuationMode.ALL, PunctuationMode.MOST, - PunctuationMode.SOME, PunctuationMode.NONE), value - self._conn.send_command('SET', scope, 'PUNCTUATION', value) - - def get_punctuation(self): - """Get the punctuation pronounciation level.""" - code, msg, data = self._conn.send_command('GET', 'PUNCTUATION') - if data: - return data[0] - return None - - def set_spelling(self, value, scope=Scope.SELF): - """Toogle the spelling mode or on off. - - Arguments: - value -- if 'True', all incomming messages will be spelled - instead of being read as normal words. 'False' switches - this behavior off. - scope -- see the documentation of this class. - - """ - assert value in [True, False] - if value == True: - self._conn.send_command('SET', scope, 'SPELLING', "on") - else: - self._conn.send_command('SET', scope, 'SPELLING', "off") - - def set_cap_let_recogn(self, value, scope=Scope.SELF): - """Set capital letter recognition mode. - - Arguments: - value -- one of 'none', 'spell', 'icon'. None means no signalization - of capital letters, 'spell' means capital letters will be spelled - with a syntetic voice and 'icon' means that the capital-letter icon - will be prepended before each capital letter. - scope -- see the documentation of this class. - - """ - assert value in ("none", "spell", "icon") - self._conn.send_command('SET', scope, 'CAP_LET_RECOGN', value) - - def set_voice(self, value, scope=Scope.SELF): - """Set voice by a symbolic name. - - Arguments: - value -- one of the SSIP symbolic voice names: 'MALE1' .. 'MALE3', - 'FEMALE1' ... 'FEMALE3', 'CHILD_MALE', 'CHILD_FEMALE' - scope -- see the documentation of this class. - - Symbolic voice names are mapped to real synthesizer voices in the - configuration of the output module. Use the method - 'set_synthesis_voice()' if you want to work with real voices. - - """ - assert isinstance(value, str) and \ - value.lower() in ("male1", "male2", "male3", "female1", - "female2", "female3", "child_male", - "child_female") - self._conn.send_command('SET', scope, 'VOICE_TYPE', value) - - def set_synthesis_voice(self, value, scope=Scope.SELF): - """Set voice by its real name. - - Arguments: - value -- voice name as returned by 'list_synthesis_voices()' - scope -- see the documentation of this class. - - """ - self._conn.send_command('SET', scope, 'SYNTHESIS_VOICE', value) - - def set_pause_context(self, value, scope=Scope.SELF): - """Set the amount of context when resuming a paused message. - - Arguments: - value -- a positive or negative value meaning how many chunks of data - after or before the pause should be read when resume() is executed. - scope -- see the documentation of this class. - - """ - assert isinstance(value, int) - self._conn.send_command('SET', scope, 'PAUSE_CONTEXT', value) - - def set_debug(self, val): - """Switch debugging on and off. When switched on, - debugging files will be created in the chosen destination - (see set_debug_destination()) for Speech Dispatcher and all - its running modules. All logging information will then be - written into these files with maximal verbosity until switched - off. You should always first call set_debug_destination. - - The intended use of this functionality is to switch debuging - on for a period of time while the user will repeat the behavior - and then send the logs to the appropriate bug-reporting place. - - Arguments: - val -- a boolean value determining whether debugging - is switched on or off - scope -- see the documentation of this class. - - """ - assert isinstance(val, bool) - if val == True: - ssip_val = "ON" - else: - ssip_val = "OFF" - - self._conn.send_command('SET', scope.ALL, 'DEBUG', ssip_val) - - - def set_debug_destination(self, path): - """Set debug destination. - - Arguments: - path -- path (string) to the directory where debuging - files will be created - scope -- see the documentation of this class. - - """ - assert isinstance(val, string) - - self._conn.send_command('SET', scope.ALL, 'DEBUG_DESTINATION', val) - - def block_begin(self): - """Begin an SSIP block. - - See SSIP documentation for more details about blocks. - - """ - self._conn.send_command('BLOCK', 'BEGIN') - - def block_end(self): - """Close an SSIP block. - - See SSIP documentation for more details about blocks. - - """ - self._conn.send_command('BLOCK', 'END') - - def close(self): - """Close the connection to Speech Dispatcher.""" - if hasattr(self, '_conn'): - self._conn.close() - del self._conn - - -class Client(SSIPClient): - """A DEPRECATED backwards-compatible API. - - This Class is provided only for backwards compatibility with the prevoius - unofficial API. It will be removed in future versions. Please use either - 'SSIPClient' or 'Speaker' interface instead. As deprecated, the API is no - longer documented. - - """ - def __init__(self, name=None, client=None, **kwargs): - name = name or client or 'python' - super(Client, self).__init__(name, **kwargs) - - def say(self, text, priority=Priority.MESSAGE): - self.set_priority(priority) - self.speak(text) - - def char(self, char, priority=Priority.TEXT): - self.set_priority(priority) - super(Client, self).char(char) - - def key(self, key, priority=Priority.TEXT): - self.set_priority(priority) - super(Client, self).key(key) - - def sound_icon(self, sound_icon, priority=Priority.TEXT): - self.set_priority(priority) - super(Client, self).sound_icon(sound_icon) - - -class Speaker(SSIPClient): - """Extended Speech Dispatcher Interface. - - This class provides an extended intercace to Speech Dispatcher - functionality and tries to hide most of the lower level details of SSIP - (such as a more sophisticated handling of blocks and priorities and - advanced event notifications) under a more convenient API. - - Please note that the API is not yet stabilized and thus is subject to - change! Please contact the authors if you plan using it and/or if you have - any suggestions. - - Well, in fact this class is currently not implemented at all. It is just a - draft. The intention is to hide the SSIP details and provide a generic - interface practical for screen readers. - - """ - - -# Deprecated but retained for backwards compatibility - -# This class was introduced in 0.7 but later renamed to CommunicationMethod -class ConnectionMethod(object): - """Constants describing the possible methods of connection to server. - - Retained for backwards compatibility but DEPRECATED. See CommunicationMethod.""" - UNIX_SOCKET = 'unix_socket' - """Unix socket communication using a filesystem path""" - INET_SOCKET = 'inet_socket' - """Inet socket communication using a host and port""" diff --git a/stardew-access/LinuxSpeech/speechd/paths.py b/stardew-access/LinuxSpeech/speechd/paths.py deleted file mode 100644 index da8f18a..0000000 --- a/stardew-access/LinuxSpeech/speechd/paths.py +++ /dev/null @@ -1 +0,0 @@ -SPD_SPAWN_CMD = "/usr/local/bin/speech-dispatcher" diff --git a/stardew-access/LinuxSpeech/wrapper.py b/stardew-access/LinuxSpeech/wrapper.py deleted file mode 100644 index 6b53f61..0000000 --- a/stardew-access/LinuxSpeech/wrapper.py +++ /dev/null @@ -1,18 +0,0 @@ -import speechd - -class Speech: - client = None - - def Initialize(self): - self.client = speechd.SSIPClient('stardew-access') - - def Say(self, text, interrupt): - if(self.client is not None): - if(interrupt): - self.client.stop() - - self.client.speak(text) - - def Close(self): - if(self.client is not None): - self.client.close() \ No newline at end of file diff --git a/stardew-access/ScreenReader.cs b/stardew-access/ScreenReader.cs index bdd4fca..f0360fd 100644 --- a/stardew-access/ScreenReader.cs +++ b/stardew-access/ScreenReader.cs @@ -8,32 +8,11 @@ namespace stardew_access public class ScreenReader { public IAccessibleOutput? screenReader = null; - public dynamic wrapperInstance = null; public string prevText = "", prevTextTile = " ", prevChatText = "", prevMenuText = ""; ///

Initializes the screen reader. public void InitializeScreenReader() { - MainClass.monitor.Log($"here! {RuntimeInformation.OSDescription}", LogLevel.Debug); - if(RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - MainClass.monitor.Log($"here!", LogLevel.Debug); - //instance of python engine - var engine = Python.CreateEngine(); - //reading code from file - var source = engine.CreateScriptSourceFromFile(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "LinuxSpeech", "wrapper.py")); - var scope = engine.CreateScope(); - //executing script in scope - source.Execute(scope); - var wrapper = scope.GetVariable("Speech"); - //initializing class - wrapperInstance = engine.Operations.CreateInstance(wrapper); - wrapperInstance.Initialize(); - - MainClass.monitor.Log($"here!", LogLevel.Debug); - - return; - } if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return; @@ -78,12 +57,6 @@ namespace stardew_access /// Whether to skip the currently speaking text or not. public void Say(string text, bool interrupt) { - if(RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - wrapperInstance.Say(text, interrupt); - return; - } - if (screenReader == null) return; diff --git a/stardew-access/stardew-access.csproj b/stardew-access/stardew-access.csproj index 45a9d76..e324c22 100644 --- a/stardew-access/stardew-access.csproj +++ b/stardew-access/stardew-access.csproj @@ -7,8 +7,7 @@ enable preview AnyCPU - true - PackageReference + true