e2q3ZnR@n5SGuE$)#18H$9CwT^i>%*I;Oj9VBaR0p z2a8a$XN~?XlV%S*kIpD_0o1~<5gr33sIw+4cLE8BV$v|TzcjLknyrWd?3l{ z>I*LldXp|XL#cEe$UT)Dj^&zyE^>>9`C$oeQ4O>{QsVhM9;o&4z&+DsmzLP&C_p%l znmoEW(MQ$n999aCwc#@hC+u;2rSSS9b4Q99VMG+U3xLGMUG=aT7dicO!ayqj#VxdO zN&u0k-bxE~Ou3a7P1C@(m#3AHhehsqG4~!|TRgpte6auGCx?;`wk>&TD0x`?WEm}e zYM2k`m$F^Qvi}}R9v=VBAU|QUH(!droSJrxnmEi{08C&@ydNDG2jDBzpl6S>skCqB z5C&b*nFg^g1XBlDN8j^F{50KQ9*1yc7fO=92K$wrX|M~(N$7RyIcgK!I!<_VjuV!d z(xfwU0pg-dVT{lD;EG=RxKewho;Ged5W$ActTUkNKHqwfj?~qY<2Y1VeS~1MV}IDT zDOp}`*mv+_aje?|*6)qcr^&oP&~l^61;8Iy09h^s+nusyX9Zn5exViDus(#l%js(_ zZu-_@+O@Y@HumPq&Axu{UF8(QIF`pPpusK%u{>z(0`gGGCmyd*cTXJWDu4uzxe%~vmCz``aiY4!Rq4hIu2D) Z)3QP?jmsXIimRf{-kq*_zHrhgY2cKyX-T>bGb`pxe*^EimA_@>Mh_f&RQ z;7(+7;jE84n^k}yA;AK@iDV(35bD7N0%bfm;K(TKhHQ&E+?b@Wm9(q1ZAmeG|K5I7 zROC=YLnHm?jMe$Cej~gorpdVrfMhAyLn~YK5 d zGN22U_UH2U2ly5mg;NLQ`k1jXcIqh;-CUnF|m%BNFDp%CRI5Joup3Av!Hd zuu0L4Yzq%Y7fKG3l+Gn^XUcb#T}FR*(b;SvdiAY8)4KJ0WSrEPG0d%*_M}*ztyu*~ zo6wd#Ol OlP5gm{?1#kEuFilJjS<-NQ8Lerf5CZY(D(1TQUc)d!;4G) zB;(wTb H2>d-VS@@Wlk-&zyDd%7ld%!#R3T&HxoR5^I@cU@T70bGp|O4G%+pU1 z`wNTK(at?pGWHr<3{#gXTb_-1@y$N-9bh^_R*Ay40BFMS7V0=rJJ*HG ?2C@H8~34`-BEas)vw46T}YLvUu2FiolY5bzy}!X!=8LG=dfLqlWK|zk=En7g0Sw za=-((!-eFlU*f0<{b COzgdnb3(b zrMd2^3uwrI3x)NocQ( & 1d9i(3&6&Jun~Syt>T!Wz*Ux@6`esu;YQqe2|se^D8v=N{hZeD zBWDrZM*PT}IV?B2+~h9olgtgQ*$WW(ghtqafVHwvBMGdQvxxP82wDSQz9b_lfF23d zs4|3J23jLJj<2aWWFvpc(uN)$*OxHwp~_l%?3tCU*I5{c6DP5>t4+z^1u%7{95B;I zO)dbE4G=aC#LOiewq1DsnY8@rVf3?y2GUJ~yU`h^mB?q=@b;PLpHYh!%cpn)>--Mg zKcW|P?}~pnM>KQUpDbTBdI6f-N7tG)V&($)?*SY+HW6&Yi7P?@l0r$FBI@7sJbGYc zZ<_h@8|jl*#>?+>e(;TcbZ!3&sMFt^&b4&-3gwwB6Cj?76tvKpRMD}rGsl}arjL;K z9ym v6g+tgEv}NZZ`s&b`G<4!} z`uYE?lJ8!(?yQi{E-EV^xeC!pfmUjb8tM!e4gO; zqT?$cVpo2-yLWg`4CS{%GZud=V>CJsG5{U=B-l8(Fp_+-*qT%TOpFDgC%NM6NvF`^ z(2=?Z`okZ0()8Ky)3_hKMmKz85#4y-%k;?9cj)!seL>ay&6MYYZ>+BA*F_4@(Y=xf z_Bo&LkW#w->aKDJd0f>8Yry-=nfxAuH9j@>1Np{FanJ@lNKFM|H`Q17%Ul3Vh=c S7&moLYFtX#AfoC6iL{7uwe`aixx$4`2LF1wZQkO!C1 -yze==!fzi(uK}D=QP4AQvb`(rGFgMP2NjB*|?9&U;hm5A|r+f zT4U;LvQ0-6zkkYcWG+B@OlEMxu~;k(U`^zNooTF(G77cz^xg+MX! k6v zV} ;hmD0AFG wGfD&Oi)+337sqH| z`hE3W!mr#M$C3Az@7&dw@FtH(5$B$B zI;cMMxjZG`v)(YaxB11AGZ>08aH15>jx2 FAbkd2`MI9}2+uo(p=#+=G-rdW~1riPIgHYl--is~cu z#|?YwnRy@5@bA7tJ%`OA+#!>nT}iLM{RQDGN5^sGhga>-Y%6yNo{GNq{2lc1^6|8Y zKV{)1?E0%Ops#d1jr7Bt$J0T^oSuBN`pgB$G9ea-g_68M#g|BN$a;v4lV&$U8y~l# zs+Qhfi#ud3jr-wi)PLklG~}Lz^s}emqu;IBMtDiXmobQfPgXZveIY&a&HnWAw4wCj zlDp{F|2v%J0rI7d<2aycFlEiujA642fP^V{tB6@>BmTlw@R)ipF`$TSoU|u1AOsK} zjvHvy2zq|(&mU{+=%Y{f&@}$oJ9OeQ>UH~UzC&K2$DVyp?hyR!-EkbbL-1(is$QMA z0R5@cSJK}v=qMez3xNIVpp>hf_&`F4H_AinDKA7ZcFbjSl9(2Q(uXesr6W36Q6qQ4 zlo!|1n1A^t_2z {*YUcbvHZSps9hnRp LugHOd8RcWRdF000HzNkl j5Z+)jqS=*1^&fIh4t{!@kDgA65j zfT2EaVvYjv;`x5WVu3s>1mAHuser|i!?Kp`1n|&uI!@FU_=O!XUs&TxmKd(3$z5|D zGZ#Q@Hhns5-UrjUY~(n$ Bq1Tlr7%Jv e =nmG#)QnG3)#81fc~WCN0HV{=K% zbjV>dz@2eo^MIiaiH;NR8zQ=b#EbE9DSoFe3VVE?bc_a>3qX=UN 8=;P4l zE`Uljzaa6|q{jGsjg=zl= qx2mKefJC76Mj*FsQAa! z&|T6EwlI2h?ZEu74!k%$Y?uCRln*_AFo5}v2a2*z;w78pr3lIPK#9k?&-U;W%5Czj z1 F1G?tC>4|)znCylHene7inolszHGu$)-(7iXPJRp*=EBn4=j y>mGG+ WNASVb2VfipVgpf&(!j<` zAj)yolN^{65aEPtvKJuZ(j`%xc|Ki__{|4gNv+Edcfx@xCh@StYy$dV4W{+Pk(*rr zwNZ7Is|_RJK>Q{>pLhm7+Hy<~EQyC2G0@wfY{_e|`L%>Da{;ia07#Gm5jF}?;t}}9 zU+oAWJPZ;N4-P0Mpbstr(|TgcnF~OEqSKR2?x7OGSF*~AtF?E+g{cXNha3D!)<}O< zN7e%H{_2k;?TiWh(NZPb8)xQ&*o?%h`ewf{Fx|04W259M`BrxqkhuWVaCoEZ<9A-7 z(fi5_16dgo?!n@Nn1ID3DkL4C6Y#2c>I=vfwh2(ukyZc%Xy5?#HHWsniiGH3edbgA zn2mtk#h$`bnYD7#>{h4atgNlv{whz2|M@R%yOdS{jzcIwU13xC?1tKXo1n-7%&dcO zwI?`cNvx_1UTT1ZU@d5BsNKJ*c1!teUIP9lIC6(HuxLNi3IJyWIdY_G+rGn_m)>7r z*u9xw ?_X6Q1nw@_-(BL5`5COvqaRJt!t> z1wWJ2lahDboC4&XtCerbKx+XkNjaXX)&d;QY+L4PEr2B{y6RYu{{jF2|NopNAyfbW f00v1!K~w_(+KVNzMt+qK00000NkvXXu0mjf$UqDp literal 0 HcmV?d00001 diff --git a/src/en/kmanga/res/mipmap-xxhdpi/ic_launcher.png b/src/en/kmanga/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..4f1c1d421b2fd38d15dec77931d7ba034c9952eb GIT binary patch literal 10677 zcmV;mDN5FfP) &2oj2jAkV+TLr{76 zXb}(*1ccBY9a0cX2qh2*sifV0?|*%J&zjjYvuDoCnR9PKxaT+DthK*Y_MW}h%$zgl zmVVUt>YITIGtgH+6(+AQl&G(O`l_JP4D=OHrRnPn?kk|a8mKe_eFao$`uc+V3aGCJ zD$PLM1yrfmzTh=x2KowUjakZ7b0B>Mv})#kjp? vQ14|JBBC2mVu&P-w802x$BuNzmM2|q!r}yq0 z=sNV5b?6sB*9=mq`Zpx3HyWKs7%`%0_wg5QyzkLB95n8vTdw=!w{Cywi{F~O;=pg+ z-fS^hq=Rn?|INwGRCwWFdUxw&nvo*k3q<6K`wY-@$&O`>^i`w>A4P z_V` G{^3AZ%IaLmok zV*s&_aj@qx|J%GL_yEuiju!sto16EvSh2@Ze|l;6uitpxuH%1u(2o0`y)n@UoW#-E z)ms4)S8oBKa|t(>XH@opv7^o%%R{%ieo^y)o8Fk){I!=~dw;7LvljKAF>?`FEEMTr zmhiKj_RK&Jd9jDFkv?7gVi5o7f@$LGVmjLYT1eBxKZbuTplRS!0Plf`7k*|OQzd?? z*c9jky3H-VrXMRY-f3=IVom!Mu{)R!9&N|h_@N)MO?_n{rLllslo(*DNZ>{Bdr|ZN z{a<=@$yT%9TKTp4E1PcW9J1~8JB>eMtSlai2y2HL(Df9$+=k$C5s^1yfu{W@{O*Wl zt%I(gJHKVWg-ePXIkcbj%fenEL++r^12 PnwC zTJgT%d!Yn%>KO0<@i)*9s+B&qM7y>yzYoy#Hr zNSxUr25bR0KeFtW$2*^Rd4fkKNcWNs4zPSYCXZJeWD>}g ^ f zs8&GIjh$FjIUq!0W463KaH}U=Ow$xjhPhU(N#NaOB=q_U8H`o5#OBZAXAzohfwgB) zvd_$8EjPMK1r%I>YW5fvnctPU*1^}Zr}HxVW0==eK0OD$!Bi@stO4392APLlswaJw zhcs88q=U4L%~Pe|#cc9JDqP+cty&_fJ`rQJ4>FIj`;Hg9JvzJBwo1!Z+xJKTVMz9X zaJB ^BuJ1aq-InFBwS5?gia zp#lm9nln1)+CEv~%-ONFaQ%_KqVs!N_Jgv|JLaA~hH3@m++MIa7TH6#cDe4z5zF4| zc+j3((oav`jV=&e_@BFx;)0WR6@6FX1>jE^MHhw;zf*Rj3xxmKsk_n7P8}7)g<=CN zKU!=7B>rcB_+NDDXo@gObb;6=iA`~l -P*y5Lbxgb&@%wD zBEx0p?LS5n4jD +r&k2WpR`}17m66X0hYK&*_46N#%^EWk zGsieInAyw2CI%xKwX0M>_A0`S8jH>2T-7Ymb9?3nkG-iqAxepD6MS$V;ct#1M~&{)ow8xre3gVhWvp3GlGU z2ou3wJcYh@@xw6~Uk`ufjV};>{U$yO6poWpFFXDeokaxel4Y)k?~ww+KuqyKse$BG z$93=M?51a?&!a!ff@9Hr`11p=l7aZZ&&A{iW?7s1h`i&$*@R!#>y+Hu*Ytlai1|$x zKQHcol_r;g -p#!EZ z3M3$^*;I#g`$roNBe&Ur4jns!1~l0v!POVP(c03|4s!_ghjfR8Hk=><=z!`f!2#Nq zBSW)p*wNM XFYkCVF$7jrftk=E-Tv4`{Tg^ZVa Zp=ZRkg*DL4L)IKw*E|P z6V$n{TtH>|XU1TGI!7CXF;~gqk7X{$V<`4SfL0B=%A<<|ci)0^2raFhbp4-SqPg!a z54m&?{_&>$@|jhB1@a9KR~hA@M^H8{VFv@*P5y~7Hd!$%;hl!Hb*Bu `!xuO&O-;3|iJyTwz&WvB!L@wj(?7#sI zWmIs0X*fG@0B#-*U0SVv1@d&s>=prZg~vId%)tb@UuX_Uzeg?`8 17P;&^0Di$JD83>cqFaA?jSQoIEIIwo {4!@)=J_Y1pPpevM1O4#LXy1A?;ZQ6zpply}P zK{~@!vyreFC9wk!Bxo6jykn4}k}YOZUr0cWszdt5u+J`=(e7UuPW^&KUa+KvZv4y3 z^uAn$5Tkiq+0(q+R}sBh0Z9*-On?ZLt|0G=8FncN8{qX3Ysg!78xxWx6tieHi4$d5 zb9OWPbAtt(AV*DGnA02rm;+uV$N $RwFL)0Q`uWDuV9{Ko;G zr0urZFf`#4*9~{PM9nQ7=1LbDR&%9N0TnqmOsorE4D8D1UYr6a_ya*R6v{zn)g5f^ zRZfE~FP4k}zz(RtEAx2YIPkm&KbkRUV1JXQv$KotxPLZ1|MGmQ=ew|E|JqLW9r78+ zXa-Yq*W(3LWIW9F%*JssTRkgIC&6cD4q{b$oqS}#$pC^30pSW*0GF$wd&YsQ&-i^u z(AJx;7Y5Io^FICY?tha0g}2ZUD9rwTnH$3g-P9#|tbnl4K+#dj+SPO|k}rAIrIL1+ zoAgwCMPlpC)} g&q!U$gNkP7fk z{h9ECWS{uhx=Pb4uP-Ki8Qk99nepBy-G>Gvr|PaO%e%2<^=btqy 9X?J6joFISgg4Ci z*gkjua&_}a7Jv5eIh1yg?__CWgjL7%1#)*Bp>FTOIL?s(u;dGFI5a&;=JEUZ!iWsTaLfq@Zv9DyN2xV{-K z;icaAW;hv}RH|$l9<@JlC1a5j$shsfN%(jvDcb3?o6v4Md@Ouu*xcGdzrJ}Yy|-v( zcsy<{lgHGR*@Y& HyaccHvrB9xhlnf(r6#b%~eB$|F89!OeEq#>Doc9_5e&?7LXxyTx59-m=D2hrER z^lAD?@P#Qps^9zA8}x|z$|8vK3Dcd~g;wTxTMf(suQD(&2Yk?0!EAXZR;n)9BL#$! z;OqsU7X{9WvN1(-M}GK@DQcC~FvhHys5IjqW92K0eRkR0ybpq9(Ng)!;;xrz>GD=$ z_Ael_@EHlS#*pETE;CV2jGL@d0pUQ>z^%dU!mp>}^iX8tJRzaK$EH;1qp1P|_&~L{ zi-a9E_*kG70UTP80Q4llds3Rd@M9XM9JK=t8Ek)46}}HV@g_YpW1e|vh_*Lh#+l^v zM|>Xdvw<^R_y&%I1yQMhn6cPN6zsW>n>{)ltemQ3KzT16OWcPUyl^sOfhG;#mbNmd zxm09^&%gow=_?0rOL+F9G4mESn=ee4EpJmQLl)@Qjm#K>>k`*X=8p%5tx5$X>k%d@ zqnLsy6c8L_fyt>kmKqMsL0^T&Rm=%rS>R*)px~h)q9#8ui=LV$SD)PRG=41T+eM=e zw^9MQ19Ek@K%gh#`lj{hs$=5P4(8UNwDGYaN*M{>D6(^PX@;BfkXh?#ZRrk_Pg=mE z11!*idu%}?x88uX9A1BWDP4Q(G-{KFhVV%OvA%IwzFUqv9swSVO6Y*LRf2N?Ui)$R zdh{MCAZDGidWK|Un3 zv-+uruT(&CQI*T8>}8=aU>CIp17G++8c4%VhJUh*PuPz*dp7uhh6JD|A)_@^mK{de z0O&51gS2CHt&u+Z3&UyL=q $3qn5o0r1$Z+VkHCVy6anQEGDCS@upx|DHMEMuj3_5|-Jt{Ni2b*r^@c@X zS-kM_eDm9>$=~tPUS-Il#>c~K+ssVNEOyV9#d};Wm6699>)KZ;psX{X&`1g$e7OT= zuI=BQ@#7pi=DcHK&d=X1)wRQ?Ha5Ry(O #ua~jp5B?i63rBt^K+&G0z$e9 zwBn-9u5vr5R6tpo-9>;Ng9POtd}Ku&lRP<&D2a}5@B!rBfcrt*7_?xw2#A!B8=I^? ze3B;RAc3SEZSWFKLk9JyBfq#UeSDLlpv{BAzfO6L9((p}8J_ Jd(rPM*pIIH$yhq!@Xyc|!`C4* zFAX}SYNc_8lzBKG-*_nDD~q*;3b VECwAydc|Y|qULUhfdiyP`VNGt!mqt-vf*0hf!qP3w=jPr@v$5KgC4l {`-P`>8f+~qI15n6XA!p@G*MRj}9{n zo6H*Z)J@D$%k^Upcu2rChB|xzqRl=!l*aA8B@LD 35T7 z(Bm)6rP*)2PfM1zQcGJW;fJ$qK(}0(nAVo}ASf8z)^N+aY~t?p+-+Z_dwzQeopIcb zwAap?(x#gX3m@{?Sd7V*jE+@#SE|ikIjv=5xxJor Z@aAenwZQ-9iV lO4;mO9 z`lj-+d$_y_?%z-PL!Ur0iD`?S0Mj5`{U%QM0&TtNx|WdrOnKUT-{Wr(uAb6#HpBrA zB`R~86FRmr*Y=48NW;#(C_TJIv)G`H?7(rrK>4KXEKo8USDS@PnrZ4Q^Xab-zeZQz z^rHC#oD fkI*B}yiIs`myFdmJ|(R;Y!ETCQuG!1()0FT9z@q&xE~!k zZd-DnB8N^MWv9<=LI;i6I<(;L`P}u;Z2ITyMe=4kq-!cT*=jnKaA1y(twdN|uKh|! zIRQ9u3)})M9CNOUI5SG$)*3(~ Y-VJu2!SFV1%nV-P8o;3q2|(`+R4O1lzzWqai-%~1&6mB$#7KxZWJkb`2Y7cr zIGfJ<<>U12^B<%We{{e3GrK>({#m)@%`^pttC=J+S0ZNQL(5{2e45(z^B
F9$$O{3(?Qas$;Yo|>Kj}4faaGSm4x@Tzq;*~Z@xJPav zBtiwp6E>2H 4QZU>6F|uNVx$!xx45?q0U^mu1o02-(fL!azzzt03a#INz#9(n0p}|q$ zqfO`v 6K>Dy?<%C^`r{_9Uu>05H8y5^Q?^w3jt=+!w(Xz|h(a^Fiav-uOe zxQ*eV vviAmB%eNWL9Cb%#LN%|D3Z@^vk{#oPsV>G4>Yfp zE7Wg(Jk~su!as2twZm{zP)>-6^X;4|O`CI#(Rk8 Tmd_s`L|Z%n2!$J|OM%a!UkH&3GnpPXafl;go>@|K71$ngCf3I `KQS zI+AwT{-d<+&_Nn1!;fM^(kS2o(^w_DRZ>8<2Ff3}DTsp8#4P!N0{$##d7fKVzj#wV zZ{Z4>Hgloe@?N7W|M&u(HtAtH{M5VYz>_A^q-&q1=Vr{Kh4PJjYikGY?y+w0-f4q% z2h%4%u|Dl3-{qZf*k|a%Q%BJsCLKWcUUMjY`>UTP_jk%-!O|#rc;eY}Tf+*-o#dpB zwgs*r`3KWY(P| suLLipTBq*8u DT`|l^&2=-fQ?SPabTd01=Ob%xs=1;gj3O z8>~g2+H!sQ`ekGD$&Z|u`a_~$$Fxe+h7}OAobllyusPf80fJYA4mA}_5HsRxx<(H0 z2O=h=AtQ>5nR9Lpwir{v2_ibagu!XzS=7wem(ZW&+0#$tmiO)RAE2Yo{2QHo;Xml& zYoCz^o3GL=`TnfK`S()@XJ&{Ln3Y$Va_Lo~VFiTAR8% 6pT7`;L)`26Mg-PJl#+c@!{DKIKi9Ti##fJF{Q>;W@c7Jw%6`d>0-3 zojc{R!E^NV^m+8|!j;t0){zVk+cNx6U)4&b0>XhPiX4bLsnC7M$^&1uK@Xv1Lc*>T zbPHwT62)ijQdn|8(BmGq1>OQzD9a`H){cyE3wSV)fV>x{PPytVlTUeb-d#qIKL0kI z|LZ4coV<5B FK?Ok#;5Ax-V4Y^kZDiu)K0W;^HwR!oNoml9Kj38#~ zkHsQ$RkLlX^g<4xd=lm$4id;n(I!vJUwU G7@!v`Fs1zK@{2Lq`hl+000S6Nkl rt0pEg&38IJs!l0lUb>1MOne_Gd1(V72lw7t51BqDJJ% z@1arFh82)IvCw0X(V}nwQFAxMsRML;LNZc |%lh zL``kVL&`8yb`ZuU2LOq-53q@g&$QZdl?w<5ZH`XPJWL)yqQ_tyitXHUj~OjRvG{O2 zS? gl^=~x>Qh(wei>y>YI%!5WY>0_TESS8=BHJM*@>{an zUJ9{V0(o3m@HngX)9Cn01ypu0?jl2vqxdAo ;XqeIrCI?w*Kd1eksE|uuH(ABH3?nl@DUq1cFP3zr?rPP(a^O93I+sl zb>#*Gezv{#5G#kS1vxN?gFe9IL@8@u_|tijKYSxt-nL!cj>qz@zBc+>+ryjjC_jMk z59k0lHmT!}Yc=5Q74_t+U)AI&41ml>87xZ@RQjb0%gn@VQ8o!P*9>8{KFn yfwp zeI}-HwL*^<5Oe%;tp!td7MR^U_J{U~zyVbARjOa~rO3&QoH=G=ir?N1wMOlFynrxU zm}mfcRoDfonk75l`CO3gBo^k{h09#qM^b&Dx5@>ig#np`GBbo6>JL4F{WPaGpM GxLp`nt0JPE^k&Q=HiQt!pzDO@gF{|S^*W9FL&aTI@%WH zZ%^pj#>&G4sXXZ5d~j{h4a704{$Uq0K`PL80bbkCWeDQ0%!fzzcmZK5?F3`F?58J8 zHWAK#1=V~p!#8q6&Zt#egFi+<$OF^}fq$M0vmKB1vwEKYnriEX0>b1fs!yX`uyT2r zrGKDYSWWNfM5yVy#yV6gAS`AY_I8BH1 `b^ztjrHWuUXd&F60^#^ZtXlo+6JIxx_y*Tw25IXGc*SYk$tVzXU0a^BXc++V; zff7@8ENku+M<&DBxWlZWPA#Nkt*w?VAatPUYI*<7d%8MX+psuE)b2X+K&-ZsI@;Qy z`eaG?stvje<+G&g`(Y`b?yk1B_T_K?Rptc$84uGeYPqd!0U U07vwPUUKWc8n5h@8 z&{np9G&wrzY+w57!p Of1Gk%XX zlwoQCiHhITHGp0dWeP~w*A$Or?jUMuTlwZ~k8~`VI;pdD(c o?!szUY~AyH?D3qN{yrJ9V{Dw|qLpLYZ5T+=>vSdMG8h=*EoO zOT0Mii$xwq(G07vU!jqqXu4XdyM0-E=gPO9=veU7xovOWdbh-4{Q#^b))dPGh^g3C zwt$co9iURtX`2u&g67sa_e^Pd^X7A8aA$JMYkzC%d}nS`=X+fOsvq+t7I-g^qIZd2 z6rJMhU_jS=8X&PBDws8RXKw$N*Z )d (hyPYYwnBSR-6{ux400Tm-O|vP9*#dI|`=B#Q`DjB3Wfa3#VZG1z4F z_qB<7YYue&Apxuj)&^^YwZfXY#iMJA^(~Bg3kV%(hmfdX3 n;1n3F4jOtNh ffpskoJs$0BA#EkSHd=)zR()nTK}0F%Ot~0DVE~+CTzWuS#fC z0qGzxs3b7(J`k m1;LJc@0ex`RYPB3`UjeO_%kse)R9^vo zaMo(I9BE$xt(MEOIu9y7w!Q+2&qrUY 8Lb8KmqBY`AU-_U0M*7UZmI1J5m%7qy?l(73n1u4N{aM zE%e?)Zy^aG-{w8%eE0si&vSne_LF4q?6uY`V~jcXiq+9lp}EF<4Fm$wsHr~F1^#zl z{ZWzwue!5N6d({iNbQ-TzAw=ZoFdQZpR~P>AoNGl+qZ5Y#Q4@ 1zTI`a3a)LC0}l1rm^u-M5~B_nGK50B&co&5UbS1q8G~V7SgIFHoznOv z*!+bj(Tfe>kaS{+?3-5bY>5O!E_92ylpT>eF~BQg4nnQ>K>d;9SC`mUd|^KqNIXjR z%_V5I#K-) PANy#?xNV#l z&D^YiE64d-$quezA9_% TZQ5hYS)n{i!XMO;{{08aRY#-z~&bPxhQX-N8>-Gpbw+ zOZ_xWMK?5sz~?W*y8{9&+R~e% I-m(PN$fzhU9(l^rxs! }_gAvuMU!t9_b6ZI}V!xuJa&Q$g#qAwhH z4YS{2o2eTl_dlA#Fi2E7M~g2Whku=pZ^!Jip|K}t;LkrU+$PL!iZzmIYp&~*q-^rj zs0)@tXoBEO_@5tKvpWvMwJ%X&ML09}HKo?2^=|K;kGC5f#<$ tDcxv)^mI$pZ=7;v`>Rk5w*POcHv5*61G2fW z@}&74Zuz!1J|TV=n8k+Q)#hzO7n%*yekC1*X%;vb`gAyZWLEv1{aLhh4pg`%T1@2h zE)QX^TIOhY=2I1VD!$s-F5U&gZeA~1TJh;m=CFY |U2+>z5#Bc@oab@G9T593eZpKHS(Nnf<%cyAo z_I66yw*@jE9ZAJrsTe=+$0&?JGA@K=H(6Yp))M_Vn%1LcD5O WCNe-%9rm zUc+ITBT0-~v7JTA_d9}A$FxQm9CYoT+V}tFWW2SZD&;on5kGw(swb}|P(A=V)K5nW z41PfQfxAZ{#~2}AbGzQ7U&c9nvA;Wx$Xy*R?1Svvsd6*%_jIDS?aWq5UP69c 8$c^J`Bqsv6V0LOycBG6g(-USNNm2 zn*2wL{+M1WMTzn;-3R3-GDL@$Ikld`5$gMItw0f=nA7}md7^xx56bsti1KNBzvJmC z{pCgPD88ZyyKyi2xp!DbbnoX|N_oC>j!@dU22Z>;Vb^1!BZox;MkOnEnNKAX 2p`6>M3h2uBFZQTZOxCQp9#EGQrg=Y}!ugL{a2w6mSm}dHg8Cgipk*3n)!G<@ zroS9)x{N`$bx=`2HapqJ!-{U;J77wKh4c@FBc3lk(BxC)8ZRsgT(%t4()4?E^4any z&NU#T=aekZkGDx|K3`47qUcr7>(l#x7VP)woH{K3mdRBvk@>v}?;8yX4n-;cB>D bW2QSpl&XfT)Qcdu8fJ0g)JAR 6@+)ogbCKd_?%74j#B}VbY_`N$T(2Nete+Vjb(~pe{p(nb3YFUvJl1yaF zRfpw0Xp`Xj9!)|)UqyX=RgZ+y-vN2|Ici=J{zor@^%-r#4(VFpcMu$`t93mljQlZe z>^A9Riph~R7JHZR1#*MIL)8@T?0(5I +PMf zO &+|iCUrueWjj ~Cd?{pqC#Z0uaC1PRDOqq(}TJMr4 zK{Tf^hOC?3(&Hs#Pt)Zskk#~WX}TJXVU-!%e6(Zty505zbm~*_t+Lzb{6Cz9{P!7e zrE@;|Quu2h`wV_@_kn#;O<7Mn_>09lu`bkH*Fvi1-}~0E*0Z=o#Ch<+k~j3S>--n` ze* pzZ+zz%Q2LWVEEsAjh8`8f`qBOBj18VOy&~LO;d?zin!= z_lo1_E5A#vI_$$xrq6s8zR qu@q^0rJEEk(Ub~uzPgc&>mZK=oHA7g$#))pN_?e%xOJaAcB z7knt`U&@~f+GJj=tLS4|=rm+E-@9PWo!^B;`!SVd~MRh2YprfgK@H6|!#=GCmL zHUp7Ca*aVC{r(!mMyAyoSvR*TgL !aQ9<|?=T54ofP9i)YCmABflXb zH&3^HnsH97DqtuZb0o6%;bZM`aZ-KVs?iRvBU3rKxUlJKb^9J% zEW--_=49lk7bi@^g%qEc<|#(XR84;+lu13)Y&m)oPu4A^E96$iuZ2PM50LS+gE*^< z^Gq=3O=KlMh+c+gTbq3|f|{(_n2rtjJJAT+Z6O>(%IMm}13_uv3VxE86l1K!^xL6a znpBqeTCfj^tO(qiQg^f|^x1#8Fk1h%p!fJUzZrntdbKXa4$1Ovv*g3d64}4CxyRlg zb9jDoo;Z7ezto>PPHQliQ1iJaU(YC^2!7q*|5N10>3Fa`8Yy0|K28%=NZ0$YBKZWk zZ%1X0mQr3%^OARjW$BL=L)tBJhh&zo>y65h&pSB%x|ITGntGDF)9>~^Fg_$3FtGp~ zQTa7+DA)2Z=_LG9`MNWw8V8D|FnkM2A_WFHkh!BXk)Lgp0NQPlP3dG63^{vUl=f(^ zp!qrb*X59?9LRLYkc4JEsucHp%UPz}K`%JYqp+<>?AF>EQ+ic?;0QEJUoymu!vNOi zN5P*iiM&-3Xtm}Jq$)GLioghypE1)1DK9Nt01p(ZnP37T(rC_qg)d|+j=fVD>zYa; zHyuwmn01OMReVzp=UfjhtIo`KAU4qFiIIL*)ylcfbu?#Cog`Bq`eBGp^^9HlPiXD1 z-a3!5BQ $7%MxxP8ne%UAo-SO> z&}>PkL1QvcQeC&R=dod>72sZO8IEOP7I!D{2j{EByDg+s*n-3?hE z`}_XgSXetOVOj>JF4I2%o6_*zXRkxKvo(7GpUEcJ(|Wc5uInZmq4WI?Z;kRX@T+-u z5USm9a~>LkhAb_Tz?O3rSMlod2stU*GgR3lM%qKvs^lcj&{UAseOOPhr|T#{OOW-* z1S!fIIGmy>RdbMw+_RG!OP{t{lC#KKp|3VF6>LMEvhKT?J0FUAPnvLq5pAw~6yelQ zD2y#11M$N|IRCt~UZw^u_eW*lrY_hgrrc*E5|yu5B6~S&kE;wwIhX@aZA+f#v1v-V zg#^$FEz+a;g|G5VB^@jfe3!28AlK=~8s?UUk)Y5LoNzEE^154TF2#Kr)G;3*+BSq5 zXQbZ8N3%WZ!C9ckh8ZcEAngVTLRu^?#@@&Wkdv?{=~?U r3%Y0z}YA=4?N!DiDai{dAZm@wzOd++h zQkvc98#Vv`nOyc!Y})S+N3kB7ZvDK5LjTF<{4nEYR(=>@n*zdo7A!#I5D|0AA?=<6 zrFDFHX+WQFIJ* (69YX+^P6NiLtBJKxA8gliZs-xc96&_nO#an7uQ$}y0;mKv=!FIb)whidDo&b4h`|NSJ zGxF0rGx6My#n~q)R})s(ym#b3pp1pd#MxIt@-2-O`#q#aes(p^X4(P;IOUteQgevJ z{RPhQ@#~nOmIHE`1AGq!>*{P237yO!t*oHGp5`EhDjJO7Zd->1+3aH{U4JapS0`;B ztK`1@`W)O{jVW2#5hr+F(4WbRt=|_AvL+}8+u&Losc`UpTb`YLvmZhGc>Ccs{v|j} zy3erELa!su!gQz+$w%r@l(gL_lB)H!z8w^DPUg}>M)Q}_0(s%KH7EEiSorZ1JG8O{ zTMH&Lilhddn`PGDC}SB24L@C q&aCUNlh?*_xq;I z{zsN@UK{L2n}r+x^F!W7NyS~#wOjwDDlNg)=?BZP!k`#|+fNMnJnuYycK_K^25Q4V z#W7HL%$FK Dat4 z#O%iT7}5DJWLDGe*DT-sD8vY0z>hqPsRCT~u^Q}ah178x$vo$0CLiV4 O@S!ji}Cll7Sg-+ZM9p@!xRQUw#8SHMB=h|C_ z^j{@`+OAuWTsM(JvXIx(#1%N|w!vLatph|1$*OpG@5Dh^X}B>T3zq|Z^Un9nQ)x^l zju;D_I-oB77+Y@h58#~5>S#*9I?)k=!-Yugc#b9vQ?Sgm-hn7mI vM)$s&7Nwc-_z1* zO)Mm !NJ} z2)ljIy)&E=G zu%>)-lK1tT~^8(4cf7Q+p2F_E+zo<+S@Snc?lwzb!r@@9=Fzs{{GI4Y5F0t~2p4 zoK?Xk3d39;+ICZos3%voHPOB8PgaQo980>8pO;pr0A#PuYiJgB67kaC9$ISXwM<&i zkg6vLd=evmc0G8%W7OO96*LYQRB+8gZjeE#E(cd>j;_V*+w6LG0)k0`WgID9uLrJi z_Xxf6&LjqR!|dT87EajR>)#W66&L8@BG>N$fX2mLX{_s&ok$p}O7RU{JO%w;!WV~^ z<+6spiWL#PUepL#dTbf$EBx{TbM~oYVQ>$ANX~!zyf{*0NAwa(7%!*VEoOMeb`J>! z%+znhAKu|M!vF!9)ZB0@5#FVB4mYUubPJo-Ojel&^$h&8#dZDMOw*YzKKdl&OIg5s zChFWzS!q>5#7PHzqi;S;pi~rTn%PwhMfC?rAV @o@wSeEp{z<%!~>ZRIk$n_Db0SEtof7h4@G!x$Awou8xE!6 zy!G9Hd>6vdW$BD(7HRay`>!-?*b%*F*UAiBzM1KtY-BRXZ`e-NcVCOouZUk5SRJyi z(eD~+M$N`LW?^*^Jm#3E+EwXjb5ZEVhNf`v7w4dkF5$pa*H-)vLSSb$er5k+Q+m5P zF;#oEI9(Q?TnR7zPWc&c4RP<)k%1y`#6MgZnAx-x-YLSkeks~XDxN5;lAF2;E`sy> zjMuuL24=t-TBcq%pcUyf4b$5V0oaLnGm^9{$p35T;#ck2k=%~spV6|7^X;?k?kCL+ zR%@ADkM=?TWnMrivel*JTIX_Cwc(BNi^*2=xLI{dmTc%X$iL?v-6!BO k=$GJdo~B6uh6s z{OJ1E1;krX%haOe$Wo=%WWnq_^Zm5C^UJfBSu^adF4m|4j*D+Fw@aF(c*rTyE;5vA ziw3U{Kww^Z@c8@z*`4n3O=3^;pqrx{53%bmJfOyGQCbHgu{;{Zg4IN?(SE6?H@T zL@Dk2V;IJ&lr!CzDeI$d)C1XH!zj&IC;R>yf?^Nn8!$|=m-D;96QhnBLk@nM**E^% zf-AR(ywafjYLXq}^t1%|0IG>ls^S+~ONBl9m#|shS^Z3V3ws(uG#|M*=B{u8GSG~l z;_e(`gc~?TGMzy4jPE>E`a_bh9rcKd P;`5xygr@P&o|126;1fYCyy}|uowJP qj_kee>k1=1g@UP?@t%89f!p =X~qonmL5Qia5UoL`83ypvp9@xSnii(n>`4MgE43BF4W%7hYRnL+t3 zAqbs)uwkae5tJ0^4q+XD1ou`?BlrzvxPaB`=hc#`Nj{1Nmb37p(6{mm+;y>&wsw zLBd93NWRcf*3 e5e?566Cq}b9CP~YcET$Ml z4P@E2%6qT`j1+K)Bpa<{K=9p{@7C+5d{f!ThqsB8Zv3kqKBgN~D&{903IKhOW0eG` zrbr@@8*u^v6h|KJdCcyzJk85D-9dIkh3li=*FQUoHQ*l;B+ }gC&kRKkDkaaDUojd;B&}iBueN8MDd^8X;%0vdN?W*@PMtTxkna4d z;mG#0Y=pO*U)R38__ga19R9#UcFjTLPq0Vm!J6{wj{EwGxKo7|`W e>}= z$bl&WBx)r p-DeE5dVZ AJpGyCbC>P0#RDt3juVORfLhZ z0=v >nVF9h(ye3#N`g*zGmtgu5yU$e=`kWeP5wmwy^oQU{9 zCLV6srYj}Dn932k%0Tg+FInXixP1J5`_R6=#FP_F+{KN2DbJ6^g+TI98bJiDedJy( zl!xLforY{(8KyB?7aFcL9E5FMjo>xKd9xvFd!^jpD}~$u`@5o(Hw&o&smGp;y|%*o z^)j_(#mhJUDA BqoQjd=;acwM; zL-Etmb^v9?AOEB;IH_E0h7AU< qI%W+X) z5 G;e@wxP@%~JD6G(XaTbx^v0~4TBpUaJ zhNC7fi2dhnkUMEoKn(3>wqem=SVhIHGEuLLCse6JDO{!*Yh6|uzUx1Jk&E~zg|~>p z*9<^z!t;JOim78C|BOtMVZ!=(Pi-}o6h>+I-N$+Fte3*U4Z7(gBq)6aZP(TqWS_*3 z<*F$T4}cYlsBhM~X!RlDY_DyiDIV{QP&{y5K6@C`?(2e9CzIYa%d3{k fdt!nwO;s2?UQCN#NR!VKuNJPKt_wpGf!6uaRK|;h66sJ zxxMR=cKAOn;AKW%$awn2GLf(Q$;sa#Hu7NaJoA4Jj+XNsRq1)(`wzhat;Rnki@7QB zLi$#vPM21d nZ4~R{BTYCWMa{6H%#V7wT!TNgTGF%$?$%U;IB`BItO_4i@ivN z7_R@D4N17@t3;EtfBpg#8#B0@5wq=VnUVQmTzxs^$TVm5Np}4bWxvGh;e8|Kmm;gK zo090hkJA7}WKWKdG-fZ7Fm}y_I}G(q8q(dlAOsfS(Q4^4UpLHHyo2jCbmVVviwBXX z-AihsQbY3^)t#Ks4tYfsO%j^=0PN{T@o_tZUg^yOtzc&hoO43)O)X*q8@5RzZ2&R| z1~^~>=eEda^u^ZzonV_QYT}$^-cUm`!Es@m;PR?&41Am*dLbkK^~;|SqPKr*>BVDq zzAh7ibHCh78MfdspO9wE3P%D= 9-rcEEE zx;#62?o#Jx Kv%B63?tb;j#X=>sQ{j=+CRPI=eY3%U#>7ql{chdh8bi(8DP7| z*_1bgHAQDRiJhbO!ue$YUUBF11S2td(?9PVB(L&+m*MH#&@OlwO-HA=Hc1yM-}yLM z)42oAUjB`ubFj%W6uMEugmy0im5yA^k7XV0-VKL(hcHKWg*S8*v@w44)FemQ66=9P zb_OUR=Ba;n)54;2lG-|Mm0001N!oB9qX0CKb_3JSg~*IUd_u}C?|b69nu`sObZk@( z3}(B)o@~O8*h%n=Y>%ZDkE4q)UxFvC${&$0;DlSCpP?(!8#B)F={-N9JhrP9bN`;t zVAV~Qv8Mn?HHV?jvH>Je&WzpRkUw_*Lp FhQ@h>{0za7FSBm!S=GH;96b~^?hgw?T*j15Y4_ai@uxzTy)P3-wyEC-RU z&9hXyQgEEyA~0LdcWOar*naN@^gvkWaTM0xy7CTj{}z*$k%Dn3pkzxiMq^-_sq~-P zUZk0679i1p&ef}|8GT6SVMr2kzOu!!CD6EWDH*Y=TSvlf(a^%gCH|XjG}$2}dY>^U z*dqAJg_L$chTPHXtf}WLm9G9oT48!iA*EOBhb*9}{Yc?`&bSM)KKQ=zI!485-&X!d z?m;gu`YA%z(iR&)unzXH??q}4qhF1u-?j)*e5Od7c{IGE*3v|!kgOmWv2G%dS;^iJ z9~BaJskWEseN}_K-EvmW?AIN(eDkE7L_27fWLI1`C>wgt;nOh0-VzVJguzzgt&IXU zE@dXGw4pI`uA?*>MO^7vMry7pcXjrfU|oAp^>n_$eba^g1+?GHG<2V>=Ye_qnt &NnP0|50U%`@);@X(6iwiG6%XCNd)v(!`?hMzqE< z2V#V4Bn-A4JRgN!kYC@_Z_Op>Y$=+~&Uhb?9WHM;IYzg6tKg;y{`vT&tm<$3fi~I! z;e #z( zC-%=x(?ha3&&_oK3j`2h_CM9TSDca!LojXxy?{IIjZL#}eRL{w^#?aDFyu3M8kHGu zVYwfdOC<7*go`eW_h7fgv;lI#B|b2E4BcmNf7L6FZ0+>$)dEulSa~*hG+}4z^7G Jk`zd@ef!Aw2s8v-P|76`J@+*XAldx zj>CP}s{9Fa;C}F2g3si}B}#CVZ~)d>OlY_?V?SrU=a*Iu(6;|qfn~1Q_zEiYM}xzc zS^fFuKxLxgVCxJE>}at3N7VP#dZY*4^t_?{zf_f(Rgg!Ma15pF|(PutCRKG4n8852dwO7cd$z>r3`WxPH` z5?wys?MsReUZVL@MSve>$v5*|)IRs>m<}Ogzou1BV#Qt>D6TwCszmxhx!@f!&xm$D zN=j`2Lp-dG?Da5Y{R=HI;@~dj04kP3ucy8nEY)`U;46Pb6ZZSOdzW~pv%tP#@H2eR zR5W3z&--G(>Kj?3;2B$u{@hRz>cRX8m?cVH)=Qos_}8a<(@#4%W4G<+MDhOgId5Ht zlVr&y;xq@sZDP4UOXvf)Po@O+L)_OMovlD1Hdv1N@hatZ-QYf6^O`Hfne_>`#&BfD zt`Xl`>ti9iK@Fnxq1eI=I`ER(8}jNvCR$L&yY)7w%&l7!Mc$VXmj=ki;X~YlO0F{n zt9P5+>Dt0~=sZ^06?Pjev+A0r%QTGlubw=9e1;#|duz)Ehe1&ctuvPa^58aeb?Dml z8VHab1JtlR_y!61?eJ2P2KR9XnLQt)=_#hU;bU5gzw+5hckPe`zpuAooDkGwNg_X* zKCJ?Iwy6afk)tl7o#?iwhX1N%77cl(E1-WL$b
V7xH*@makpg}~byh?NUTDXChVR64_~t@4cV!~?pU7?OvpdkuyEA0TLtKts zeOxTD4I2}5?+M??PH_0_q?LY0rhlxxqDo0}Z)9Ji5OZv1i$PnjZdqKv^X%l~D^LU@ zeW1;x`Ih*8mDrtxe_EgAC~M7RbGni1ICD(21Z>QeJ$J78*6VXAD%{l7emp8aqtX#r z#Tu-o}Fu{NFKhyk}FF(?M* Fo|-T}hDNJ@OHFl$baIh}Iu@5+Pox zX8yU37oVS}vX&WH$o`7fF7k-;^O(ln$x)u=>YRdQz-;Sn6}A;;p$fx0$eAJ*R|~7) z_6tUtt+Ddmaut|kI;57*llBh%vO)Po#$w}*X$;jDihCNP95v8k#;i$(2587`&F_WD zj&?6z+#j5G{iCM~k|$JuTdmd{a0sMUBe#Z8nG=XJNhEnsoD DI@buHC#hKWKG|JnFA=_{IUb!JQT~-~dT<38|(%oU$zWkwE z```-+Z$u5# $T3BT(cQW4ds+j!wy_2Z`Bo{nMr9i1`rD =3%0U)dOjCd z7d`ojZW7`6QRWE(%z4$^!nIBBH+e&JSd#)1VeDfh0|-g00uTvPhv@8v2-3Sf&o+NM zG6YB#+#io+n3hgf@lS&1&%Arcenchl$jqt)whvK0AL0UII7+5uz*xtCDVboafR5d0 z@1|hQLtJf@IK2LY5gWJRENN%Mw3T&0R4`2q0i0AD9JI0!$uWpOKgLZqf}5k^rv@`q z3> z5*vQ+<;@p%g_wG2sc_!M`M%e$?PtA@Nw`B eOwy yvzJ*-hf%9m(Q^0njeDbp5+Zc(iQ)YQ-e7edMYy<@`*>@LbzV z_;iT2<9m*UU+OM!<5T&t%Sz~q3e%*kR_nA-vwQxQ&t(ckJWfM3)7O#k13MldH_^W5 znttg!Ca@E5`LmgTnJ9@Sa4c`lIKQQv!M4MPZkq3DKtEM|&8`EdHah~v{m#aM*xAPj zHd5^MIFI-3w|ZT{6=3D}l|QM!FvUP68`Fpsqq418Bc`dokRvVy%w-5t!4~vElxTLs zClm|el$5}{Y;-0B7}m-qqq;m%`+Ahj53pX^b$a^gPK6HiVKql?`&{@=W%cY}#~PLu zcNA^0=$e)52$R{FDuzAX|GuKU6NH}P8(Zh)6blW#RC*$#IMVmsXYSn*wdE&t*_^WZ zYK5^(DW@}!PUQt4Jb5WvjMmAl<9n;UErsuhuP+`G*DNj#*Ej%4aH+WaN{yZR+XHce z9W}miy&M+aiFB2mw%CCv2VzmSlb@M{zFk!6>OuTAu+B#KTmH5Lwd3-$z=~j+LotHj zp_0s23_lO52*HvxO03$-fyhp@Aj zU`#XH>1h1UQ90vOnCYj pzD;6pfvdl68@Wp=tG#YlQqnL zNXYva+Tv{qbR(@{g`x3U?n~YnXadmvasq^s=^LK4&VrS6!H|814wTTB7JowjIF~ld zYP!%~7X$c~yadOQ`fc9QIa0sESPwp DfL2kT@f-F;?$`Zeq3EP zC&c5Queia}a3%yZ=h@n95i8|W&M~6=IiLWkyvhUVS3U_fR_*6h9^?ETq#iikjEsiq zcA!&ur)MsA)6ag EYL266S}hrSE8kM?B^~@N-BEle7{C1 zO@Mzg4bMq<1V`_;{4Vd{Sw(JjYpyG`8|l$Jmuc7eMq3DkaC3(-V1eop4oP8wH+HrB zR#gE_y5ni3aE3OT% 6KeV8hCONd@(`o`MW=xO462UR)GZ6ZsFC z?}0|?X7`L+o&+Wwp!5h1(a }u_^A^}3l 3b~7q-^|-yWwnj{GmGgEm} &d-sDH^xJ9uQW^d$EXeV_46hTYHhrVQ8qf~EglK(ZYTLtk595+*=>qrA zXGQzj)<~?CPHbT8Z52KbGy=iY>BK>TU?al >{lQG)FRVG#S}F31Fv0cATEQJ&Ufbs;_2+R bTpBFwT>?~M&2+yZ!|ZmFf00Y>=ll2dDk)uG>fs3lD`_Nx(wBE zDGr&emKu7qT!K F*m zdz-y|-UC>0q|n*gzF_$Dbs2{rPz?@`H|J@;aG{aOUIu3_MDqsc7_hiM3UC=(nSn1; z8l-WV|F1N>rx;6qB0Db0?6lOZngR*b?gB_QB8MpR?g33~tgLbj{@+ (uz70~na@sn@w zI|=O FW0WgN z%#DLN>HE)Ql~- Aq6HX}YO6+U+9)_xWN_4oqwM#Tg#5x%DqBLEmP hWlv*Ril z`hN8^(8A8LO7$)!H%0F-Staa0a{di&q#UnL2@t+osZXQ=Ima1oFcn-V32)f~cCKnV zw)ZbNaGSc6bMS4tS>e*Z#P1;SxesE%^wet2wnu=6deg|2J^l0)Bs@H(ekJ?hd$Vho z!OMf?=8*Q6Iyth-(y6XtZ6gRO?BmW-RKIDphSVBLHB&OrrL;c0hvGvM4?1&X{3?hJ z^<3GEIf;oea}{rHw+H!t4=R5sPqM4~E^D52{C-I#jvDKwu;I^XMhXbBzp1{^Wk)hr zKgb(322BaLzE@sm56V0zj%ke!cKzkUCf8b5zEN?}q(dlkwz8l0Bk_?;!@kto)Y*e* zY!05h;&D%B_R}RSldffP0*ePUtt6{_|94KXX6|zy9sqE$X%DcIaQow=Zd}6TOW2Ee z(kfO!g2flAiKNixLFBc}k<2m3$2B5f=pP2FaCqXH5ixsv-bX-d=#*Tz7!_bJ*LPcj zDih0JobJ)eGyny`q_FMP8|WKPq-hk{xjhWCIV(xG`r`w1z7QR$-u!PFSf#XnZ%L>T z#JOiSY}+XTz9>lfnI$TzP|t`ZBA&S;%&8A_u@%PgCinOw!zUYV07#j0Lb@>xq)7fn zzAjT+fnE`il%#!3P}B;onwP11GPd0}+|rTh3`!PAM)|cvy1M&x3y&6KlHhhRN$)fz zk5*b|_#Qw8`7hUby(JV0RR`9WJeNPmF-`M_nQU!@xv-7iq ?i2a`KSMbiL6#DHaJ7)4S%#tyu15 z#WFZmq+jM t=<)a>gPAmWHSTNw&IZJ|Cp5vsCN}o^S zU(Fb^0KH!~6geyt@`(a=wu6xV6-{ITEszy@>bO>gV2`?>V06UUl`*==Wn|x$ux>NA zK+=!7=A>yPKV>M@x&&hB1>b{e$^6Ra>|0VL#u%OlNyeMYDJ25}Q1hRF+1a6=hJ#5A zSVYTz+ME)f+LA-bl~}%gZs(j(0P?swpp}zJClFE2DgCeC0kkAd(~wI}N(~fDxB7tc zdfm@WygyZn@BB6d51=)jpkh@osS;lvXi+A@+%ZM|u(V}WL)M;9dbwwh1#WfVkeuX@ zm>z?+HMflpKCKH$g0AUj@#~joUIl)m6Gts-LQL+ZU!l N$1xSR_Fx wevRo~Z~ z?fKgNjVCEZtHVEvW4ARIuLV#wA9p*Om}$u*I;%(5MfuW)D}9m|uHQ55VR|M{??80i z`5fK}bs?TjRN19?MyG?Z7dGx02P(&PDX7nlHFeVZqO@a4+wOxM;13f~ZCc=Z51@AL zSF};VVL=U)KRWkG3{CBhO4?*^9JV_0%dZ_V{41X&l7?3-ZISTiKFAAZgQ?7WIRy-F zg=|Lm=EOZ=KuB|5CgytZNJI@+JBr;V)ydJJFZr6!3pB*#sb%Mt7x~`$Ekiq2y$wJI zm07Bl)%04nvXuK*T~5=Su#@cDNv$j8hInyBEN5o+Xy6tVkTO6^a9*9(`B^c5P60hi zBDJWnZNb^_gu^fs`1FsM_B;s1WA>Lf-{dh1%rj;et8D!HYeUDF#4*1IYzf<@6WO;_ z5OWs(Mx|Qw*|&LBQN)-v1`1#6v8w$~2Qy#24B_t1s_bsaAs1(SW%=|fQzxsuT1+eJ zdBC>F&oQkGOt)ga;qI5GXVl+=%9=E6hpR{5?CIX8LrB*mo #xbSEgzU(Rpo<&r(#&ukZ)H1djU0D&YeYUI}l`QL># z+t%M-7qF**YfX?H{pJNgb%A$aV+KY|Tx&_L0|=+UlzO^FnCQLOruu^+4rww~{5+6a z{gda6_|%yko0*UDtj}jU!5t0)#i$O>37_g1EmbcouJw|QK3SkCETl=JyW`qpR=T~N z+PS$?O3wso0i&?Mf;?$Iply8dM=AFM*vt b)b~TpktYvyi*?DJty#h&+N|pTUSgP4wMgU^nutDd7<%~DcOzsZ65r{upBJ)k0 zi9cTmPRcEN8Fj>}Jqo#e-fZKAl5_M)$xkTq3dP _coP&a+VYP{SEwg4wQ>&sV3Avcp;{sVk z5K!ORS(qZB{^lagc_)8}eueWr(0HXZ2$*5Z;O}yyN8_{Cj-7PrtUpq7zK=C%B>WTV zU0)^WN>v7XG**r6^~>hXK$cvlYUor^ucm8Put9VJ;{Iw|UY5!Hm}TDG*~=IsR`qZg zXftTcE&i=MJD&IF$nIv$#!dR_YIBS5TS*>rG5Aj3{ligl#Z!mw%Uha%cd5Q@GY&k0 z-tq7haCxU`MYJ0J3U0N;-BWGREE&jQ#?VGZvvT*dVepgsnmUj~ahKoZmQK!lDS<&Q z=&gTSpx%~bY%v(uI>c5s?T)O#wIFW>PkwH{U9)~l%}H~whP(kACC}k;S;mdL_)!tq zHD$xKm#wq@?WYFAuckX KR=c;k& z?Joj1b-|bw+yiT0oD%CnV4}@xev8WgG6!viNjd722T}?!S$O>*D|cx eY6OIxJ4CjKjn2a8^O8M3oJmjl!R~orZ*GXpnperkf z?)1I~(9^)_%A(H;*e~>jPoNx3O+lt3Snn`iLs({+Z{Iai*+awQg@@*F>cPo*j}ub} zIj5Zna|U^7=(XQWkfU*7%(h(M=|{%_Lt&bNh$dwv7!e7!Wtmm+?mp?Oj(IZrDl%Wk zbqa*^rYJDKez4KFF5Wz0UnAk>LBmHaiw8yNLAx4NLe+VGMN+_nV^Sr}yiKz6kF&^< zg#;PJIB8yxydbe?SRJ{aXyUzF4S_G8X#^K!jm&%*7f8<_ApZQQ+FCJmqt#A;mTKQl z2Oc{(^7_{aYub~9RZ*{*|2z)-H4XxiXkG0WutRmO?Tf51ad0%8-NqGaU(5*O2KrIK z`0b+fU_BYall>RRU!9K}u}#kFJx+`YEtx_>g=Bf54xP-tokuLe0rMZ@+YfSYQwMo; zUc5~Yg2D}qVDQVba;B<|ZD!NpwQ+`Sas!wbE%M`2o~Opp%CAZ6)h!cGDVu)z#nD_e zC&r%|nb*GyPczD?Y_7rnVxTkjKV+IOkXm7QajfFXhRD>LHmmP*sInjAZt`lgzDccM zG@&5IN23+~B3>aJvK-Z^Q7v 9NZ%_Fd!5UDyeCWq&(H+RnRA^q||;vWMF3|3*UaZR;^Z>0-8Gt|O~R;OXiS)={(3 zY!B2<;*?N`7-dafsC@`?sbK}V@Mcs*H+Yt{U-NZ?5ST=B&vOBjt&@FZHNY{zJ;1LB z*u|r+lCgiyqN*bF!NejdjlO^(2{PeY%UwkY;~utoML7xQtT{bcD0n_>Pq@ZP(c=M^ zX3*8y1CO`{q+m~hM`>$7Vqs#OHRODoqM_s#5YiSkMG0`^l-%~+^EaFB@|Yg^ZY#wU z2#XCEBc3i+LOSrc j7ZKDReqD QS}yk , +) + +@Serializable +class RankingTitleId( + val id: Int, +) + +@Serializable +class TitleListResponse( + @SerialName("title_list") val titleList: List , +) + +@Serializable +class TitleDetail( + @SerialName("title_id") val titleId: Int, + @SerialName("title_name") val titleName: String, + @SerialName("thumbnail_image_url") val thumbnailImageUrl: String? = null, + @SerialName("banner_image_url") val bannerImageUrl: String? = null, +) + +@Serializable +class BirthdayCookie(val value: String, val expires: Long) + +@Serializable +class EpisodeListResponse( + @SerialName("episode_list") val episodeList: List , +) + +@Serializable +class Episode( + @SerialName("episode_id") val episodeId: Int, + @SerialName("episode_name") val episodeName: String, + @SerialName("start_time") val startTime: String, + val point: Int, + @SerialName("title_id") val titleId: Int, + val badge: Int, + @SerialName("rental_finish_time") val rentalFinishTime: String? = null, +) + +@Serializable +class ViewerApiResponse( + @SerialName("page_list") val pageList: List , + @SerialName("scramble_seed") val scrambleSeed: Long, +) + +@Serializable +class SearchApiResponse( + @SerialName("title_list") val titleList: List , +) + +@Serializable +class SearchTitleDetail( + @SerialName("title_id") val titleId: Int, + @SerialName("title_name") val titleName: String, + @SerialName("thumbnail_image_url") val thumbnailImageUrl: String? = null, +) diff --git a/src/en/kmanga/src/eu/kanade/tachiyomi/extension/en/kmanga/ImageInterceptor.kt b/src/en/kmanga/src/eu/kanade/tachiyomi/extension/en/kmanga/ImageInterceptor.kt new file mode 100644 index 000000000..ab4257950 --- /dev/null +++ b/src/en/kmanga/src/eu/kanade/tachiyomi/extension/en/kmanga/ImageInterceptor.kt @@ -0,0 +1,95 @@ +package eu.kanade.tachiyomi.extension.en.kmanga + +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.graphics.Canvas +import android.graphics.Rect +import okhttp3.Interceptor +import okhttp3.MediaType.Companion.toMediaType +import okhttp3.Response +import okhttp3.ResponseBody +import okhttp3.ResponseBody.Companion.toResponseBody +import java.io.ByteArrayOutputStream + +// https://greasyfork.org/en/scripts/467901-k-manga-ripper +class ImageInterceptor : Interceptor { + override fun intercept(chain: Interceptor.Chain): Response { + val request = chain.request() + val fragment = request.url.fragment + if (fragment.isNullOrEmpty() || !fragment.startsWith("scramble_seed=")) { + return chain.proceed(request) + } + + val seed = fragment.substringAfter("scramble_seed=").toLong() + val response = chain.proceed(request) + val descrambledBody = descrambleImage(response.body, seed) + + return response.newBuilder().body(descrambledBody).build() + } + + private class Coord(val x: Int, val y: Int) + private class CoordPair(val source: Coord, val dest: Coord) + + private fun xorshift32(seed: UInt): UInt { + var n = seed + n = n xor (n shl 13) + n = n xor (n shr 17) + n = n xor (n shl 5) + return n + } + + private fun getUnscrambledCoords(seed: Long): List { + var seed32 = seed.toUInt() + val pairs = mutableListOf >() + + for (i in 0 until 16) { + seed32 = xorshift32(seed32) + pairs.add(seed32 to i) + } + + pairs.sortBy { it.first } + val sortedVal = pairs.map { it.second } + + return sortedVal.mapIndexed { i, e -> + CoordPair( + source = Coord(x = e % 4, y = e / 4), + dest = Coord(x = i % 4, y = i / 4), + ) + } + } + + private fun descrambleImage(responseBody: ResponseBody, seed: Long): ResponseBody { + val unscrambledCoords = getUnscrambledCoords(seed) + val originalBitmap = BitmapFactory.decodeStream(responseBody.byteStream()) + ?: throw Exception("Failed to decode image stream") + + val originalWidth = originalBitmap.width + val originalHeight = originalBitmap.height + + val descrambledBitmap = Bitmap.createBitmap(originalWidth, originalHeight, originalBitmap.config) + val canvas = Canvas(descrambledBitmap) + + val getTileDimension = { size: Int -> (size / 8 * 8) / 4 } + val tileWidth = getTileDimension(originalWidth) + val tileHeight = getTileDimension(originalHeight) + + unscrambledCoords.forEach { coord -> + val sx = coord.source.x * tileWidth + val sy = coord.source.y * tileHeight + val dx = coord.dest.x * tileWidth + val dy = coord.dest.y * tileHeight + + val srcRect = Rect(sx, sy, sx + tileWidth, sy + tileHeight) + val destRect = Rect(dx, dy, dx + tileWidth, dy + tileHeight) + + canvas.drawBitmap(originalBitmap, srcRect, destRect, null) + } + originalBitmap.recycle() + + val outputStream = ByteArrayOutputStream() + descrambledBitmap.compress(Bitmap.CompressFormat.JPEG, 90, outputStream) + descrambledBitmap.recycle() + + return outputStream.toByteArray().toResponseBody("image/jpeg".toMediaType()) + } +} diff --git a/src/en/kmanga/src/eu/kanade/tachiyomi/extension/en/kmanga/KManga.kt b/src/en/kmanga/src/eu/kanade/tachiyomi/extension/en/kmanga/KManga.kt new file mode 100644 index 000000000..5446fb077 --- /dev/null +++ b/src/en/kmanga/src/eu/kanade/tachiyomi/extension/en/kmanga/KManga.kt @@ -0,0 +1,394 @@ +package eu.kanade.tachiyomi.extension.en.kmanga + +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.network.POST +import eu.kanade.tachiyomi.network.asObservable +import eu.kanade.tachiyomi.network.interceptor.rateLimitHost +import eu.kanade.tachiyomi.source.model.Filter +import eu.kanade.tachiyomi.source.model.FilterList +import eu.kanade.tachiyomi.source.model.MangasPage +import eu.kanade.tachiyomi.source.model.Page +import eu.kanade.tachiyomi.source.model.SChapter +import eu.kanade.tachiyomi.source.model.SManga +import eu.kanade.tachiyomi.source.online.HttpSource +import eu.kanade.tachiyomi.util.asJsoup +import keiyoushi.utils.firstInstance +import keiyoushi.utils.parseAs +import keiyoushi.utils.tryParse +import kotlinx.serialization.json.JsonArray +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.jsonArray +import kotlinx.serialization.json.jsonObject +import kotlinx.serialization.json.jsonPrimitive +import okhttp3.FormBody +import okhttp3.HttpUrl +import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.Response +import okio.ByteString.Companion.encodeUtf8 +import rx.Observable +import java.net.URLDecoder +import java.text.SimpleDateFormat +import java.util.Locale + +class KManga : HttpSource() { + + override val name = "K Manga" + override val baseUrl = "https://kmanga.kodansha.com" + override val lang = "en" + override val supportsLatest = false + + private val apiUrl = "https://api.kmanga.kodansha.com" + private val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US) + private val pageLimit = 25 + private val searchLimit = 25 + + override fun headersBuilder() = super.headersBuilder() + .add("Referer", "$baseUrl/") + .add("X-Requested-With", "XMLHttpRequest") + .add("x-kmanga-platform", "3") + + override val client: OkHttpClient = network.cloudflareClient.newBuilder() + .addInterceptor(ImageInterceptor()) + .rateLimitHost(apiUrl.toHttpUrl(), 1) + .build() + + // Popular + override fun popularMangaRequest(page: Int): Request { + val offset = (page - 1) * pageLimit + val url = apiUrl.toHttpUrl().newBuilder() + .addPathSegments("ranking/all") + .addQueryParameter("ranking_id", "12") + .addQueryParameter("offset", offset.toString()) + .addQueryParameter("limit", (pageLimit + 1).toString()) + .build() + + return hashedGet(url) + } + + override fun popularMangaParse(response: Response): MangasPage { + val rankingResult = response.parseAs () + val titleIds = rankingResult.rankingTitleList.map { it.id.toString() } + + if (titleIds.isEmpty()) { + return MangasPage(emptyList(), false) + } + + val hasNextPage = titleIds.size > pageLimit + val mangaIdsToFetch = if (hasNextPage) titleIds.dropLast(1) else titleIds + + val detailsUrl = apiUrl.toHttpUrl().newBuilder() + .addPathSegments("title/list") + .addQueryParameter("title_id_list", mangaIdsToFetch.joinToString(",")) + .build() + + val detailsRequest = hashedGet(detailsUrl) + val detailsResponse = client.newCall(detailsRequest).execute() + + if (!detailsResponse.isSuccessful) { + throw Exception("Failed to fetch title details: ${detailsResponse.code} - ${detailsResponse.body.string()}") + } + + val detailsResult = detailsResponse.parseAs () + val mangas = detailsResult.titleList.map { manga -> + SManga.create().apply { + url = "/title/${manga.titleId}" + title = manga.titleName + thumbnail_url = manga.thumbnailImageUrl ?: manga.bannerImageUrl + } + } + return MangasPage(mangas, hasNextPage) + } + + // Search + override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable { + return if (query.startsWith(PREFIX_SEARCH)) { + val titleId = query.removePrefix(PREFIX_SEARCH) + fetchMangaDetails( + SManga.create().apply { url = "/title/$titleId" }, + ).map { manga -> + MangasPage(listOf(manga.apply { url = "/title/$titleId" }), false) + } + } else { + super.fetchSearchManga(page, query, filters) + } + } + + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { + val genreFilter = filters.firstInstance () + + if (query.isNotBlank()) { + val offset = (page - 1) * searchLimit + val url = apiUrl.toHttpUrl().newBuilder() + .addPathSegments("search/title") + .addQueryParameter("keyword", query) + .addQueryParameter("offset", offset.toString()) + .addQueryParameter("limit", searchLimit.toString()) + .build() + return hashedGet(url) + } + + if (genreFilter.state != 0) { + val url = baseUrl.toHttpUrl().newBuilder() + .addPathSegments(genreFilter.toUriPart().removePrefix("/")) + .build() + return GET(url, headers) + } + return popularMangaRequest(page) + } + + override fun searchMangaParse(response: Response): MangasPage { + if (response.request.url.host.contains("api.")) { + val result = response.parseAs () + val mangas = result.titleList.map { manga -> + SManga.create().apply { + url = "/title/${manga.titleId}" + title = manga.titleName + thumbnail_url = manga.thumbnailImageUrl + } + } + return MangasPage(mangas, mangas.size >= searchLimit) + } + + if (response.request.url.toString().contains("/search/genre/")) { + val document = response.asJsoup() + val nuxtData = document.selectFirst("script#__NUXT_DATA__")?.data() + ?: return MangasPage(emptyList(), false) + + val rootArray = nuxtData.parseAs () + fun resolve(ref: JsonElement): JsonElement = rootArray[ref.jsonPrimitive.content.toInt()] + + val genreResultObject = rootArray.firstOrNull { it is JsonObject && "title_list" in it.jsonObject } + ?: return MangasPage(emptyList(), false) + + val mangaRefs = resolve(genreResultObject.jsonObject["title_list"]!!).jsonArray + + val mangas = mangaRefs.map { ref -> + val mangaObject = resolve(ref).jsonObject + SManga.create().apply { + url = "/title/${resolve(mangaObject["title_id"]!!).jsonPrimitive.content}" + title = resolve(mangaObject["title_name"]!!).jsonPrimitive.content + thumbnail_url = mangaObject["thumbnail_image_url"]?.let { resolve(it).jsonPrimitive.content } + } + } + return MangasPage(mangas, false) + } + return popularMangaParse(response) + } + + // Details + override fun mangaDetailsParse(response: Response): SManga { + val document = response.asJsoup() + val nuxtData = document.selectFirst("script#__NUXT_DATA__")?.data() + ?: throw Exception("Could not find Nuxt data") + + val rootArray = nuxtData.parseAs () + fun resolve(ref: JsonElement): JsonElement = rootArray[ref.jsonPrimitive.content.toInt()] + + val titleDetailsObject = rootArray.first { it is JsonObject && it.jsonObject.containsKey("title_in_japanese") }.jsonObject + + val genreMap = buildMap { + rootArray.forEach { element -> + if (element is JsonObject && element.jsonObject.containsKey("genre_id")) { + val genreObject = element.jsonObject + val id = resolve(genreObject["genre_id"]!!).jsonPrimitive.content.toInt() + val name = resolve(genreObject["genre_name"]!!).jsonPrimitive.content + put(id, name) + } + } + } + + return SManga.create().apply { + title = resolve(titleDetailsObject["title_name"]!!).jsonPrimitive.content + author = resolve(titleDetailsObject["author_text"]!!).jsonPrimitive.content + description = resolve(titleDetailsObject["introduction_text"]!!).jsonPrimitive.content + thumbnail_url = titleDetailsObject["thumbnail_image_url"]?.let { resolve(it).jsonPrimitive.content } + val genreIdRefs = resolve(titleDetailsObject["genre_id_list"]!!).jsonArray + val genreIds = genreIdRefs.map { resolve(it).jsonPrimitive.content.toInt() } + genre = genreIds.mapNotNull { genreMap[it] }.joinToString() + } + } + + // Chapters + override fun chapterListRequest(manga: SManga): Request { + return GET(baseUrl + manga.url, headers) + } + + override fun chapterListParse(response: Response): List { + val document = response.asJsoup() + val nuxtData = document.selectFirst("script#__NUXT_DATA__")?.data() + ?: throw Exception("Could not find Nuxt data") + + val rootArray = nuxtData.parseAs () + fun resolve(ref: JsonElement): JsonElement = rootArray[ref.jsonPrimitive.content.toInt()] + + val titleDetailsObject = rootArray.first { it is JsonObject && it.jsonObject.containsKey("episode_id_list") }.jsonObject + val episodeIdRefs = resolve(titleDetailsObject["episode_id_list"]!!).jsonArray + val episodeIds = episodeIdRefs.map { resolve(it).jsonPrimitive.content } + + val (birthday, expires) = getBirthdayCookie(response.request.url) + + val formBody = FormBody.Builder() + .add("episode_id_list", episodeIds.joinToString(",")) + .build() + + val params = (0 until formBody.size).associate { formBody.name(it) to formBody.value(it) } + val hash = generateHash(params, birthday, expires) + + val postHeaders = headersBuilder() + .add("Accept", "application/json") + .add("Content-Type", "application/x-www-form-urlencoded") + .add("Origin", baseUrl) + .add("x-kmanga-is-crawler", "false") + .add("x-kmanga-hash", hash) + .build() + + val apiRequest = POST("$apiUrl/episode/list", postHeaders, formBody) + val apiResponse = client.newCall(apiRequest).execute() + + if (!apiResponse.isSuccessful) { + throw Exception("API request failed with code ${apiResponse.code}: ${apiResponse.body.string()}") + } + + val result = apiResponse.parseAs () + + return result.episodeList.map { chapter -> + SChapter.create().apply { + url = "/title/${chapter.titleId}/episode/${chapter.episodeId}" + name = if (chapter.point > 0 && chapter.badge != 3 && chapter.rentalFinishTime == null) { + "🔒 ${chapter.episodeName}" + } else { + chapter.episodeName + } + date_upload = dateFormat.tryParse(chapter.startTime) + } + }.reversed() + } + + private fun getBirthdayCookie(url: HttpUrl): Pair { + val cookies = client.cookieJar.loadForRequest(url) + val birthdayCookie = cookies.firstOrNull { it.name == "birthday" }?.value + + return if (birthdayCookie != null) { + try { + val decoded = URLDecoder.decode(birthdayCookie, "UTF-8") + val cookieData = decoded.parseAs