From 817b89767aa31b3b2847203b27151044f5d323f6 Mon Sep 17 00:00:00 2001 From: Mary-nyan Date: Thu, 1 Dec 2022 14:08:43 +0100 Subject: [PATCH] infra: Add distribution files for macOS (#3934) This upstream macOS packing and distribution files --- distribution/macos/Info.plist | 46 ++ distribution/macos/Ryujinx.icns | Bin 0 -> 108982 bytes distribution/macos/bundle_fix_up.py | 609 ++++++++++++++++++ .../macos/construct_universal_dylib.py | 95 +++ distribution/macos/create_app_bundle.sh | 51 ++ distribution/macos/create_macos_release.sh | 105 +++ distribution/macos/entitlements.xml | 23 + distribution/misc/add_tar_exec.py | 24 + 8 files changed, 953 insertions(+) create mode 100644 distribution/macos/Info.plist create mode 100644 distribution/macos/Ryujinx.icns create mode 100644 distribution/macos/bundle_fix_up.py create mode 100644 distribution/macos/construct_universal_dylib.py create mode 100755 distribution/macos/create_app_bundle.sh create mode 100755 distribution/macos/create_macos_release.sh create mode 100644 distribution/macos/entitlements.xml create mode 100644 distribution/misc/add_tar_exec.py diff --git a/distribution/macos/Info.plist b/distribution/macos/Info.plist new file mode 100644 index 000000000..c3e1e08e4 --- /dev/null +++ b/distribution/macos/Info.plist @@ -0,0 +1,46 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + Ryujinx + CFBundleGetInfoString + Ryujinx + CFBundleIconFile + Ryujinx.icns + CFBundleTypeExtensions + + nca + nro + nso + nsp + xci + + CFBundleIdentifier + org.ryujinx.Ryujinx + CFBundleInfoDictionaryVersion + 6.0 + CFBundleLongVersionString + %%RYUJINX_BUILD_VERSION%%-%%RYUJINX_BUILD_GIT_HASH%%" + CFBundleName + Ryujinx + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.1 + CFBundleSignature + ???? + CFBundleVersion + 1.1.0 + NSHighResolutionCapable + + CSResourcesFileMapped + + NSHumanReadableCopyright + Copyright © 2018 - 2022 Ryujinx Team and Contributors. + LSMinimumSystemVersion + 11.0 + + diff --git a/distribution/macos/Ryujinx.icns b/distribution/macos/Ryujinx.icns new file mode 100644 index 0000000000000000000000000000000000000000..f54a9aeb7e4780b09c1bddb1ad8b7fcf4f9a0d3c GIT binary patch literal 108982 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%0As-kfW@cu}8;_m{6n6W3pfWl(r_KlL?tfA!qs z2RRf1W->53q<%?>32)Tsy2Io!PnzLnievWb`oHH*yRPy!?760Yp;*m~;n%tHW3O(l zyTaYL3pCTR)^(--Q>L&tLhD&e?p!Wje%rg6vFE>jyxkI8!GDkc=iQvIk;J^<)OtIv z28S|6riM3RGMxt+S@UjLoxX9Uo>?Jz`-wN_{<5E2@IX23$mcJyM}$Fh7YvWh7`&9s z7_@d&ddM}}uyQbbx}u`XvgqE)u7WAy{0`COceg%^``NC&D&GG7vh-~J13vp17Tka1 z?^%7lIfj8l!N7uvg`xR!*pw0mmPaZVKmN~jjx!HfJN?^&*K7`}ZNhl9cpm3;+L|*+ z-9PlB?B^x>eIXGmJtP?6b5yf+5_)ZKum1P<v_ZWGhX=gbk`?&X@<(J^6bh6 z2bfqGnD11G*cvo`&0|*BeB<@sdU1v&-+mq`TP<&&zm+faV({K7{VhK(T^4P~Im6`8 z7~3bp-pI@;z#!w%abI~tXu|P1k5x^NzwWDuJTLGz@4907=D2CPE9#gRls)IEo&Mio zJ%fYy`uWdOqGt0pY}yW<(q~|F5IF0g_)0z<5Amt5_;;xPv)^kx?se!=;k#eG(yZ3@22l z@7cpR;rNHv?B0I`?jpyy+8)NqPT^@(VC7(F$`Ru-Enq6QtDa)#y8mJ}Q$jtn!teK= zt_b^w#of>N!uv~QeK*5S)xSn{cP{VWYQ6Py18;-Tq(TWMMNR>RmMcz)c; zsSA1fUE=&N{Q5TMom7Rp1YaKWf`2{#-W+`?e_#Ip`xN_5WkCl85k{tlfT*S-o(&Cp z|C39Y70R8@|0?u9f7ef6bhFuOO;_3UyKD~Eav7dj{(JAnts~7KdR1>px5Gsy76w=D z$#s8O_x*p7vv9Gn+otA*-dmc%tkM|i)oQSoWc<%Ym-^*t{`)Qti&t@ra z!w;BnYr_fS2eNM=-fv|Nk8{)xR+?)m0`w1XeIGIxOII z-pJ;1K+S*owLR5;p3N^5N-z?8dHHj2(LvUm`MX>TRlXe;I(^>jk)gQardwA9_iBX4 z@AX}`bJ>bX6ZM!lM06Y&0t^gJUXMw5+4MH*ur%A&Y0plX&ffCoUH|{|ui<~sKjdK% z;Bag3Y&dAb$_ia$1{;EdtQ$*U;b3UW>!5#1IuL*5f+Hr zPoOnso7KR(t{7RG1Qi%KmT78$xAibEJ>?W&n3C@6#{pjF#-i-NAm9@mAOKm9c1l=* zLF4?aIRfCNf*fuQ3<@)rEl_}{oucl*5HQcw60-VH0JN6Ez}L|cqE^GdfnkA7b`fM> zjDiFsQ$xb6rY5i@3IX#N7#%9FJppg}X>b4yD%0Pi6Z{z_um#IQLy&<gx?h7~(ZPs%}JsfTGN z3q#T(*YZ+Op41#c^8aCpJAlZ~OXGjFQj_sq`67he1Rgs9Hg0@{%@ z$ND{I^4q&-m@~eZx;_Q3F=lg6xX<9AowGLPc#{M3g-;@apnzF$k!dG8!^$uJJgpYr zR!Ndra4}qLDtMP9N0h>M28VOj-vX1Ls%3t(O1Plw{}614lCW~UWP<7E7+#32@DQm=kG7O%(#APm~ zk1R>)>WO<9nQ}_IzzOz3MdKkR2Fvd+ewSt*$oH&X@$m!$B*6&GawueMkeDs?nbmVw z@*94EsLjEUb@$7-MI;zB-aguy?Yq9*b}xhP{xd7UyO|v_n2xeB98)>wGXHX1L(5f0 z=j^IeU?*QlX-r~fnEJN&?B07SFYQlWD40BVr8OiKIdY1~F`QssHofD|#FClZ0wJe^ z!Ar3l-MB^M87%HCEzTF2{im7dqnpFx^=4Y&ZNmaR4wj4!7tNX0{AGU2S`n9I#>utp zD|B~XGV3W$2ATJpet!2@|JyZlvsFU!dNVD^-n$-$O2&pT%iQw+A#TUzQac|c+?o%` z$R;5Rt}r-EoAY7I#4nq7`8O2JZkrUG3kkqg;u_`*7k;VDp11#2S@nO7!+XRcYCi`v zfn*z(aZQn9IMEesb?wOhrMFr0lbsTR^{rol_Y($eb@<8Hu=?Ys#q~O#zuJYiuUcpS z6S7U?f=6Q(6T?!!#b3CW-TtXADVM2YGZnmFz+r(`<0&SF$XyBLLNouba%S|uEFADu z)d7-Id_`91F%;bLes*vDZ9s8BV?4u=MZx2#Rv47$r3ni&{) z?LlV&q+Hw+yY2GN)R%Aeb-OlY)&C821iQP7meQnnV&z4-}_I#cf8c^0AJ?DbceO;owUG<;sbU$>|ktIY*GG?SMBBBNfEod zuQPk^H#-3?8WtR5N@Qo?nm=dv1Lrqe=g76 z|3iJI2(U~kFPs3bm_R#>l^6>Cyxhb(e|p?=^ODC3u^Juo8h*u1y21m_>pYE(oD4E< zRqy`TPd54ar9!|Uz*T>-?n&szi!)5j<>J2QXMSIP>2+OHJ9E_+mXy47KJXqLhXnn` zMs9|fmzPsN#|nIy@0!!To%vgq|2|0dsWAC~0~^D-G%J}8u9v;*JTH|@-*RiF)@er$ zNY*vbGuXh`pj}sV@6YN>Dt{N;DCy?my}S%6J0UP(0dqs0?D^`qrJ0w0R=)fym+-!! zXYIK}u!3H3i9ZYnOdoDa&hyXx{@iDKb+J8%!LpCM+@J*309qM)MVX<%G-lU>iAT!n z|7?@EoLKZAM2{149zjNv z!(Jwa>q+7>;&$cSdq3On7h|pSgTiO5CJ&94^?{wSkLfEnL!6k$x%uC}uk6(`n!EA+ z*Jg$`mYVgvec%#v#YTr*=7zf2y0)Jtrnr5Hd)ZSPwx;LPs)XM4DXYNSD;}r@yk|HN zIH&H*xssQA{?GLKMOUYt+i!Pw=WqKHH{P9p zndX#y>6$q!v}(B!(D;mnVO^uX)x(!1H?!+aeJ7XCQrWrJ-drIq`(az$Z}3Loj4ckE znHoBm**)L-f6kXaz_;zW(3&eALO63xxZ?Hz@*C%F+|N|l5bXBl>cJgbpHKWF3rfEXjae)? zo2>hLcCOw#>rt1er~i}RzgEA$y>+&@+ue8i9<%nu$IIIIF*6B%zpYZa)%d3*$T)(do_fP+S{_jwFJfr!Rq+=W|-%B5Y%uI-9yw05awma!;@$Y|G zTK9z+d|Y#H?m2EMQ4!<+cUR5^j$TzrIS_ zd#;n0$eE+Jl8xiip`YpozyB<~YBlxu5Frixi5uNO5{z6nYX910I~1-@3#-s@kzRhWYz z&bM_wq&+1t%VCu_!}lli_H?;2Oqj0n5|aM|rZ_CQ`~RKjm$K)Ldp0RBpzPGy%b4&) z(DK_E-^x@+WIZz)W;1A*&kkK(vT%rNYqeUa3v{+KPetP*u z4u(%+sH6X3NY+xYLSNok3dsH z^uO?R2J`=)ix6C@$e`gg)dXA?2v|rkZvR$4Dd(DfeZ9d+pE5nh11VO2_&`z5(5S_7 z=S{tGykY(4-sN4V<{|A2OVDDI=hR;!qSZ`K4zN)$Mu@qnp8<)>_M=+DjnzUwO5GW_oP06l5Im1D~< z?td}bm-&9j2%h}C%#-1P_OXu;@BZN0arIx-$tp=d`&7OCdtO&p2_6?_5LxIo3nK5s z;d1Nk{AJchPU+7+z5nBtBmJxYc`*c(a!-IXo@-g}%-BD}<^K2oQ+5`ey){A0i$NeL zJ3tKNti~$NFVlZ*txs9^EZ{2r1kOY&mLK@RSKt34%U<4W_4-<)CtgC}!DU`hc1S2-xpVgZE#F_y4?jD# zem(!*2P_N=w+lQ4x12cQ6aGB=-?L<|_nE2pcj*3M>K0(&ancTS1AC=_^;rDZ>M-_y z_DjtFOCQpGaZfc_=)pa?8c5s0p^UNov-s=0fAZJL|E@n@m-N;B5_3SUbO5Ba?I_am z_1XL-{+{RKlJ*%znAacoWiaRvmrVsXomYe%*!ybw5`T}RddK{K+e4yftbBS-0KD@9 zZ!Q?B`GDy?iNbm&K-l2Oktd7`LyEuW#M|c3w3p4#K|9v;2{?Wac z|K*=`nTGrSS;ZOuk}<z`$v||mZ3Uc2ok{o z>5SL!zPHOPTKw;Sdg+?~zyH3M{!!1uz?)OD3e<-&@k?0$uI{(Gk;%4OF?Dy>9kYBB zn8?&%Yw!co-Z{%sSM=E5{PpVE_$zt;PoI6YK6hoL?(z^>rUuQ8g+idx!QleaW8>d< zS@!qdy8B->_QG@TXRq>Y&Y-WoP3Z1PH6!X4jkdCGrMZ|?@Qg>r{7aE)$6nW zy^@yZK2^A2vMPhbi)V{Ja40l59A^DxUK_hB{P(x7p7quBMe|L=J731-hyDLs%zJ(2 zmS2c+r*Q$x9p3s!{Sn`ntT(T%?$^8b|NA7j`=;S_Ul%SoC0&rn!N8O!(*Wh{LTx0mVGbDdyRIRsO9KEZ=(mjjUM#&uzK{M zH}Ht>g3*KCMh|*pWYHQu=uKhtptsS3-V{a;dK*3HZPDmKZ=(mjjUM#2a`d3L(SzPb z4|-z&jS`L?^tNF1ptpe0gWg6DdTSg#=xy|%x6y;%Mh|)$J?L%pptsS3-bN348$IZ4 z^q@D+(SzPb4|*Ft=xy|%x6y;%Mh|)$J?L%pptsS3-bN348$IZ4^q{xVgWmK<4|*Ft z=xy|%x6y;%Mh|)$J?L%pptpIW2fd9R^fr3X+vq`WqX)f>9`rVP&>KVJ=s|CwUEq|Q zXg7M$Tf^u;G&gWR1O8JI*9pEmqwaG%P+81kU$KZE-e z2FB3e&HowPCo?dLJ!=6o7-CxgGq|@iunOAQ+Sw>IGcZ&@1QFac}<5 zcu%VVNucFF%W)1Qe(Qf`PgHIxl-tO_#>C9P%*5c{`k%Q1AvjTsX^tFfzIq&mNw#b3ll?GYE^U4i?$zBFc=U)}4WY zWv8&fJ}w4ia~&M^A9=s`TiE)B>gu)8xb zPE%x?Cx#-*2C|KTk);u?v>Y0$Q1AO7xGWV644kJxvC$4z%}~+6z-aIs6i+aLMg}JR z1AiL-Gk8EnDw`M>nGG9k8SMn2BJqt{|sXjAdhCU}9!uIu7MB zurx3*F)}|x;xdACGr|pIWnf`sW@v9jSi#D|z`(>b7fFO2B*M(_|9=C_87v?Xh6aQH z14|=!vt(j~+dFYzhO@u~R1-k20P-HkpCx=+To+5GLc% zv&|q@I|FM}d_sIuQZobNv2#!%#+amr1g6Bqs740HBXGgk#D;k0gye+822|0ec(&yD z1O^6VmCf<2NpToL$p}@A4DA0`F@!cS#3nY!v!Y5IJ;V?nhau6xz;WWpRsXm&R2klr z#~5~=KXR-*K0YlW9@#M7lSdhjo;rH8G$uYVJ^@wc7{kdkM~|k($0dfM={d&0u>a_F zhj?EGbbAgTKYi@vgsAwWcm^~*M^2nNdb~3(J|PBG568)43=GGPFvQ0-FfgKc6eRQi z=t=GPSacb7ke>Yv3=WM*T8<(`0yMaeorXmg$Y3^52p&6n1{}NX5S@(42@MR)M^BtR z)&PlVuoD=Q6B-%V51%}7;`s5?$KXoin;6(uoj!Tw$kD@avAD?~gPP)*65|uV%*J@8 zgv9tMAkl_+rliF9sSIqfiShA?2}z*j*YKSA#nV^MrZTWUfBF9T)2FYVO<`bq^7>Wd zQ^r@%CNnU-c=DQF1;tLCJ4rN7^c>0_nG?W2dqJe?q?UVm* zp=qcxysw@yym67B1(D1MjG#Q2`pPoER3k?emMc2c? z@c-$5gHT@vbbB5@fAjR!EdS8(5C$|oPhY-y_Pj4BG#t&<9Iu`+Fg$(25Ej zkj(#QFSNq~(Ph{{dj3NaABq;nSI^p!OnLGK8hv1c*}x(AFpS*nc ztN{|yV3mxKp^XeoPhP!jc)|DKbau-l`=c;wU($Fd^t-Mo)*-R4tKIjowQy{Y>VXFNuHtRPlZF-H)ns*T>sg-|c^= z!TD>Rr(o|zm9h<=J?EKrbP0DED_vpe5^lOu@IG>H-ovBSdZu?C&ME!&!TNsq&h+`4 zzeoN5|Gxgy$)EH0UhmUf`oM8PF4Mx0fh@8 zjh)Nb^zxW4Gkx6uw&Kl^Pal^23$d`RUs+$2mu{=s_Th9zkmO_{gZa$FVBCo z|G%luy#IgZz2slE{9rSnhHZ&=U|#UoG|#uK@bg~xmJQ!uhKim$b^p=%)A3K^BYa!j zzyG#8I={aEd+9#Yr_(qf_Jj30i(O!lFk11jOKOC>yz5Z->;J#H{KTp?JWeD132XEW=MMwp%+F`+l z1~!Mpg3C|me=&b=qZxnv|EwGSuRododEPd~{}a1&12p)+I+R5(Fi2>wKbX!^_iyR{ zOA?ok{*3zf()Ewir?vHM$K#R1s3*pOIpN8XyK@!l-b8Cxxv#GYwLg0L^Ynl5XXAgV z%kBFeS00(-hh%!=Mve@Ip4Pp0Cm%aoK0os4ztpRNtM5%(|J42*|KGRkw!h&JfO|5d zt%1$K_*KYnjmN#;*Q^WkkDGS=&42k-67z$8H?Vpjo7%^j!BDd1!C^o1ynWfZ`(p3k zUUm3z{oRXVmhMOP9ZQ??0!9OwGHR~=YPumGjD4BNBLxvWoqE?f@*1O<;Y;raaq0i z_qm#n>x1^Fwf=b0XXG&(+45eo3k(@0Ki#Bj_B@@Iy4O~Hk7E7D|9k%&{olEG?%5Ym zlORdSz>AT$A@G%1$(xGXxu;jl%l~~H8u59zX3J!$S#29Q5#glZkT9)*?SM|tI{UNB ze;u#s{eLvn>R0_)vE^lVo&O=5nK8M6&0+Scoh>obYO{`eS849i*8fm`O{lF<17auC zKUYE=m=`Q-)f5f8Q}cahs89IwpVy|&x@?1NmO?&z!>h0T`yTq{?{Arh6TkKZg`k*dki*E^kl1~# zVo&Pq?fX}BW#5x^vf6wsk*gOu3|ZV17BCtxUD?GexjoN%qt{1%r=ICobHn+$SMGg! zwO6w>oxi?B87aFtG&o#lF=3b$w$b^>?)-`S?`GF6=eoMzUs1>Y$h&)}$)JxTgJH{x z3x%J*ZC;&qwz|kGeT#|8_5X6+7Y^3K@O+}+(BQC~>OcO!s>qkEft;%aGB!4_ zIUMe?-kAFQ^{NT~-v4>}|3+J@UJQyNiNy_U2P8g<8-3l^`mb#He`Wd2iHgT>P6xXi z?p+`53X z=7=dj;AuFzLexSH6rE44|K*lFEzqD229w!iEY)-iD*Md*7Vc@pPX1@u$K6 zP5nQdR-S$207_cBpvlPFFl(xH-px9{sgK$}<^NN^qGR$9?rspnp_z3JW5D7^PhbDl zU+MHmJ^o*Ih{!^a49t%URGFk0Of^#W9kBnl?M+2`*F$E@Ia5tJJULJ;Gh&?1(6e*F z$79uD$FByzKfV9g{vFR*1Z>qKy4A0UvL$eps%P723ddK%#4J&g*aS>zw}oKOOx0lRaB~ zFmifc(9I;p(7M`gW94({-z$Gb%=>oYQ1I+N6!%!^EMN@S@MEv8_5FECyX;!Fta-3y znG$l|VPtY-UT~m2;h9=8!T&t_e|;*;Vd+UIzT?n|IM7tWt!Dj$@#D0) ztCRNs>d^nsUj9iB#lk6|x^PkXo{#gc7MB0mU32TdcbmnUg|MK8#PWejtT9X*QVzAV zbFK`gf2;4g2Yq^4&oQSBIU6%H3UcgV zN$6Yw3aq0cxjmY(8;&fLL9RdF1RlcToe_A(zF3;aOnUj-Ttc zhQ9gleDCXv$~Sr9s7WZmnvu7`d5TKjzV97c-~QRXFT12z_SFm}It5m&ZD2d#=p~%b zu{YxFztwwA8}n_Ny&cWAx2$Se@((4L*Iav75$^HvX)@2jEASuzGZHkI&M{apJU;Sl z?s{IsKi$lG-ln3u=7I(zZ^Pdy+V}Rn&D31-`g3%leSUQ(Y8;vPIWQ;qoqV*+x_{-e zD*03Kf5XpLo>oJ3amJbkwgYP}J)QsibJPF5|Am~Y4y~DPix!Bz;u|;~tPu)}F8kwX z_xk>#`prHzC`ua3I4qbSJTIMdW6#?|f4;Z+MGF(T zM!)%t0_N{)>hzwLFdt|}4UR2Q3pVpM?09)DdDV=2(^uclE^SqHM|Hv`hjM9#t4FGf z|Ed2LK`w|J7#j6BN;WYXY)pNgTKRE*{A;Jir^ro2MkbbJtX9_GKb+N$tJ6Aj- zm01Cl)uuGH{=fC_sM-_x=rDON8>jWhM@PcmV1Ak z5ZUX55@jqLTNW{H$obqi{n&)KC3C=~0Ybrnf2>g#8D^|{^ncI)FMGTY65y2X!)>yU zQNTRk-g;R{V-aed#mK}imQly75cdDuftQu-VD*RuK0_>{gjpf)*1s<`Z~wP?d7>FC z6SLqVZ^Npt|7y$ApZ;&Yeq%SPyB6$BXE>r;ZmW3xeUd3sw-9U$pYDYl#tf?h{=S|Q z_x^(BEwo%IkdeZyaIfU}Ma84$TBwmMpkQF%=w`;SN>qE@necz>=f!t@Lu((fNL^<* zq5bb^>;KF3%S%vQ)o|bstJHOd89%P{#h-g=oB2=k)?_q)XT&fkXf3@TV1^q14he}& zy;%%L&X%w3U0L$vA%*JCT z42Q1CK7S(3FlSK?x|A zMt9K$lg21>P*|Kl@pA5tEh4hM;1q+%OjmX>23Y>Rua4IE;Bj4WjkjUOqRao=gVLH2 z$r#jAxz3t(o1tU&I~%p5ANH&h_dw~ODmWxaFm26c__C_=zlReQ+l+U|$^1kqEEyUTIj?MD zG%(!y`^}t${0L7evk`L!HO-vb_eMPzq^Aro$pDQkfN67cd%}(UR^)iF7Od zfZc2kdlbJUb)0`&lgfbRC`GO->lg*D-K(wQc)3FHSq5q+OyNRh<1JH$s)hfjoC{|~ zF_%L?Vw1yLDF)3pb$(0t!-6O^dE+^5kg-lSi$8}lqNRj39RtHBAKMIKE-^?nEmDL> zWEyA8#($3l4sYBX_i>9hli}Uz!E;a9oBT8R@;GJ1qrZw@zqf__HCgiX_TjJB?JtOO zRSK;L1t;`XPalS)BHDxoA&nCMV&&eibe6rGa8}^NbqV`F9Y5P1YyO{Icckv$cI_7J z>;C2p+n(2df1-A=@6Y#ldoIf+uKJbrA~c@A{@?WbXOI8+v*6D<_C_}DE2|g<_-zRg*?A}9FE>`1zMzG(7;*E^#p{9n4hO~PJY zt!h5s|MT^Gmq%uHO>^Mbn#J(t*TbIn|Mh;QaF25cC>Yc=hFZ3yKhyvJGrrDeKF@~o zqF-kk=T^R+HsR5-KaZY$NT}Oa__+D^w~zH4S3(wq@ivHDe%#FG;A^jS2&uG7n08>+ zpFfKPF1!AYix&OX!uxvrv-igG_rJei*ydo@dZ5{%mL=;d!-?fr*POk(`PYk%7UU@X zEF3X!|EWC&?|&WIGwnb<$Ld3Wf9f>O{`Ae=`ORI4b-xeD{@lslxRLA1YQ_mcc?=cE zDQUq+ruOGN%g*!d$haTH6uxf1?t1wGXS?Ktw0+U_bLV3M>L8+bhgI6_a2j2cX^L+p5*X(x${v|zL)-3-lmg%rCD6)65Bvec_ z6++TkDr%t`|L@PYnw1A`7aKpAZF%DR^J53*KTkU_x0E*`=B>brtf_8Z>Q}Vn4k9}@ zVGGmprw`YkJlpX8N!o#Bf8OUl{B(MGzV8digcnR(GZ~J|erI7-|DyIndt4GTQYCqT zq4DMWwC7LjueW7g-*$)NhyCHU>-NhT3%NHsGJW02l5n(3_}ks!&5jjL$mQn&4wk-i z$^YCu{@y>)+i?Er^XqLpe*G%ATe{oLV7j8UO2BMmhC^4b1j-goM`;K=Q~t1K&*iO( zwgv7tHoS{{*!5NY+;fHB0ypM6)Jiez?L7QVe8F|(bmX8QGylERhuuFnz07ad*00RJ zvEiSj4~I_30yEx*&Iw_9CKbPtOPvMDOxmAxShC310XrCt#CqP_msb&eYaC$+7o?`;v+37F01@Gi-cIpN^bV&wG1^64&f z&@YcT`Gn#bc`QdHW7n8|XJmoU2_SxZ&Nif}^VXGp`$#y_({XE5)Fx zGM%C1)nfHXq+-@h<-;6-%f~-2Wt8X7ulxHYmc@qSjqnOzW(EDEg^UlJky|E>4>|X! zeEB{9Y&q`@iFGx9h1<;6Oq`_^FrUqV=WFNEl~+YjYHc>wPgM+=u^fNbKQm@p$D6`(FRdVkr45tbkm3?9wQxifey+tS0h+`)5f;^-{qF z-Im!5B^zHAA&>1HaAp#JYRr_!Ev;9XUh%tCd%>1y2j&HLzfP4!$`&kqsvE49l$7px z|Ka?z&5ZM^SIpgeKZcRF;n_96CrG{!kXUvg_|r{>=kYscoNc=gep}h6>{!bS_ zT=VYF_w<|V;mo{1ICYu|au~Pi2pn!reE(#v7>CTJ*BtGa86*_fEkt(xg`!6Hr<)o7 z-QVzVQu!{G1^=dAV^~q&>w?@VY_#J3;^YdV<_j zYkbJD$H?c+-;Sz^%)+7tdCI9T(wXf&C&~lvn`5MED z;?g6?V=j%)IM?tf?lX_s`R`W2)zH(k#Otb+uQ6og=cb}4E9Bav`{v)p`|Z5zEvx1@ z&iF0PB*n0Q!w1whWt;YgOB1HmTJ1^wJGsqz%|$Wg1&kXcOpha%whaerSpB$z6TK@w z{}Mm{o#A({n*(!#$vQXWGLoS&oei|bd0ML94p*s|j%a`|?_x3N>; z@b1G?r~f%Fko-pNV8!NZ3=-@2-$V&@!^SV`4?lhV?~p9hsp+xI3-;`kM3K*Jl+Zg} z&hexD!?bYui@6<}Ze}w~`D!MO9Jw4eDjy~pY<)EU)1!udrXV*5<^?^QBaz+W@L(d- z`IFtudA!rt+JBvxUzvJ?Ns8gHn5i6!IXW&6I2Vflvy7?yIFsA*#6E8E3k(rEW-6l? zm9T>;+j)hZq~87?HxJ8R%h=3n!jP6-fja2+Sja*x`Zh`JFi18N$54a;uOgvV(Rh6oh#E?QiFOt@pxdlWOr5291@u zddP+d%;-PxQ1R&g)7v>}KGu|NJO2kX^7TALA4QRd_6Do_N*|OfeH%WX_Pc%WrU}EX z=;)cq?Z5>unO1+E#ZdqL9m8SG#cT%(WO`9MvWo>RjPA=mNd8&WVC-gGQ|@+^;f3GY zROCKu<1#Lp>H6$HDk@GNU2bC9Xmw;QV?euB2WSZsBG>Y%ZZPu+oqr;mX->%s*}ePg zK?7gAE2g4UN3YZig5rc9Jbt)#x?KGJ3$NrSvN`~C8gxAm2{ zex;c_BX7e`jhm>A@mDGz=4|-Ap*QjGueiBQtEzRR7)sYq_<Mka1?kB|Mu!Vk7`+i$3L z&tNE7|I7%vHgK5DvTDMk-=E(yp3e*pT71HlIYDIAF_hkQ<0K~S&ubfkZbwf%_us_B?s3JbziEa0Y|Ph72cUmohXqb1t#x3y=A_z^<3|n~>pRlS;OHoIM9?lRr{QJ{d^d(4pVogR5YU1@3sdy38uzk<34mGy@7iO3;h-sxEw|F=N zGWrjES;`RH9(#9Q3*VehwgZj1dr;Q_%oXr>&jG5_w=G}Ac;Oq@Y1C0RK2-z3q_4{r zk5#OYo&9GYo5SSQ>SZYTSz_9OpFedPyxUD9WRG8DSdkoh1T{4z3v|@Wtc$9@BB7>d zw3N+3Fl{nQ-Q~c{(#O9$I9r_M3PZ*L^?(kPhzjsh_!o9#!@reF)3h!7*&IA`i;+vf z28X+>YEE4B5#JTWh3B&!NV+0}>c3pEA6G#Qs=VkXDTdtDD{4@S5Dun~vAMf-7qA@= zuoFX@rn$yxV;!@xE_U{&OAHyorcY6eR%a29dVy=}iGEm@YCt*^PJB~G32gTB!lX-5GM7O9j{jf^2Qa# z@HR}k^$e*W+Q88GjPuBc^@rMc+fDU99PBVKiiMDArXB*SH+#h*UT*)c^lNi{okNQjGsd5eeUZq zmv6kk*vq>oru8gpw6lEUoFn*d$6Ha~trr+FvYsA6P5Mt+gMQ8ZG?_2mjkn>Ug;Xm_ zMY2n=z;VZ)ul|`ytE3ow*UXxM+`4c`s9_5Izv`p!z8^D98N~9Rd!Tsh#j*pU4}J5k zHf65N#!?U$a?1!#d%L&fj-yUV9dAQx*UkATR&y-*GTYbr0z*dB(IcocuB8GN#xWcJ z{jp*(x-VhE@Ju@urDY(XV4(3~+rhI{`I!t?cH1pNZZ$GA?&3P~;a0)Vr>kE}-Mh2H zgyC7&4V1#0k?FH=glrZ6zM{I_ef9dh4Ii&Nu%LwZg^)&(`xmBMFlCq(q*;jE>~3(7 zWsQ?tSMgT=s^Ny6hM5d!L^q-aHp?^p4LU9Nj()yhTT`9I&~yIY8C0{`8dph9XUt&O z^6;=4J4$d$%y-~A&!1m+IQh8vfj?>27-r0$ycZ>*HXLYVS!G}v`Fl5G27}CsnmFWw znt_q&vuMZ1LqFeHt@~@ja7;BHH4$b^KG1zQbbVCl^A;0^S2401QPQwOFiTiltae<) z^8`~Yvc)V}b&?Hi4#jp}N~qOs75AP+=N@JKE3>gmvJllRpIMS@)~|ESX1KD|=n-mOWaCodZD0%YJ%JLc4hajH zvJbD{|L#evTu4%VI>VFzGj^2Hy~I7C=+PZ3wbhIP$5(Zr*7vitCmgaY<%wfn&=)Ew zjH1mXTtO|J!DM@t5_(XJRaEWx^Xcm6d%G|6onz2o$(2Hh%ojZeTwc7b%(}bjD#MCs z*CVJ!rLKTS`W0PH-iBSFE~u-3F4Q!Nm@v$Wca=mbxfvRrIW3qU1UTJ8bH`cn0yc-w z*&56!wks5PG)nk!YGBZps2n(EE)Du}ZXB_17qvuLF=Y+1j;2qjrFG8MDxFj<5jL7QIBSlr;v za4RCz5+#2yG&(j;3FFykuunG|O9@lfI3p=G=n57uvbbq1U^H-waYkt(GBoZ2E!jd3 zEJh|}kqa2Z01gcfu`DK-9%E<}^5x9Np&DY~#K_wq_(*CNYHf4H z)q#0|-c$k9azQ|1VFTL%jzV6vV*HiT0>*&pUL9z&cRc|P%n28zmK0(X4{#ppSyd*<=NNwTYNdJ z>Kgo3{+so~U$5mgtd^eyW%31InWPxj>ThHLwImn3WD0$?-{{DXZAq0OFP9}1n$NQAuLSE-X3}GP;jwTjc*OgxsE6U4aHC%<>+Zx9?PEI=dc}Up zhLwzJPN3m{Mm8=)pS5XWHz73 zg3W>o4hxK#q!{LEZgl}WYa-L^_jbz1YjY0Xu#YTQ_sd^Y{nWo-_PRzFFEgz8ukPH! z!Xc2cq=C&r`Lr6ijC#@K5b8TO-sHMS&CfZ!a>*~*4%9we{#XAz-@#&V@nfLFh}5v} zaZ;GN_uqc`$*<3+UpHK~iFby?wzgjB83SD4$5wX`rYA7>@+fncxJIZiib zU1QyEcfLVhY~Jkodh>7Ud@_$>*wC@r6V(3>WaMq=6N|eADpLdM8}Hq-b@jLJU-w`B zPr#J@|LTqI$lKXUwCF;7VZ+GV@Gw#e(m>(~RS@0xZ~v-4zic87gnatc{c`303G4sw zuQy1$7r;j{I-<9(jXe!m>_`D)#~(|VT1$18F!T1+-J z=&G6x8m5&Mxxnxu;o?$o&+e=Ej2l<$<2W)SS9*MY$~N2k?A4CSM}|8*4M;_N7_=~P-SUf| zwAgSUfF(%lH)~0jQjVn8gIr0KX}Jej(i65kovaL69eV{dntSc3Ab5nLBv7F(;Hl^f zFFl8H_o{oHw|8aiy+6l%jlJUje?R{2Gv6dWx6#ljiWyYp9yrV-#lRn20cka5DQ(F7 z9sK$0fA^`~YLT^d4A!>w37e0&y^3sJ*s|FY6m^B5sHEr}g9K&bO;g+kEE!i!c9; zE~F_sfEGZRFj)N;WB@nEN`*S2x6hsN*m7rxkLI$onXxmpOyl1^pLyf!9+}6R78Ri?4iPk<&&QVfd=O|`(8Dq@Yp zLHz}W|9{>2{UwPx_io|!nBb?ZKhNpS3h&B07$I^e1++J0TLYWJ_SgE=paj2SqQjn- zzuTV|%nQ?6B5~;X<;d(mzigQ6o`=t$HDAtjbL5ltum5)&%mz;`%L!dz_}~c%KU2XO z7CG;~tUoWj^w7!a(h;?FX5TJ6o39P>c&&CnPKY7_-szX3{u4hf7*f9o`U zv0h`g(gC``TA$(kAZrWL^d-%p4LN9GDXt4zq#dTSH%9`x_g}^S^Ec z89$#t*Zg_?UvTkp_2C?u$1WEw)*cL)avK!q3Lhpkur>HIfptl2cF>=;_iua;C{ZK=PJ)MFMYQ9uT6Wn|9L~XK(ljC z_;^weo(0cn*^6Ib_z(yQzhsdaFW%3K`&<7j{Do3~%Ez6K&+D`*za9N@M>8bxb%MY) zu$!PeH$ZuqfsyIA=!@ltxEa*0|K}H56kqbU)#m&4!`0?>@FIOdPXn8SWcp@sx25qf z*OS{Hr~YA@wan@K`t!_FJ1(auOsTQ>yMM{HogmXuqeERFpG(+q$@3|*4QrI=R{#0V zbuRqEpTc|T>;Kff5N~Woo)q%p(%JueX63HK+uu{a>Yy>9Dy@b1CEA#;m+t;_kaG;`SXu0`d4|V`tbbv57F@A{3(kG zVqBkxW62?I2EVVS3~XEhWq(^=uC$j7TDJ?*5s;V++5zyu7hJ8fB;C|s@V)KxwYuhC z=UG7`)}J>UpRU^RZ~vO@H^Kg0*T8llM({Q`aV%(XxbtOi@F(4e*F|Pz-52?p{`~CE z`SJ794@`eMGw$zx^UaY@^4~-@FNEf!2M$b94D6FWLe}0$EOc1E{rew5zW*HylZ$@+ zXYkUh`}@E7VdKB&Y)Km+!_QpY84Mz9=8);|8Dbi>f9vnR`dn;Ru`+{M&Hvxb4TX>A zSvuYTwTd8%%e#uel_d+u84i)ZtX1VdPBz!o&Re+u)vDB=FN@R7PXFok`>)TOD+Wrt z79F6)+XZ_d6VPso0_JxsedNBMTr5#tpZx47oBiClx>xW1?!A8UdfDF>GcDhTrWS8! zNQle_wWk^m{9`d;__q9!Daa@V1E(2|ywO-ACzEAhh&13R>#}XjJMN`Bt zFlglGxq&QsvBN=UH&gAbS3kd2o^#Lp#nV&NFJV4)_8A{=bUZi!3SQV`kHjtq(4H{O zmURBw2h6+@hr=%y`~NxEc<kVOgxT#d2n zbEdZIZfBVL`v3Eg!c|{h{;%BHzRy(RS;#@~it-+J2j&EucWIzJ+}Otz5~@@6|L2T9 zzifU4YVTusaQC5YeeLfA9!MTMP{CrtaO`nj7|8S&D;(k`?th_wA$a5aU!M=2kGja_ zGjG=a1^dBGFz7ZQUvQ;PtKO|CSy6`O5aH^!*ASz7Jm(Il95CSPq#G z2j&M0Z06dW- zP`J4+e!tCr>ssz(P5m!;dYW#5_rWweb7vstj}lHYeOxy8_*2JuTb?vNvvlbC_^jlZ zWL@pNk5N0{pZ>pU=2@lswFd)QZmNJ{-vBgC`ZM|_q!QAJTCiPb?ri?PZn=a;_Fez# z`66`c{?^;;@JBy~S5%=6%n57$sDUb~#&sN1_WiS0TRxw^Zri0VvJ75!`()PL{V{ox z@GM9=l<{&v8h&#YII;i4^gm3u=C4xAmvg%DRzm66OJn_I=MQLn@3_d+^K=Vji2k$4 z1%?^wQy|+f4kWQYI=*kK(tO5%0>)iGrk0#buG?{=BD(y})9G`j6<3wq+%t)l;dB$E zig>2DfYE?oWjCZ8R^^o0{OabHc%yTKnzsoUU6|?UZB=00Iqkp1S_n!6aTFLDP844mzo=k#fo1n>ihWm zv%>97doM5l?|+|l!5!sqG5_QJZi6%P1_nmn2G)d|&~{+R0`t%>-|7{Yyg2>bVItT3 zfAWX^KD7Q`lc~TW4T^n-1!_!E45peb1|WMD%w^JCwut+}*Wd3L44zL8xnh6*@5&FI z{yyhAA$eC|MQa0_gYGE>4p69NY;@SEzd+MQK<#?X#-r1?z2CkSUw1o9y}Es@A*krTFU!zn z0ckplsV-pLpkc-iX`DNAe@VLe`~2}2U;a0)3VHaa@$JQYpVrP;^Zq(1V@QM_<;-Bv zId?V??7IgOm^7!xGHlqy@F2mL>-Y1M`z7vu-fl0;a|j$c0xS9(*c@uJ6v2rgW2VDD z*L_d)F9c84mizMh^O0q%Q<=CjcEd`Jt?NWfN?{F<#A9bFf{(< zIC8$`e@l(`j^pp2ubsGOiqnsfDDgkPY!>gfvembbwF4#2Rd*QS?faEXQViarB{ra( zE-*v&!t6Vr|3uDKu9uqp`ak=G)A#@78`;dTGln;LgF$;e#oQu5fp@{DG33Xu|5{PC zb?4NVzG3Q7tg(y#U;lrO1Y}gaF^VgL;mRhRCQv`SaT(WFxsspxwZaGekg{TR}jLZ~mG6cFR+jY6NZg^=CmTC{kzbvVY3c za8L={`EQis_PJtVwh&~oLtTS#@!j;B>pslme0{oU_- zT`TwY^Bv@DW;dwInXzhLZNv9npNr=Fnmp%Cez|($Od$n_1v{DkiSOOHRTwm$)TVb~ zzSnw*UkliB)*Lu`|7Vl__TIH|2M;VibYEo7S@5p#4J?ej4XO_;L0b#f3U>rDFDP}5 zt#Mkv^RaieSp61dYo&tx9j||1b9~{kaj9p6!)I2|G?X)F8Y)49sqk~3`qM@~=~D@8 znXLyl@2d~D`+L9nfFrA&h~3A|lg;lQ`hr4xMQ;O}L#~&i022#GPxykamy15SC495u zl-XDQp1J+~-L;A#%NJcSoL2WIK=!Z@c&hCUVmXn+bC#eLbN=1mxk}#RVf^0L+txoX z*A=TtP~7ZBD6Kw0oMF%ULPzc$YaF=b zd8N&x)EBMU1!}l&PyzY=qvZ?+My9s{FMREOtX5o-`u2pI*kOT+f8X+DEqe-kE+#de zdih^>-MT9r0vRhC*c`mMwLxt%9hUPpd&`}JGcc*}Q=93-lzV7ETd}_>K!(pRRaL)Gr-!z_=1#&EWN+0+^BN%TK z-dBTL7FnR8@$e#!PVn~7Z8`OE^UlA$*R|++^`_G8_5bEoxohnD$Zher{*S2uBO?=w znBD@$4F$&T0tyZbLYbys{U05AK2hcKChPsXY9t;@EZ)kr`|y)Z`}1?lgp?f`9J*Oe z7@}fgRthROB-AmzT=es8`ucSob3R>`w_B&Z^HgyllRW?X`u)3Ibh^$tvTz7UtZ!gD zQ1SPyUjswqMy?~Xx7(|+f7r5-E%8$n=bT@YtG9o>bY)f)tLls@&Lcm}zjAqi*P9x| zgI1P#-Rx&zWYQH`p?3Jo`u~-(4*X2~oO|B&>DNqWZEWW7dFtwHUp3+TUT~>sDt3Wk zMPXr!TLVL5Aomi9c*9qgJ@<7Q!|xZq4%w_EmYdi(`%txc-Tn`AmVxG#8x=V-7}F( zP*}ksL4)b!+y8BkZY*=u+x2-{{*#(N+Z|_jS)GXq`Vmrbp1q8x*drNSY(z5tSB}4S z8&p58nCP%;gWExlrEE;=f1eYVTj40-v|_bG$*=lYZ)MQp#53Uz%nAE~zJa5M<^B7MS=tFfYgy71n2DVrf$l zNG|%lU(sS)W{3E@Ki{qw+ljU21P8Xy>c0HH{Zys_D8qU>FemtYSO&6X2g|3o|NDFu zZSrG2&)zGi&^JLRt!8wFlIMSBX3jhgI0)Z=ooB#`^RQ+^j_VGwt63b?2`R= z>-PRFd-DuCmzyz_x4i!M>hso4Hw6|B0R`lO;;hh$q|~qf)26QR*dkv0{hHF}8A}?O z3~Mx=c)LhR?3@7VHYM0INiqC({0JHWkJ1)sW^>5y-u>gqxjz^0_DBA?*q5`_Vbibr z)vvWJnOLmU4Qh2)fQKDQ92D*eRP8;!WVQ8#S02G{z8~J-VJNnzH>t+G=kMIbUaQPa zMb(zoKj`KCInn&+f8FRgCR+V}dw##nF}6)Nm+!YO&NdgGZpz5Sa!Ydod0!`~B)`@}gz! zhtjo|^gi4r{pbJL9a(wc^&%3h8oU|WASps1W3$7ZqVRp8aZ$lq$N#LYzkFifd&6`&=ov7snvuQKbSJzwH;5o``^qoh3|U+8Z%<`w3_9(ukhK&x`nS zUOO6!eLdg9-xfPn^uz66m6Jf@IaV4A7%v=LsvoSPY1jX&O>)`WUE~qtv+DCU~>pnere(T8gB{A!EWktqu zKD6|fN2jVb5YOgQ^z7LnHTHq0976i2}eNl&unJk2GSPi1=nBu&i@zxrrtbw%a1z;)R%ww z#a;PMr0Qibe0ZH#T;rR(5pTJW9U~J9M@fvr*VB3q;*(h!p4U9-TDO0~^M77HHuu`k z5pD+8n+g{)7c`njx?6&;BIIB5mOr2wj zzy$`4g*o7f?~E-Dd){t)s@bxcORX^4>bYTE?L5Z)Ie%CFd^rm=`J&4ZaqK2|c}GBy z1M>ptz}+%SPP^DZzk@-~Qx1ag7gWDwP; zHSI6cEPeBWUq2l@f2->6pE2*}yMn*R-t)91KxWCefQCX@U8jJDS$o11mM*&fzkTJE z>kKnuFG+w#l^^Vk|Ie*38&q!k1g0& z<^T1Vx!h#tnxKmci@jm9K;R=5L>8L~f`Zi~DB<$A%SCtk^+c<=POd+{dE<0{zyH7D zzwHlSb|s;V(+1r5`o=Nkul$v7J5GYf@U4_CXzyW&^#M(PWr*(o_rGz&r|J7FB$`1L zJ!r8iBX2|5l|V7DpOqR#PRISfzkW`gUBIaja7X3r!&}+k`HsMR4C)rEwoU`~DshwS192oi~Pg8FT<~0ccXu6SA_>VKK`iU+ezUjP@Ht zE@&Q`eOWU7jizb*+vhv$zeZXfJKS|_rdD}^23MXDsBNX8xqvYs=ynXqWfwFW?|m|< z{r|H@xWTBu=I?%1hVLO;^nOd`)2bE0?4zpQI7;c65?gVv}4g|1z#s80Y zx1CgY7>(b18jhnR|q!3acA%C_2-S}_e&(lO?ZC?H0rYY z#h3p|7t}z@fEx=z6Bge0_JCX!klpxWWBytXMVr+T6EtfqV?XSW7oWZUo$%huyI*c` z_iITnV`YHOX0mvJ;$O@hyaU?A`N4^+=TA0Hi*C^S^|kUrvtj6$cijBGcek@0cHo6{ z^j@==Fhqq1ae_*K0|qRscH}c2NaQhHZ}{mco1eY>gf4K3tgQag-CNEFPmw+j%nMRk zr-Iji^SCeg!qgCKo6f)!wl0TR?f#pGlbb)^2K7rAncfOtV91E~O$D#4^cFes{crbA z@t?)hEY3Lb-Jf{Q?qOf6%!OCA`g_0Zm~%hVwb*CffBjdv;E0jvZeTkQal;q9)PJkk zk1OXc7uQ^lcRFpeDEmD7f^eU?^S2kvFMG21y-?4jJK(jY&||yqE&&HydSl%F4@@N| zU;byF-+o2E_V53{56{Wpv4Uqr(1I2*u~09N>J281m#+W+t_||VnjcIJ(TCOT3@zd5 z*N&05;oH>^@Is{xA&nsgjJrhSICfvF$y}+p{n?N0@jH~Be_FHX=Z=5-HMWE2>l132 zq!=D=Ha7-ksVe~r_x|jCTpY1{8kcVC2Gdvp&~(_p%U92z^;K$YkSUvE z>I)bR?5d5y1AM%!Rg;5V*W33S{`h41!BV5{*Z+_iH%dP{aF~HRwGQ5(hG&v6Aw+<~%y2k(T&Ze15eN&$sL-x8z%x_>j@Z-%{@cgi;a7Xm^wLY4*n-e19z0&7? zn%wmHYV|7K*YoBu+ic63us;_gc8TQ;YzHj9o&^uLnhMXD5!aAcxp6_ve|wL+ z{LlIK1teq_6?r#C`Z$7Dp=Bv9U<~Ls0ylnGUMU1DUAZXUVe#w#&!_DD!PHRR^1Z&` z_8bLh&-WTv2E&$ZTR`n_hQ?N|DRB%1S(lg^noYe_ug9MVb}vtm&}jgdI~hA0*c?uC z>)3)a`i%JwakYBKMCP0C-`l?F`rMbS+3%%JdE`nwSvy^B{^bg%n<10f93ZP28V<~0 zHDNdw!*CfK3%)Gd@~XZ1mfT?~(Tu3AD|`8U+TK4&_phv6&uFll4Lm35@EQ~m@kyMZ z#_oX{mQ{5RU!T`yuvo71_t5hH?Y|E-Uk!i8k_Z_`-2$5FzWQ`3c&7VIv_jjWc!%G$ z%jff7TUNlV)}ovE_}EJL#90}42E&%!`!<1F>p!`UEQ)t{&u#7h=TnVy_TReK*Vmu1 zv|F4rPs#vR#*_+NU|8WSdIUTZnJn5d`+byjP({s3g&>aT%ex+2e=hmqYG&2PqfbHo zd$()@kwP<0a3b|`U|taRQ~-RIQIGS2@K9I(|L3<$Z}@TNjnndie{aq*omW51{Bbku zGegMEzZFv(*c{fLQj`FNuSC0pSL*uz>w_*ZNNAK?JgvVh{a?(!fB*kA3V@3mhDJV6 zdwao&AkcVvV>9;=3GaVQww627e=g^j3v9o)_t~tM|HUkK=NvnCKm(eNRv@oMljsHy zSp4K^SU!E0P1}dol7ovu`Ka#qTK|*&{qt_>eBNHf<|df|o@H}5%VNTi6(#^aEXyIX zu~S)Xd426X4byC|%;1$fr40RbcKAKgb z*z)4b|Gv6QOTf86F@xa;_%IlUbk;|_3&ox1>|?K8=)TK#?&leATh&(oH_5L&d`srZ z<5O%|j0ZN|O9Mx^6=w#+m6cLS;96l5lX_l#7RSpKO_P6ptu*@P78uT>v6QUr+{jzgl!I@-0}7Ae*M4s=V#^q4=?{ebbt87c)yuC$81ochq`*_ zfgsb$H(&q%HF?O>(5Q6eQaVEpXL$Twd5(kNdRXAaga$T;_AcE}P;6JUIGl`MP%m@N z_TM&lmkU?L_4e14`u)vJyFd9M#?H)Qeovgw3u|AL zfav^H^HIQr*x@h_g=^SRf=-#>kRUi7uYh-uYQkqd}zA z;mSlmqwEh7ho4^puWm@*w8?(&kNbh3Ty^Y}s)XwTU+{vIQc#Z^w!ug_zR>A4cnd3x2R(?MHltn@GRuY4hXp@yv?etvrW zt5fm+|DP@R+rG~n-X0NWh3}PG@EBC8FXC-j+1a^PR(<~Dx|?i19AXJ`f*HV3egSFO z(u-9Ndo+EHY~sF9Yi#>Gu)O^0Z^6p=jsKSXxs@#c@s~&Z28IV7mArnSBDz=b0z-z+ zN)~W!>?}Cr#?LP{=e{U4ES2;7KVLU?>Ym?+&a1zRY)*u=58rWRFqCZnW~A=W;GoXB zYSo*U{|znw)_iCd>yE3N_2oDE4FfW*03hsLC(hjI#Jg{;eD+9Y-&fi1V z|NHA7nDJ>9KXm_-!v%&9ilClRV;<*{g1mz*^#bSOK5qrrMCN;6o)9=3%*FmvsOQiF z$Ngzx&sJ_C)s^gGk? zOEKBxQ!&FeU>f0FLmwP`kddlzT1D}|Nk@auvGq%$4lcs*%)k7a9Y11%%mQgBGkq2WLe zWEqfr`0S6`AABzS<8yuR(f;qj$L&TF9CvAaxPR!Y{I581;pw18(k`6^j2E81Iz5-6 zF^uC&n9n{F^+(emIEQS`{r;%>lK;D%tEXT3*zR%rHp@4Cfy?*b{O4T{s%sr)vzjp6 ziizDRsBnRkaryuA>(4yi$x>UlQ8#V<^*jHTT8kIYN#;mt6nS){-4oPcclgd?!mum6 zSW;Nwf>Pt3rDo6mPpPj2jT70h#(ll3|NZ&S)pt40i0-i5^PRc;Z0$wx;_)qzV|J%0 zGBV|g-6;CLxwtC(_MAm`zUhD1BL6O8^Z)WaUJnGA3~OEpR9DOf^|_<87BF78bjYil zp)rpmWMTf-|9MlY6i!11$tG9(OIBQDYy8Qz#EUs$Ul@4Q>NKb=9v1LYK;eRJV+|*0 zJ(br!{oCE|_Pmw;+4-OMeZt%6bBeg)c|b>y#C@`0QvhugbmRKb-|jZe7qmkBg!upZ zPrI`}o%)u(@x{CYulId!W#2V@Yfrni*kb z4`hD6b<>Z9jdh>oy8oy3`S+!OM__k>_8%!4$c`KJ{z8JPUXXz=3(eV|j*`!U`9B8tc6L{^zH?oF?&cDa+??!(8eOy0NfW16e;oIDQ_s^a> z4VuMgW|CrHmI1ZU1L_-By|Vth|6i(V#;J#XOVNq;_9p#6>!DU zEp~w+!elNpr+`90oWi}L-+`JT&!^45+0!o8(S9$rv)bk>yY+RwLXP(GKn2Uc`@2)Y z`Rtqe0>%yYqU%A6cb75kwQY};aEc1M{Gr9vsWG|S{>S6Pzb1<@$Sf@MZvR*KBXZBm zr8_;r5h|v!fN_I^!EVq9?i{8{-u&zT`!_Qr&-p#IyyoTX%BugnZI4Nr6um^S@#1XIoC3E<#O}{?YK|0g+njo*D`@fj-FunalR}mR`aSsfAz{zPJ0)qL zerDq?Zl5c*4j;jtAZMn~4ZrHc?YU3?`n&h|yPr2##-5G8*DdPtU%>3%hX+ahWip`F z?iT+8k_>NGuk7Iv$XM;rv+jR>b}s+K>)hpazh75Q4{g73_BIO}YtWwy54gdLO%}Xn zl46Mc&vIfuL!%qVk+atRr&j;B-zs>JtLOz}9G9myfO%VNLivoSe;c={Yl8RIMa*b` z?NM&r$Pp4+^)y1wEK`>yZqwh_?N^lq98EkQ9Q*L^`~B-n8jgbN*2Ur%7$o*?1kG7) zNNIfX>%XeBS!KgyxgQrF&z9MLW%43%U1bMMu)HWmrimMS;CgvSZp1raH_w#qq)>Tv}* z!Z|0m9Y{zz&hq?7g1nG*U-C@*Jr7hFD@6~@YIP0jpTO_UP$+anX(21Oj)TWBjtgzy zVi#XLQ?hq<^dz=jZkLU(-VNP;@6&7N%U@UB+kL;h{=Rj3Nc`UN*B=DSVt?ytwqD@= zFX8-VeeH(Y{tt70zuUKb`lScb40*S{%b8Z2wTh?xpY*@BFkNiN#x$>-OIi zR&ZDl%j9qQr^RNWbC<7Hn|IgIET;umZb#Uhoua2J7wiCXhQfsaM&5=tF+cEX?+-4G zarz>)%u>l;o)xd)*|?&fuh#pzl~`n-ybKmPvbZ+rW&+dKFJDA%!Y z2;A7;z;@tYE#z!KUO|tIZG3sklIG&dbc@veT6Jq#O$TCU;L%qPiQpZIoT$0)qv^gz2EB)q;OaAG@sQ zNjm*{7c_I3h@SuQmZL!*v*e$9e*7`#@xj#3K2i*+2VKD}I5E(<)eAb7fvUcB+-JW0 zR<-{tUvsy?e7>`KZ0MKQ;>9nH8f<=jYM}_E*U8AlE^>jvLSdpTXwYN9JEo=Y|G$r{ zty>?w=m%3nc>Dh9|IO8^(2lu)MMDGI0mg_V07e?nbr)7j_0*vv2?BR`LCJo}3r7 zC8}UBse$c)hff)#d<2b>Y`D*FR~uJlbY#(M{U;}0PFniV7P{Zc4OHgbJvEO7Q~?<` z?)ep?zpNtY;FdfO`wj1wHGO;~`>J%g>|EhL>lS&seg7~1RSdLsfF(+G0po>LMYBKy z23BeXH_x6wZ(J9(F>?2x`Wd^pp1%LjzmG9t2M4(LY&h@&w4@xmk}ON@g7#j9*pmPM zFK7yM-~V4rE8l=ypI?PFezR?vwen8AU5U_=Ugz19@Bi?L zh|*8@4edcj;KDTcReR!4%02yG_S`weyic{a~xtveuBFmL_;mFiDx<4O+vJ+`|! zKS5$WWG}*kWJrJUC1}cDm+9lB+1H-#wAp>7CWvdk=W@3BcJCSMpKK8Re7N~rj@VD> zyZ_^lM1p4D1v1t)usLLVXD5ML?b{Fdybz6T(5w1vS>yDzOP^tfwalK+ryi>0fHElq zBa=4h+~Lk?wctc<#q2T}*iwQ$mLn?R~ z%!+=8o`p~T{_pPI&Hpc_HqHL2&%IC6;yfNMSrmCBf$`X)2as|=8r1lW2rX7}XmB{q z`sjF`z0Li}{|^OS?oq6@oBOG!-RtP;+BwqeYaP#hT$%d0l6{Qci~|Na+T4G&jM&t>?* z)|OZc-R&}!Ns3{0OR)r~{P1yC$m2FPJR*F`)j!@_zjm7c{O=9>zis)d*p~hlG{Cc* zF_J)sCdZl}2j+yS z1&}_uq565{eN}V zvG3>gb<2E~u4mX`GN;gOs|jmAC}JBJ8n1F@Fsy+tD>JZdoWy+_V(3mt;qa$j1iOo z?QPCHZ{J*PcKVNR-a!p!T`o}glz=9Km|e|WLDRUd3Cp_lbuFjQxbxrsP3)h4|6>!Y z|Fl1pfhE7ypi`~a&Up+jZ+1AW;=2F;{in|lrIv?$`qu6EX7!;%hx!nS6-D(Phl{KX zkKYw|e0ceJp4R-bTRwFakLI0Kst?}4kSv(t2sToiNs1wM<(5iNCVI&9v8#ICq~!rQ zjJqT*g}&Uevt9a}*}gCRFZLSne|z$g*W#4NyZ`d8;N?0B4hbQkYa^Dv0k?mHMQ6Ny zZ=^(rcn|3xh5LO^HLC!n{T{KkS!3o?m$TL$K_k2I>wEk6-)oo;#F^^;=4EJRl?aaedJr^`4{ADxftt=mQebzfDNlH& zZv4*9eXsVJ^M5bf#lL(sd41j2;7?oT$5*^scR+(j58Mzg0UhQhU25jd#KK_`rcn3l z+w&(+x^*3<+n4?G`SMw(GyJgHk-M3s}L!k?TO$nXG;E%)5c1 zv5oW0nJ`y<`}6nB9$bFD^tp#t-T(jRK3r+H+m6U4A3;7peo+JLbHzrH(|QaMUriX= zyk~ZqB^15UK0H(Yo+YAPAp@F02`~W*b%X_5UaMRen=m`sk|Nxy|!` zKAQ7bF!i%f-a!p+J#fRN#KVDkf%8!_(CDU#Z^E+$HxDwE9R0WU)OP8Jn!J~ulY&mJ z)@Pge`BC@Z)63^WL8XvtMSL2Plj#xH9rK z=!M;#16l~e&{%Oblzpjyx1K|JY}Lz)Rhi9Znww+NUI8T%)6}4E=^GT{2i!OHD5Z(VpqsK%~tPE;uoI(|5+LG zV8PwyXWlpeyM5qg@UsIq(+vt6H~rt?Ut`5206N51nk6mBX6F-|q>`WiH@8dpRE6{UG={*RQ3CO{!m@fujd!8=iuhv-4+DhHp0;p>%g4wW(ga( z{4wO1Qp3DUS?1?~=f@`$TensGea(ML_~*YvlMDaXCl)*e<<|wXnWPx*uGt+4^2Ro% zpa1?V7Z;t^o|?e(@>I#2jdr#7nD@Q@_xRIc^|v>5K3)I&-*pB!YcVoOF`NSx_W~=X z9`M<3{^QI38~gn$pW4s2)3;>~0&Usji)}sHtRFwC={P9992yR!uyp-pzm{^z9K307 zi?700r<14uJAZlkIc|Bt$KU_gbH1-UzZcQeodIg$ZWYBo-UY1Ah`>H(lsqBBiRPu6`N&Oky zuR$}C%+~+h`Tpd^ey!P!>l1Wd>q7EXqyzJU(^t(w=Ura$ePBJ$B%Y8b&x%*LZ7gAaFgx+` z&ifLGcCH~KZ$sxrDR4O+rZpkyCY!_lSr-{3^nxC;ITZc>zX7rVM8N-JyvudkZ-`17fAmqjH~_d$o!*O8)W~ zfDWd*;`-oH{=e08P8oYGot*UL<>#O`&Eo85rytzTAo6D;LxN)tbL5Nl+6G*X+>o8n-#9WD-c0;39pv|AY6A9!uM-$|z1N)Tu2yS+i} z#NYiEcF!v^Zra@aAAc>{1e~P=)-XO$0e3TBa-Wg;^y2UTqhbrXHGh4nT=C}U`~UCT zogaxJ+D&SD54ajSg}K3VkL*Gop<3lX>;0Bp-^}nJz&9!Kzs&FMhih7Yw->>(ln>}? zu180}`Sm~}%crhCs?rQsA04#Tvk&l_TWOp2SHMrpCZ5|sj7_``Z|TW`MI`<1`+|K0iito{6r&5=*?Oa9jjri0rm4F}?wq!_9j z!JE1o4}p99G9M>21>XI?U&Q*GcU%K2DDc$gjqMKq+E={z ze*U@ky}dtUqPik#7!S-oZ2A1&7DVM{1X|`X#}!;Nrm}nr|0MdCWmi1w_xb$S7iBQ3 zwS0N`@B^&$3}TXESi0mWSgkt8ldI1^ag}8LvXf%!qSh#WX^6*hY3TbmuX*_047-%kJNyqTJRUe@#FF&`unYV8*6AbeKD|1M&V#;Hj(YJdADEh!@1k6~lqJhmJFz+C! z$OLt~!GaA3N?1%7mOa)3muDdFf}>wRLu&zJz?5jv(n^G&f`Kd}Z^O$S-QY1ah<=dr zF0Kp)SmOX;x{td9bAr{oMWFcukSd5w<02+02G^aepkW4Z5P(uWBhzKU3k;)9h6KoH zC_};loODMM1|)-ka@!zJ7y--K9JH*8(Fz!cc9vI{89WNZ+wb^JMU(@eC>9X~9TwiU zVeyMI=;eW|5J>9FA~uJ1Kbv6C76p(7a8cB_iRo)5!w#keIZwe&2ZV$l=aprQ55yY$ z=9a5L_HIGS97d*A)~w465k3ze9x8@baA2cAT1(tOXR-^@Un5i9*0^f zhRs)VpDx!|0x5;-5qQxCk}_Yn-*oc#-3Rlgfno=oT)~V5Yd}Y)zr5mp|IhunvR{ip zO*@3o8}mSix*I&`Q@neLw`?=0nTuq2Dr?qNh80d%7&M~m!Hsl;bCj93W-%NQF=4PO zdNflFNe>Ih6wwu-%nJ9J3xaRH>;~_(2Rj6;cb8(oYBmR+hr9AN%vMFwC7_`VlCuA@ zKLo2QS1}5h)jYrM%yoOWV?Mec=QymDV$f{SI`-vh(PeSa4iJQu93{@6yAc>Gxi021 z+_m_A2KBl%F3?>U6QUWei_F;{fTRmFNGr^=HJjl})yG$UCuT#I3&GRBhDN|{wgX8v zUryQluXALEv;pDa>hPL1>pFvny4+8Pu>Vf4pZZRKOMw|8A}eB5G_~V@*iAQiPU`FFMt}FW(C-}|!9dm5|vbC%R`e@cVE%?UU za9rS0rK-UZcyvP;xuO{^%nP{omOo`$vQ6yZ9weVAI5Zqs!YXxsy`1CPS0xF54Un$DC$>H;jRU z1FV0>5{Jc74E;_=zVvxENr2ZfAiTCEa6ut&L)PeS`B7%CO(6?8j=`=1v6Zk6BLYpxHa72`0>MK z3J+RKSW550EVcs+_%y@**M3<3&~zer3k4$9C3ZXPmSXsPJZ5Inj|(@Z@}QM9QhFCw zu^m_-rg`i>m*Ts;ide|uNr(VArgdQ-+X2tw>un)_uSQJeK`ZK|^e*gTJFr0R>YK#$ zCsP^nvMX{iOi;UU3Q~3?>CZDXQ)Nc0oZ7gzY+^K6;aOkzZmwZVeMUtsh6zd+Zm~K1 z(>t*0K6m~puYF<#I?KBt-9K1yvEU@r+FS;nu1S7T<(dDA>n3xnfJP<|;hQeKzU1BM&~LKQ1LX=221cfA@TGeVQ~cuoPyJKN zP`@S2;5b?lqQ+ISjL|^D@9UP||1AFRT-Mn-$rzl2AxVOTLm;BtVYw8;%Nxn%p`l+- zOXd{nX7r)7SQwdng3(>U^u-d;Qs~_b>iPGQd(T#LHHS7f!MrSg`5klAYPkJ^L?r zYN1u2C2^oT6&;_h{<(AeauK^*KQ}YLA`D{Embe9%c^jtavv?@(vkU)u?^^gR7L+n^ z!DXhoc?>*S^UcoPl}N5S`yg@oc^1%M6~dgxFz%9lj0P*-J^J&D|EYcb#aCXl!6V@C z$S;Xz_V!fqcy4fjMD)kzQUzy<;5ThxOm8ZlN}q1TwZXupO{F z6)s!#I{(R62D`2O(`~?eI}t%HrMZA{L;oh*U2+G`yvh8zboTd;o0g+Eqru@ZXuG>+ zq}9h2w^zsgdHLs4UuxM$w4**VdK%ac)Ok$>)qgw}7d8Ag`~=!@hcL;+*@5{1Luu3< zgYWAE4#&$SnCqh@!YI(WP66|>p6|Nvp%`a#Xk8gv`q>IP?sTEf%PAXXq^%DAye?ic zv3xf?$ia-pTcEk^h)E@fe(hd;p}z9o-*bZe8zCDI;At7O3Hkun=Vt}q7#L)kJzX3_ zye68}JpUTnwf;=I&8DWqZ_rA-WuPs+hlG~&7EeDiC2lsqx3_0CQhgI%44AKZml_1AEfQA)K3&OXr`F&O7 za%;B4A@Gb7B;kXUE&!b#A~oak%7ycts!DlYPR^D%hNdLp5t9@{wu;~9<8NM^m-!ZK z*tYtb#gxM+3B2LJ5zt{b4zvAiJ~jS3_anJ-MfP!nIf%>AKz218xB=R?;4;f^=I+(A zJnbj7?@sh_mO^VR_4qn4C*(}|+0E!5Q(r79zgOr%pCMm1c$o-1xD_^}G4eK;3iI)U z>f+PB4e~sicg+5Dfj3qlR2kSZ@-}eZu)oDJqtku~rvYP(GfJI%U=?Uq`)JvN=1ZQ% z*TXhn?SE$b-s;1>ZwMp6K{Xk4pvi)KtB*7ELqdO>r_`}F&#Xt%%`fNo|#x#L~&6;PvdbGw6M~~hDMuS~fZ+!Q$|8(Z<%W0+df=PdFZ{$b6v3i4x z19JlBF%!9E!Or{b?Z3C(VW|l2o3GFD0WvTQPBox1@|gYtMuWP`p}jL^GvxOh{*AHw zF)@_)fdXoKcmwFJlfMt9)MXad{oNMlrGLKgy|!$$4T`Fa?G0=WXJ75Ee*CL@wZZ30 z0*9|>Pn%T&w+h5qa1eB|j85FG4`=j4jbHxQqj2DO_KbN`KyvWRwV)BCNH@wV@$cQt z>$~L7zTCTYnWk4a$h~ldQlNp_$4N_r4<*$f-gUoE@n4o-ad>a=RPgizD2sxXUx{&G zUhs@nYRUE;iSOR560g&+-?NwTrPoBTEKC{n*nl_MOF`}9)gt~KhH)F$U$+HMQzL>O zvW9Z*ti99B&fUFn-?F^OFMETYL!9yjv?iqlD65$)zxC$p+ET`#3Rk~Z_im}E38Dno zimjksG^aP$6m?5quU)|P@n{}r#Jv;R2&p%z*@>vkIWARFP0b{`Y zs2<(PM}K~eULCR5qOIWP4K`EAcrq-%n}GH{U3?mC_tT{QO!((p4AP&sv+S{Z|1_)N z>B>FG-N{Bj&{D4*XDV$QvI^_o$%d}~-t^~3vhKW9pko){iKO8GBZ~6bm- z>y_8@epp|)&AY6_bIzdN0u%z@vL><^B}jhRit(x}7th_5Vz&uY1QEYE@Air~cq;Wt13Cj%ThfQbKPuB$`SbLLwoeVO{4G6_vhb?ONBpws+Z3>`30#FlF{Cv z&2THweX~Tn1E^W~+2Xd`f8L*Bjd45vTzh_Y$*UhVr3XGcb)7#K&Sj%+U*}i%@kQYg z$odUf0o%o~oya>OJ*Sc7Emy#Y!tclXu7(5+7}M7cxmWrM{wH%9H$8(jk6Ce+rK~m%3SX^ z%NYZlB6(0s))&(pxZ+pcZZTze6(;x-<{fZ?cKFO%wKDAce|`s3hFSGdGePIcfF;1r zyyB-27rcFn308f-_MX3^9}c<+|tMrK=k1m{`YGh^O%Cp216aUaWH00+OuO_C-`!a zPoO1k@GfNIGtMPn4(?K03%ai41Zw3yORXUL#-_K$rM^+j3)W4Y2s*g}5p*Wr3QPH0 zrv$}LzQ(X3JM{@_FX*gL$4BQ3iHoZF?YW&X%*O=JyHmHSo_Q4|r#0Us)WIuHH<>6A_ zyv(@G-sU#YmAUY+2veAFi8V3tcAuz>;nvt#Mg!BTM%481k;(Y%jz9Ge)-Yc9vH2{> zP^84|#dM$JeAPeoWg^u_6HOVye4c^NCWL!TVFGK=pW6k;BtwIBOhEVZKvvwr;?Q9( zOIh2N>fpITRsnia4D3RmAcw8O_1}nAXl>o_^^o zOdEHo&MAK9>pO2N_zE)AK_?BxfL4+JTU-`+9=pIWW68RWD9&H-lSy-rdXD9LZQuDF zYz~*TH9$*I5Jk|8B?sOKROio3X$VTrW|%Tb47K)@(wJ~h&}@ErEwgaF(a*+9;wSGDi4jun| zsJ{OG14*6PyVwqFGKqsqfoy1CXzb(s;;g@C<=WzF3>n#}XHiR`FtrJf;@f?><6~|* zp1m0jItOi%CQ2Ci_$jRavH!R3Wa+wJ%GnG)``L_8&Clds^2IvwbZfc4b+6~K>sTU@ zlWBVU`(M}7%@r?heX@dagP84c)CT!w*0QIq|LZL06uMpg(kY|y|0eU>2cVs#&|n6o z^$SsrQ~nvu+5GnO+PU{9b?#(J;B0FLNx<{^1;s{@f0|$VDzd}V)-Ya}>NQ&(#V0zV z5;33tYbRb_=045t=Ck8c47uyp?L--BJy67y-m1$zUFUm}^!)1#8Ofn%QHR;Tsekxt zu=(@$Z`+-v<2Q38NUzR9oq^ae$3b+}hg}v=1S8PrX$V}N-?sL)-OnT9 zxBmA;gD&GWMwxPKJkGL?^I^yR_gZb*47;K#KO+~44%=DE+Ct{anU$ws{lEA0Yp#a9 zu8`B-VcvD%X1(;jRad-bVXLrorI!?ga_~*m-ky}!ht3ZLe?GT8v)i`Cl;N0LCTe$g z#li!!iD}P56B8H4AHLJIYzgMV6Ag`k)(Lfnf9~$QuOqQ}+d>nDtZ1oD)Zo0u@<_hz z>}|Vk+YE0NZ((z&&dO9niL(#6jsC6D?I{&&!wermkmm(9A4^WF4p>36q%OIbL44-ZJHqO0!92 z4){Vn)ZsJ(fgS2C_p}dRTYI}uM}Gn1hCRLEC}u49$Mjd=@ajX)KCfF}zv}YY3)>ld zuE&?4RL&9I4nFyOv$xfj=sK4?jo@uq7jnx6*{K)I8mF8)r~PmL+T6>tuGHLNGSG~C zjFOWMsIb}zn&@!#Z8U>R;8?E4CuEy z0Y1(N9(>b;Jsz5WzW#Ua_B*HN$@?CVVpwc>6SWjuru<=d;^{-TmkaNT4K!uA73#kk zxew|vo8_NTPTY@XGvAC^#TOZ7EZYaUKLJ*cZs>8SDz7;A`TmYG-SgsF8%!C(8lMTE zM9z#A2Yw&oE#Iq`W4Cx##*{wZcU%p9qIbZjF2IZl$ZizT<7u~fD}Lyxxc)V(^K5F% z87~}I~ihr=1)C$vL>8j6+rEYZ*t(0=ku?9@ulEd zllgyPE5#T_1I38LsL|cQ)W7#A=bMzu#WOEp*d&0GT{ld3=t*yV zF7sQ9``_EAMhAVjGx(t2(6vFou_W!rp0~b=OSh~Kw>6h!3}^5;b`^Eu+Xnr{8TV%0 z|Fi$%?Eke7eZEKIN4F9Ix*zz{lVVUv*Mg!UMK$PsD+UV}YwSS8J zPy7Aelh@sj!R(+ch-LcTCJ-;UZO;FdWxlH`H(zAPh&!8tT!}X>< zi^NU5|L4BP;(JWj7*>?mrXUaIHA-bT3k7+7_4apL3`(dXs=twIg$28r z;yE`?7yo7bf9<_L=D$@gFl3bdO#xqM15G}SCpnjVSofj8LNCYa{pQKsNl${9@)$K# zb-|~0!3>+FT;T5a)&80HKl=-FrleLWF!DAqb5_oUR2d+DF*4m1`jPSC`+sN92Cui* zw%oRl=56?P{RR(mGBoHq@b{M zoZ@_`?+1>p?uQ((4c__>v5SRcir5a@mM`}YJc+{TH z`qFJ&2@+OUc>a<5%Q5>UD86$@@M1E5 zx??@R`JJCl8#1Hgp5CARU-Rn1X^F9n0jD>4piBg43+>R>|L{G1;rAum+kI!tMP8g( z_O7Ra%^}?@cM&upfGY4U0S}hdto;9<>+j)ne9_zU@*n>3$?Rd0Vn{w>A_7tXOAlO} zG4sQ}{qMeD{YPj14{@PCE~0aBi~9?EscG-SAE$ikgmry`~2>@HH)Qt4sd`BR=A0> zCPZ66BIeNl^FO~&5(tXhY9A{)?{iajZSH+8mbX@dpiK)Kq>v`k8^yTpOn<&b`M=$q zdiH+#bKl?HpJIRWe^uR_`pf2re128^xOLaOty@3t%=w>+0oslK`E8a?I67m$%P|H9 z1_n=8KbLh*2~C;FhK39bEUujae(t*xM?U@WLASDb83{oKT8JHI^GBATh7#SEA zFu`RN7BC~&AeC#{l0GvqFfRv9&_-|`4Uf$TaTob_?#$g3>Et!HTqdt$am@J0AHT{` zTZ46C)CKm%54=UYzj&_bX!5t?2-4_K=+Mnwyv1qruYioHSHu=i(1=_Z`f+IoztYXA z8i75YX5Y`roWD~#IY%lbsq8ZI^y>6)XMXOptT*cil*IiumHy$l!81lRs$8iP`M5c_X{Y8#TXkSvuFxRbF4W)5AsEVcNm21*(e^s$aHu^Q*m6 zRy)ullJUs@vc&2F-Yxrh8hhVH8TwT8UM@N?r(H} z{fl7Ie(=|H(%s4r6D*GU|KapV^Ws`t0ZddzL3vvzoWWOHTb0a^j1?m4DBk>u>YOy)T+qz4UJ0L51?hrw-pm>X}@d zvxEFUCVewj3{HP$T4wg|w(?z8&M*9>m-}DZc`fH%V^Z$_QB6Q{GJjr*dBC%ip|N*w zpL9(6`myi*t&3OQ=xMGFi2p2Izwd8I&*~+j)m`O$EK7t0S6WZvY@d-id(*Qz=eh?X zOn+MwzIs&KvOE7ZT=%&_rpfNnTSmhb7dAaG4BJ0_V#o^RWWBexH4nrzW+;FBKlN7K z$qW83f7>m1T>6JoB>dzz)_~bA6CS3Xj@iW9c-Z>r{N=yC@f}=|Xqojk^j?4IlS%yn z@%#I&tnYr0_H1l;lj*hE{MdtadXkqJ%4ZaEbiBCdbEiT5c8bq|hX0EuKIB<-f2Pm! zMa8WuF}pK7^iqEdv|6gPvizJHU^$~V^x&>5_GUch!6LEEm)vfxX34noSZLjxN2@%S zY~i}LJa2Z{KbG#_VTXcpYubyW%>1vlmETRj*%G>bbEwyWvu_m2UH{##4Re`dVcS(S z>EFlyXZ{PV;CPk2xb;ra@n4QvyC)QHPPPp2Oh~`|ROF%Ln!l6I#r@dHVQ$Pa=eF+l zlaiO5(l6f^J9ziE=%krWzF+sGH!&a=B6(@~3=E8RNd-BX#U%y?R~VU?SyAFR!X{kl2dC93EnaTNiATy<9 z#1<@QV7nx*pr{lDF$ttG0>Vr!j`0h2$xJTE%+E_Ks$8U~9N`!25B30upp=THk-4W& xU~qI|MnQE`YkOx;--JoiH*DRrci(}d7j8ZS6_%iKkP#GO3=9k^P#Q*k0sx{jdu#vz literal 0 HcmV?d00001 diff --git a/distribution/macos/bundle_fix_up.py b/distribution/macos/bundle_fix_up.py new file mode 100644 index 000000000..a8e3ac760 --- /dev/null +++ b/distribution/macos/bundle_fix_up.py @@ -0,0 +1,609 @@ +import argparse +import hashlib +import os +from pathlib import Path +import platform +import shutil +import struct +import subprocess +from typing import List, Optional, Tuple + +parser = argparse.ArgumentParser(description="Fixup for MacOS application bundle") +parser.add_argument("input_directory", help="Input directory (Application path)") +parser.add_argument("executable_sub_path", help="Main executable sub path") + +# Use Apple LLVM on Darwin, otherwise standard LLVM. +if platform.system() == "Darwin": + OTOOL = "otool" + INSTALL_NAME_TOOL = "install_name_tool" +else: + OTOOL = shutil.which("llvm-otool") + if OTOOL is None: + for llvm_ver in [15, 14, 13]: + otool_path = shutil.which(f"llvm-otool-{llvm_ver}") + if otool_path is not None: + OTOOL = otool_path + INSTALL_NAME_TOOL = shutil.which(f"llvm-install-name-tool-{llvm_ver}") + break + else: + INSTALL_NAME_TOOL = shutil.which("llvm-install-name-tool") + + +args = parser.parse_args() + + +def get_dylib_id(dylib_path: Path) -> str: + res = subprocess.check_output([OTOOL, "-D", str(dylib_path.absolute())]).decode( + "utf-8" + ) + + return res.split("\n")[1] + + +def get_dylib_dependencies(dylib_path: Path) -> List[str]: + output = ( + subprocess.check_output([OTOOL, "-L", str(dylib_path.absolute())]) + .decode("utf-8") + .split("\n")[1:] + ) + + res = [] + + for line in output: + line = line.strip() + index = line.find(" (compatibility version ") + if index == -1: + continue + + line = line[:index] + + res.append(line) + + return res + + +def replace_dylib_id(dylib_path: Path, new_id: str): + subprocess.check_call( + [INSTALL_NAME_TOOL, "-id", new_id, str(dylib_path.absolute())] + ) + + +def change_dylib_link(dylib_path: Path, old: str, new: str): + subprocess.check_call( + [INSTALL_NAME_TOOL, "-change", old, new, str(dylib_path.absolute())] + ) + + +def add_dylib_rpath(dylib_path: Path, rpath: str): + subprocess.check_call( + [INSTALL_NAME_TOOL, "-add_rpath", rpath, str(dylib_path.absolute())] + ) + + +def fixup_dylib( + dylib_path: Path, + replacement_path: str, + search_path: List[str], + content_directory: Path, +): + dylib_id = get_dylib_id(dylib_path) + new_dylib_id = replacement_path + "/" + os.path.basename(dylib_id) + replace_dylib_id(dylib_path, new_dylib_id) + + dylib_dependencies = get_dylib_dependencies(dylib_path) + dylib_new_mapping = {} + + for dylib_dependency in dylib_dependencies: + if ( + not dylib_dependency.startswith("@executable_path") + and not dylib_dependency.startswith("/usr/lib") + and not dylib_dependency.startswith("/System/Library") + ): + dylib_dependency_name = os.path.basename(dylib_dependency) + library_found = False + for library_base_path in search_path: + lib_path = Path(os.path.join(library_base_path, dylib_dependency_name)) + + if lib_path.exists(): + target_replacement_path = get_path_related_to_target_exec( + content_directory, lib_path + ) + + dylib_new_mapping[dylib_dependency] = ( + target_replacement_path + + "/" + + os.path.basename(dylib_dependency) + ) + library_found = True + + if not library_found: + raise Exception( + f"{dylib_id}: Cannot find dependency {dylib_dependency_name} for fixup" + ) + + for key in dylib_new_mapping: + change_dylib_link(dylib_path, key, dylib_new_mapping[key]) + + +FILE_TYPE_ASSEMBLY = 1 + +ALIGN_REQUIREMENTS = 4096 + + +def parse_embedded_string(data: bytes) -> Tuple[bytes, str]: + first_byte = data[0] + + if (first_byte & 0x80) == 0: + size = first_byte + data = data[1:] + else: + second_byte = data[1] + + assert (second_byte & 0x80) == 0 + + size = (second_byte << 7) | (first_byte & 0x7F) + + data = data[2:] + + res = data[:size].decode("utf-8") + data = data[size:] + + return (data, res) + + +def write_embedded_string(file, string: str): + raw_str = string.encode("utf-8") + raw_str_len = len(raw_str) + + assert raw_str_len < 0x7FFF + + if raw_str_len > 0x7F: + file.write(struct.pack("b", raw_str_len & 0x7F | 0x80)) + file.write(struct.pack("b", raw_str_len >> 7)) + else: + file.write(struct.pack("b", raw_str_len)) + + file.write(raw_str) + + +class BundleFileEntry(object): + offset: int + size: int + compressed_size: int + file_type: int + relative_path: str + data: bytes + + def __init__( + self, + offset: int, + size: int, + compressed_size: int, + file_type: int, + relative_path: str, + data: bytes, + ) -> None: + self.offset = offset + self.size = size + self.compressed_size = compressed_size + self.file_type = file_type + self.relative_path = relative_path + self.data = data + + def write(self, file): + self.offset = file.tell() + + if ( + self.file_type == FILE_TYPE_ASSEMBLY + and (self.offset % ALIGN_REQUIREMENTS) != 0 + ): + padding_size = ALIGN_REQUIREMENTS - (self.offset % ALIGN_REQUIREMENTS) + file.write(b"\0" * padding_size) + self.offset += padding_size + + file.write(self.data) + + def write_header(self, file): + file.write( + struct.pack( + "QQQb", self.offset, self.size, self.compressed_size, self.file_type + ) + ) + write_embedded_string(file, self.relative_path) + + +class BundleManifest(object): + major: int + minor: int + bundle_id: str + deps_json: BundleFileEntry + runtimeconfig_json: BundleFileEntry + flags: int + files: List[BundleFileEntry] + + def __init__( + self, + major: int, + minor: int, + bundle_id: str, + deps_json: BundleFileEntry, + runtimeconfig_json: BundleFileEntry, + flags: int, + files: List[BundleFileEntry], + ) -> None: + self.major = major + self.minor = minor + self.bundle_id = bundle_id + self.deps_json = deps_json + self.runtimeconfig_json = runtimeconfig_json + self.flags = flags + self.files = files + + def write(self, file) -> int: + for bundle_file in self.files: + bundle_file.write(file) + + bundle_header_offset = file.tell() + file.write(struct.pack("iiI", self.major, self.minor, len(self.files))) + write_embedded_string(file, self.bundle_id) + + if self.deps_json is not None: + deps_json_location_offset = self.deps_json.offset + deps_json_location_size = self.deps_json.size + else: + deps_json_location_offset = 0 + deps_json_location_size = 0 + + if self.runtimeconfig_json is not None: + runtimeconfig_json_location_offset = self.runtimeconfig_json.offset + runtimeconfig_json_location_size = self.runtimeconfig_json.size + else: + runtimeconfig_json_location_offset = 0 + runtimeconfig_json_location_size = 0 + + file.write( + struct.pack("qq", deps_json_location_offset, deps_json_location_size) + ) + file.write( + struct.pack( + "qq", + runtimeconfig_json_location_offset, + runtimeconfig_json_location_size, + ) + ) + file.write(struct.pack("q", self.flags)) + + for bundle_file in self.files: + bundle_file.write_header(file) + + return bundle_header_offset + + +def read_file_entry( + raw_data: bytes, header_bytes: bytes +) -> Tuple[bytes, BundleFileEntry]: + ( + offset, + size, + compressed_size, + file_type, + ) = struct.unpack("QQQb", header_bytes[:0x19]) + (header_bytes, relative_path) = parse_embedded_string(header_bytes[0x19:]) + + target_size = compressed_size + + if target_size == 0: + target_size = size + + return ( + header_bytes, + BundleFileEntry( + offset, + size, + compressed_size, + file_type, + relative_path, + raw_data[offset : offset + target_size], + ), + ) + + +def get_dotnet_bundle_data(data: bytes) -> Optional[Tuple[int, int, BundleManifest]]: + offset = data.find(hashlib.sha256(b".net core bundle\n").digest()) + + if offset == -1: + return None + + raw_header_offset = data[offset - 8 : offset] + (header_offset,) = struct.unpack("q", raw_header_offset) + header_bytes = data[header_offset:] + + ( + major, + minor, + files_count, + ) = struct.unpack("iiI", header_bytes[:0xC]) + header_bytes = header_bytes[0xC:] + + (header_bytes, bundle_id) = parse_embedded_string(header_bytes) + + # v2 header + ( + deps_json_location_offset, + deps_json_location_size, + ) = struct.unpack("qq", header_bytes[:0x10]) + ( + runtimeconfig_json_location_offset, + runtimeconfig_json_location_size, + ) = struct.unpack("qq", header_bytes[0x10:0x20]) + (flags,) = struct.unpack("q", header_bytes[0x20:0x28]) + header_bytes = header_bytes[0x28:] + + files = [] + + deps_json = None + runtimeconfig_json = None + + for _ in range(files_count): + (header_bytes, file_entry) = read_file_entry(data, header_bytes) + + files.append(file_entry) + + if file_entry.offset == deps_json_location_offset: + deps_json = file_entry + elif file_entry.offset == runtimeconfig_json_location_offset: + runtimeconfig_json = file_entry + + file_entry = files[0] + + return ( + file_entry.offset, + header_offset, + BundleManifest( + major, minor, bundle_id, deps_json, runtimeconfig_json, flags, files + ), + ) + + +LC_SYMTAB = 0x2 +LC_SEGMENT_64 = 0x19 +LC_CODE_SIGNATURE = 0x1D + + +def fixup_linkedit(file, data: bytes, new_size: int): + offset = 0 + + ( + macho_magic, + macho_cputype, + macho_cpusubtype, + macho_filetype, + macho_ncmds, + macho_sizeofcmds, + macho_flags, + macho_reserved, + ) = struct.unpack("IiiIIIII", data[offset : offset + 0x20]) + + offset += 0x20 + + linkedit_offset = None + symtab_offset = None + codesign_offset = None + + for _ in range(macho_ncmds): + (cmd, cmdsize) = struct.unpack("II", data[offset : offset + 8]) + + if cmd == LC_SEGMENT_64: + ( + cmd, + cmdsize, + segname_raw, + vmaddr, + vmsize, + fileoff, + filesize, + maxprot, + initprot, + nsects, + flags, + ) = struct.unpack("II16sQQQQiiII", data[offset : offset + 72]) + segname = segname_raw.decode("utf-8").split("\0")[0] + + if segname == "__LINKEDIT": + linkedit_offset = offset + elif cmd == LC_SYMTAB: + symtab_offset = offset + elif cmd == LC_CODE_SIGNATURE: + codesign_offset = offset + + offset += cmdsize + pass + + assert linkedit_offset is not None and symtab_offset is not None + + # If there is a codesign section, clean it up. + if codesign_offset is not None: + ( + codesign_cmd, + codesign_cmdsize, + codesign_dataoff, + codesign_datasize, + ) = struct.unpack("IIII", data[codesign_offset : codesign_offset + 16]) + file.seek(codesign_offset) + file.write(b"\0" * codesign_cmdsize) + + macho_ncmds -= 1 + macho_sizeofcmds -= codesign_cmdsize + file.seek(0) + file.write( + struct.pack( + "IiiIIIII", + macho_magic, + macho_cputype, + macho_cpusubtype, + macho_filetype, + macho_ncmds, + macho_sizeofcmds, + macho_flags, + macho_reserved, + ) + ) + + file.seek(codesign_dataoff) + file.write(b"\0" * codesign_datasize) + + ( + symtab_cmd, + symtab_cmdsize, + symtab_symoff, + symtab_nsyms, + symtab_stroff, + symtab_strsize, + ) = struct.unpack("IIIIII", data[symtab_offset : symtab_offset + 24]) + + symtab_strsize = new_size - symtab_stroff + + new_symtab = struct.pack( + "IIIIII", + symtab_cmd, + symtab_cmdsize, + symtab_symoff, + symtab_nsyms, + symtab_stroff, + symtab_strsize, + ) + + file.seek(symtab_offset) + file.write(new_symtab) + + ( + linkedit_cmd, + linkedit_cmdsize, + linkedit_segname_raw, + linkedit_vmaddr, + linkedit_vmsize, + linkedit_fileoff, + linkedit_filesize, + linkedit_maxprot, + linkedit_initprot, + linkedit_nsects, + linkedit_flags, + ) = struct.unpack("II16sQQQQiiII", data[linkedit_offset : linkedit_offset + 72]) + + linkedit_filesize = new_size - linkedit_fileoff + linkedit_vmsize = linkedit_filesize + + new_linkedit = struct.pack( + "II16sQQQQiiII", + linkedit_cmd, + linkedit_cmdsize, + linkedit_segname_raw, + linkedit_vmaddr, + linkedit_vmsize, + linkedit_fileoff, + linkedit_filesize, + linkedit_maxprot, + linkedit_initprot, + linkedit_nsects, + linkedit_flags, + ) + file.seek(linkedit_offset) + file.write(new_linkedit) + + +def write_bundle_data( + output, + old_bundle_base_offset: int, + new_bundle_base_offset: int, + bundle: BundleManifest, +) -> int: + # Write bundle data + bundle_header_offset = bundle.write(output) + total_size = output.tell() + + # Patch the header position + offset = file_data.find(hashlib.sha256(b".net core bundle\n").digest()) + output.seek(offset - 8) + output.write(struct.pack("q", bundle_header_offset)) + + return total_size - new_bundle_base_offset + + +input_directory: Path = Path(args.input_directory) +content_directory: Path = Path(os.path.join(args.input_directory, "Contents")) +executable_path: Path = Path(os.path.join(content_directory, args.executable_sub_path)) + + +def get_path_related_to_other_path(a: Path, b: Path) -> str: + temp = b + + parts = [] + + while temp != a: + temp = temp.parent + parts.append(temp.name) + + parts.remove(parts[-1]) + parts.reverse() + + return "/".join(parts) + + +def get_path_related_to_target_exec(input_directory: Path, path: Path): + return "@executable_path/../" + get_path_related_to_other_path( + input_directory, path + ) + + +search_path = [ + Path(os.path.join(content_directory, "Frameworks")), + Path(os.path.join(content_directory, "Resources/lib")), +] + + +for path in content_directory.rglob("**/*.dylib"): + current_search_path = [path.parent] + current_search_path.extend(search_path) + + fixup_dylib( + path, + get_path_related_to_target_exec(content_directory, path), + current_search_path, + content_directory, + ) + +for path in content_directory.rglob("**/*.so"): + current_search_path = [path.parent] + current_search_path.extend(search_path) + + fixup_dylib( + path, + get_path_related_to_target_exec(content_directory, path), + current_search_path, + content_directory, + ) + + +with open(executable_path, "rb") as input: + file_data = input.read() + + +(bundle_base_offset, bundle_header_offset, bundle) = get_dotnet_bundle_data(file_data) + +add_dylib_rpath(executable_path, "@executable_path/../Frameworks/") + +# Recent "vanilla" version of LLVM (LLVM 13 and upper) seems to really dislike how .NET package its assemblies. +# As a result, after execution of install_name_tool it will have "fixed" the symtab resulting in a missing .NET bundle... +# To mitigate that, we check if the bundle offset inside the binary is valid after install_name_tool and readd .NET bundle if not. +output_file_size = os.stat(executable_path).st_size +if output_file_size < bundle_header_offset: + print("LLVM broke the .NET bundle, readding bundle data...") + with open(executable_path, "r+b") as output: + file_data = output.read() + bundle_data_size = write_bundle_data( + output, bundle_base_offset, output_file_size, bundle + ) + + # Now patch the __LINKEDIT section + new_size = output_file_size + bundle_data_size + fixup_linkedit(output, file_data, new_size) diff --git a/distribution/macos/construct_universal_dylib.py b/distribution/macos/construct_universal_dylib.py new file mode 100644 index 000000000..b6c3770c6 --- /dev/null +++ b/distribution/macos/construct_universal_dylib.py @@ -0,0 +1,95 @@ +import argparse +import os +from pathlib import Path +import platform +import shutil +import subprocess + +parser = argparse.ArgumentParser( + description="Construct Universal dylibs for nuget package" +) +parser.add_argument( + "arm64_input_directory", help="ARM64 Input directory containing dylibs" +) +parser.add_argument( + "x86_64_input_directory", help="x86_64 Input directory containing dylibs" +) +parser.add_argument("output_directory", help="Output directory") +parser.add_argument("rglob", help="rglob") + +args = parser.parse_args() + +# Use Apple LLVM on Darwin, otherwise standard LLVM. +if platform.system() == "Darwin": + LIPO = "lipo" +else: + LIPO = shutil.which("llvm-lipo") + + if LIPO is None: + for llvm_ver in [15, 14, 13]: + lipo_path = shutil.which(f"llvm-lipo-{llvm_ver}") + if lipo_path is not None: + LIPO = lipo_path + break + +if LIPO is None: + raise Exception("Cannot find a valid location for LLVM lipo!") + +arm64_input_directory: Path = Path(args.arm64_input_directory) +x86_64_input_directory: Path = Path(args.x86_64_input_directory) +output_directory: Path = Path(args.output_directory) +rglob = args.rglob + + +def get_new_name( + input_directory: Path, output_directory: str, input_dylib_path: Path +) -> Path: + input_component = str(input_dylib_path).replace(str(input_directory), "")[1:] + return Path(os.path.join(output_directory, input_component)) + + +def is_fat_file(dylib_path: Path) -> str: + res = subprocess.check_output([LIPO, "-info", str(dylib_path.absolute())]).decode( + "utf-8" + ) + + return not res.split("\n")[0].startswith("Non-fat file") + + +def construct_universal_dylib( + arm64_input_dylib_path: Path, x86_64_input_dylib_path: Path, output_dylib_path: Path +): + if output_dylib_path.exists() or output_dylib_path.is_symlink(): + os.remove(output_dylib_path) + + os.makedirs(output_dylib_path.parent, exist_ok=True) + + if arm64_input_dylib_path.is_symlink(): + os.symlink( + os.path.basename(arm64_input_dylib_path.resolve()), output_dylib_path + ) + else: + if is_fat_file(arm64_input_dylib_path) or not x86_64_input_dylib_path.exists(): + with open(output_dylib_path, "wb") as dst: + with open(arm64_input_dylib_path, "rb") as src: + dst.write(src.read()) + else: + subprocess.check_call( + [ + LIPO, + str(arm64_input_dylib_path.absolute()), + str(x86_64_input_dylib_path.absolute()), + "-output", + str(output_dylib_path.absolute()), + "-create", + ] + ) + + +print(rglob) +for path in arm64_input_directory.rglob("**/*.dylib"): + construct_universal_dylib( + path, + get_new_name(arm64_input_directory, x86_64_input_directory, path), + get_new_name(arm64_input_directory, output_directory, path), + ) diff --git a/distribution/macos/create_app_bundle.sh b/distribution/macos/create_app_bundle.sh new file mode 100755 index 000000000..8076303cb --- /dev/null +++ b/distribution/macos/create_app_bundle.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +set -e + +PUBLISH_DIRECTORY=$1 +OUTPUT_DIRECTORY=$2 +ENTITLEMENTS_FILE_PATH=$3 + +APP_BUNDLE_DIRECTORY=$OUTPUT_DIRECTORY/Ryujinx.app + +rm -rf $APP_BUNDLE_DIRECTORY +mkdir -p $APP_BUNDLE_DIRECTORY/Contents +mkdir $APP_BUNDLE_DIRECTORY/Contents/Frameworks +mkdir $APP_BUNDLE_DIRECTORY/Contents/MacOS +mkdir $APP_BUNDLE_DIRECTORY/Contents/Resources + +# Copy executables first +cp $PUBLISH_DIRECTORY/Ryujinx.Ava $APP_BUNDLE_DIRECTORY/Contents/MacOS/Ryujinx +chmod u+x $APP_BUNDLE_DIRECTORY/Contents/MacOS/Ryujinx + +# Then all libraries +cp $PUBLISH_DIRECTORY/*.dylib $APP_BUNDLE_DIRECTORY/Contents/Frameworks + +# Then resources +cp Info.plist $APP_BUNDLE_DIRECTORY/Contents +cp Ryujinx.icns $APP_BUNDLE_DIRECTORY/Contents/Resources/Ryujinx.icns +cp -r $PUBLISH_DIRECTORY/THIRDPARTY.md $APP_BUNDLE_DIRECTORY/Contents/Resources + +echo -n "APPL????" > $APP_BUNDLE_DIRECTORY/Contents/PkgInfo + +# Fixup libraries and executable +python3 bundle_fix_up.py $APP_BUNDLE_DIRECTORY MacOS/Ryujinx + +# Now sign it +if ! [ -x "$(command -v codesign)" ]; +then + if ! [ -x "$(command -v rcodesign)" ]; + then + echo "Cannot find rcodesign on your system, please install rcodesign." + exit 1 + fi + + # NOTE: Currently require https://github.com/indygreg/apple-platform-rs/pull/44 to work on other OSes. + # cargo install --git "https://github.com/marysaka/apple-platform-rs" --branch "fix/adhoc-app-bundle" apple-codesign --bin "rcodesign" + echo "Usign rcodesign for ad-hoc signing" + rcodesign sign --entitlements-xml-path $ENTITLEMENTS_FILE_PATH $APP_BUNDLE_DIRECTORY +else + echo "Usign codesign for ad-hoc signing" + codesign --entitlements $ENTITLEMENTS_FILE_PATH -f --deep -s - $APP_BUNDLE_DIRECTORY +fi + diff --git a/distribution/macos/create_macos_release.sh b/distribution/macos/create_macos_release.sh new file mode 100755 index 000000000..545baf20e --- /dev/null +++ b/distribution/macos/create_macos_release.sh @@ -0,0 +1,105 @@ +#!/bin/bash + +set -e + +if [ "$#" -ne 6 ]; then + echo "usage " + exit 1 +fi + +mkdir -p $1 +mkdir -p $2 +mkdir -p $3 + +BASE_DIR=$(readlink -f $1) +TEMP_DIRECTORY=$(readlink -f $2) +OUTPUT_DIRECTORY=$(readlink -f $3) +ENTITLEMENTS_FILE_PATH=$(readlink -f $4) +VERSION=$5 +SOURCE_REVISION_ID=$6 + +RELEASE_TAR_FILE_NAME=Ryujinx-$VERSION-macos_universal.app.tar +ARM64_APP_BUNDLE=$TEMP_DIRECTORY/output_arm64/Ryujinx.app +X64_APP_BUNDLE=$TEMP_DIRECTORY/output_x64/Ryujinx.app +UNIVERSAL_APP_BUNDLE=$OUTPUT_DIRECTORY/Ryujinx.app +EXECUTABLE_SUB_PATH=Contents/MacOS/Ryujinx + +rm -rf $TEMP_DIRECTORY +mkdir -p $TEMP_DIRECTORY + +DOTNET_COMMON_ARGS="-p:DebugType=embedded -p:Version=$VERSION -p:SourceRevisionId=$SOURCE_REVISION_ID -p:ExtraDefineConstants=DISABLE_UPDATER --self-contained true" + +dotnet restore +dotnet build -c Release Ryujinx.Ava +dotnet publish -c Release -r osx-arm64 -o $TEMP_DIRECTORY/publish_arm64 $DOTNET_COMMON_ARGS Ryujinx.Ava +dotnet publish -c Release -r osx-x64 -o $TEMP_DIRECTORY/publish_x64 $DOTNET_COMMON_ARGS Ryujinx.Ava + +# Get ride of the support library for ARMeilleur for x64 (that's only for arm64) +rm -rf $TEMP_DIRECTORY/publish_x64/libarmeilleure-jitsupport.dylib + +# Get ride of libsoundio from arm64 builds as we don't have a arm64 variant +# TODO: remove this once done +rm -rf $TEMP_DIRECTORY/publish_arm64/libsoundio.dylib + +pushd $BASE_DIR/distribution/macos +./create_app_bundle.sh $TEMP_DIRECTORY/publish_x64 $TEMP_DIRECTORY/output_x64 $ENTITLEMENTS_FILE_PATH +./create_app_bundle.sh $TEMP_DIRECTORY/publish_arm64 $TEMP_DIRECTORY/output_arm64 $ENTITLEMENTS_FILE_PATH +popd + +rm -rf $UNIVERSAL_APP_BUNDLE +mkdir -p $OUTPUT_DIRECTORY + +# Let's copy one of the two different app bundle and remove the executable +cp -R $ARM64_APP_BUNDLE $UNIVERSAL_APP_BUNDLE +rm $UNIVERSAL_APP_BUNDLE/$EXECUTABLE_SUB_PATH + +# Make it libraries universal +python3 $BASE_DIR/distribution/macos/construct_universal_dylib.py $ARM64_APP_BUNDLE $X64_APP_BUNDLE $UNIVERSAL_APP_BUNDLE "**/*.dylib" + +if ! [ -x "$(command -v lipo)" ]; +then + if ! [ -x "$(command -v llvm-lipo-14)" ]; + then + LIPO=llvm-lipo + else + LIPO=llvm-lipo-14 + fi +else + LIPO=lipo +fi + +# Make it the executable universal +$LIPO $ARM64_APP_BUNDLE/$EXECUTABLE_SUB_PATH $X64_APP_BUNDLE/$EXECUTABLE_SUB_PATH -output $UNIVERSAL_APP_BUNDLE/$EXECUTABLE_SUB_PATH -create + +# Patch up the Info.plist to have appropriate version +sed -r -i.bck "s/\%\%RYUJINX_BUILD_VERSION\%\%/$VERSION/g;" $UNIVERSAL_APP_BUNDLE/Contents/Info.plist +sed -r -i.bck "s/\%\%RYUJINX_BUILD_GIT_HASH\%\%/$SOURCE_REVISION_ID/g;" $UNIVERSAL_APP_BUNDLE/Contents/Info.plist +rm $UNIVERSAL_APP_BUNDLE/Contents/Info.plist.bck + +# Now sign it +if ! [ -x "$(command -v codesign)" ]; +then + if ! [ -x "$(command -v rcodesign)" ]; + then + echo "Cannot find rcodesign on your system, please install rcodesign." + exit 1 + fi + + # NOTE: Currently require https://github.com/indygreg/apple-platform-rs/pull/44 to work on other OSes. + # cargo install --git "https://github.com/marysaka/apple-platform-rs" --branch "fix/adhoc-app-bundle" apple-codesign --bin "rcodesign" + echo "Usign rcodesign for ad-hoc signing" + rcodesign sign --entitlements-xml-path $ENTITLEMENTS_FILE_PATH $UNIVERSAL_APP_BUNDLE +else + echo "Usign codesign for ad-hoc signing" + codesign --entitlements $ENTITLEMENTS_FILE_PATH -f --deep -s - $UNIVERSAL_APP_BUNDLE +fi + +echo "Creating archive" +pushd $OUTPUT_DIRECTORY +tar --exclude "Ryujinx.app/Contents/MacOS/Ryujinx" -cvf $RELEASE_TAR_FILE_NAME Ryujinx.app 1> /dev/null +python3 $BASE_DIR/distribution/misc/add_tar_exec.py $RELEASE_TAR_FILE_NAME "Ryujinx.app/Contents/MacOS/Ryujinx" "Ryujinx.app/Contents/MacOS/Ryujinx" +gzip -9 < $RELEASE_TAR_FILE_NAME > $RELEASE_TAR_FILE_NAME.gz +rm $RELEASE_TAR_FILE_NAME +popd + +echo "Done" diff --git a/distribution/macos/entitlements.xml b/distribution/macos/entitlements.xml new file mode 100644 index 000000000..bf3185071 --- /dev/null +++ b/distribution/macos/entitlements.xml @@ -0,0 +1,23 @@ + + + + + com.apple.security.cs.allow-jit + + com.apple.security.cs.allow-unsigned-executable-memory + + com.apple.security.cs.allow-dyld-environment-variables + + com.apple.security.cs.disable-library-validation + + com.apple.security.cs.disable-executable-page-protection + + com.apple.security.cs.debugger + + com.apple.security.get-task-allow + + com.apple.security.hypervisor + + + diff --git a/distribution/misc/add_tar_exec.py b/distribution/misc/add_tar_exec.py new file mode 100644 index 000000000..fe659c161 --- /dev/null +++ b/distribution/misc/add_tar_exec.py @@ -0,0 +1,24 @@ +import argparse +from io import BytesIO +import tarfile + +parser = argparse.ArgumentParser( + description="Add the main binary to a tar and force it to be executable" +) +parser.add_argument("input_tar_file", help="input tar file") +parser.add_argument("main_binary_path", help="Main executable path") +parser.add_argument("main_binary_tar_path", help="Main executable tar path") + +args = parser.parse_args() +input_tar_file = args.input_tar_file +main_binary_path = args.main_binary_path +main_binary_tar_path = args.main_binary_tar_path + +with open(main_binary_path, "rb") as f: + with tarfile.open(input_tar_file, "a") as tar: + data = f.read() + tar_info = tarfile.TarInfo(main_binary_tar_path) + tar_info.mode = 0o755 + tar_info.size = len(data) + + tar.addfile(tar_info, BytesIO(data))