From d0396cf19d2ce35628face5a748aa79a7678d75d Mon Sep 17 00:00:00 2001 From: Crypto137 Date: Sun, 6 Aug 2023 18:01:14 +0300 Subject: [PATCH] Implement GPAK data loading --- .../K4os.Compression.LZ4.License | 21 + .../K4os.Compression.LZ4.dll | Bin 0 -> 67072 bytes .../K4os.Compression.LZ4.xml | 1211 +++++++++++++++++ docs/Setup.md | 2 + src/MHServerEmu/Assets/GPAK/IMPORTANT | 1 + src/MHServerEmu/GameServer/Data/Database.cs | 46 +- .../GameServer/Data/Gpak/GpakFile.cs | 51 +- src/MHServerEmu/MHServerEmu.csproj | 6 + src/MHServerEmu/Program.cs | 4 + 9 files changed, 1332 insertions(+), 10 deletions(-) create mode 100644 dep/K4os.Compression.LZ4/K4os.Compression.LZ4.License create mode 100644 dep/K4os.Compression.LZ4/K4os.Compression.LZ4.dll create mode 100644 dep/K4os.Compression.LZ4/K4os.Compression.LZ4.xml create mode 100644 src/MHServerEmu/Assets/GPAK/IMPORTANT diff --git a/dep/K4os.Compression.LZ4/K4os.Compression.LZ4.License b/dep/K4os.Compression.LZ4/K4os.Compression.LZ4.License new file mode 100644 index 00000000..f691b2ae --- /dev/null +++ b/dep/K4os.Compression.LZ4/K4os.Compression.LZ4.License @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Milosz Krajewski + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/dep/K4os.Compression.LZ4/K4os.Compression.LZ4.dll b/dep/K4os.Compression.LZ4/K4os.Compression.LZ4.dll new file mode 100644 index 0000000000000000000000000000000000000000..9b8a0ae6456ebd9734de0d4e0c5822abd9e1e737 GIT binary patch literal 67072 zcmeFa34EMY)jxipWu9l|nI-dNl9}u?-KUu(&DJC>2u+i8Oud=wHqM)L(Dk86-BD;vbNTcG83i^tOTK?a2?wuvwfLGuD=k+Iroadf% z&pr3td(S=hKKD7#Jbfp8Ko~*@6MwJ2F2tR<(!T`^4-a-BI~2b=B<}Eicgmew?{}y4 zZyes1*g7_P>e#w1i4E&UMn<P-aFpZ~2zKIotMi-vZsy@*)p z|NK>-i7}XLac2V+cvfVMAi*D%7)@v@NWTzt6^+t^C%ceR{@==4}^ID-$KeR{C5fU zEB=vgy6}kB8-=*#93gy6&4cGEG_fm>9x_#`)#~-(F%Y+o6#;B3LtW<@(Pr0-HU|we zt-pVGpB~+(rL<`M(ZwP@y)0P{mSQnh5B6jQr;Q{8q(D0(Pcs!QSp}AD)g+eMP=iEm zMe+vNA)z;_>& z<=RmW=H?cOWE^!Mlgbxq$pkPzs8etacqc%aid4jO&55Apv(2PGSNCy{0lRl~!;B`O zL<-f0JTj@z@Yi|l_Yc>5q7zQaXGbri{-1C>GlF# z{TKA*`p@HfCL(5$&ixv(U3(&8S*~fjmNyAY!f2AsnLNk;`F$!|K>EWWkxM)1n1!s6 z9rDI_DBtS%g{&D7Po3p@Ftk<_O-=b-j~d*FnIB!(4B6?BYV$HZiSbA!r^CVwnzvT% zDi~7|9UzGwNnD{D+h&4?8BIw;J1Q|pGq~D?<56#&2ZJ?htpwR-V-;T}_7(A!rTBHj zPQtFN+G5>`CI^txjbc4{5)w=gKJNuO2=qB0jf)2Vt&5|CyHdJT>c>5x!@uU{4=ghY z-)H&lXp?DM%?(kDrm|(FrQ_j-2E3qRN?XAqQLOutv#3o;fZC{KCo$YB(-P_|X1N9`o0k`u9u?P8B>Ng+_vsU?4K9rW@+FJlj^w6fkm24A{$8@z;bZf>f> zi$RY{$nzIzww2x&CEn3GFa`8t-L|*_liL&Yp*=pUxh|_Zj2ODRW?uOs4fGfu; zFq8Z=Jgh1oj2UL;AQEV9X`)o|8TCG%rxpYm*;<;5L^Rm|?W3z=P>kkcR<2PGpGqzj zK5@>?ac-_ZJKx;?HtY~yF zKnX}P=U{D!yRq!b@$SQ}64}a%v(@TXb?E{%%RWkAlO>uQ zJwdyzMl0pjqjS6!PRc5CQhLlGM-*w)c^&8eu4pmKk)tDr+DhvU=?o1ka~ggouc3ax zI##==V3sE#-m1lXE|}z!wybcDOA^(06zao@lbvVT*{vs=APDO!4H+g~mTvQG3lt08 zo5TjrAz*n`f1eCln4^c88??SpmW&BfFg=3BhMIo4Dy%#Q+x}GDNW#IAag)u#w~HX^ zoGg~sa;(5?+lIhQWF9p^3zdK{Y5}d;hWH-%uoSsPnx32sDrxFCy6gfhEeW6juidb` z*o?QSjfXk*)7;>gUO71CSWwGuMoc?PJ2p?_O-Qm8Rh+9QJ8-G@6$?{(D$7j59a=%3 ztY=QHo;CUMIhoj8sZEdR%hvOuJguM}bQiag)>rfy#ygs9Mtc@l4>+h^C&con+Kcsg zg|a(+?5>$Wf-{kcV`sd|WZ73fgCtKd)sfbu9)Ad9w*K%T3`U%RTrjNPvc`HTcA1igF&w^9l?Tc`l9=cZSz3P zwmE|~yZArqU?&+e;0X*>b5p8Vi^o)@+SoKb+nt))Luv(;q6}@?u<3wtPIwKf*ok7- zR=a3yHA?CA$kHW|u$EXWi@JY0)`-?R)C^!>r%;%$~dRyly zTMJ7+g8F18w$PkYdz_wp2Pky`DJwdPK67eo_f=uNudLZ;)}l;0mN8B=PCzMm6l$+b zK{EWH8EwRTMn)$IZZ>ptlevj%acDltxrv5gZXO95ITO@J5~4xvNZKzmUR~+CKv;Y!^gC zCjzx0eKEEn#;R=p!9USD1^+ssm$BkjZG#TyE)n*@#1mCK6@@$!$?;%aWjQs4JQAsm z>c-+#)LBX{QmQmkiYI&j(U8QBBDD)kh*?{49@aFnn_cR<1M9U}i=Etw)HOS>W}Cdr zBGck5Ij`ZvX)h0bFwg^FwQ4_sdlvuLyI`5y&>OgoN@5~dO+M=Ibx85ht*69%eP0#z zpe%{@=>n!q>$7Xt!_cXdQ;J(rXmMxewsmJ9pH3ja* zT-1fZx5`Ndn*hevq0x8zx-BJG<0K+m_#?87Q7oa{zUV{?jEs{qhpl5~?H6Rtq?1$U z)ssV{V>nx;0akPhD0=c#z}jyXFxML;;A6FuOHp_`%%)P|%Sbs`omIPDR<{O*h+rQ+pN2+elWHSt5xvnBsuM^&AX2c{~XT=}T#xtnT~+EbFX-;b74Xq*^Gd&RK7_7{?@to9N) zg!VQ2s^(w|Si8xH%`xM?ux7*qO=diRMj9K(KSuMSK{sFq;~&q?5ljWzo4C3In`$wl z6SamEGKbPu=~Lq$0V~|t_(#?ClYC_fR&@LmgvEWy_d-bv-cuG%?x8llqVJ&WI;`xM z0-u?huJ0siGy3!$BrOv=anrlgVeWAJXLl=n$|8d zmE>i_LHqYhpg-=xTz%(%pzO~3045xL=kGqH=Gs-i`4?-?5${N3kcngcL&f}B^GWF8<4U{&N zsEq76g9j-qgw)7(X6-YDeGyI24Fa!wGYdP6?-vU4&l2{({}VBu!rO0CZJ8}dXA+oQ#`THI<& z^&Hhc>^HfdCYvu|-K>DE;$k-$b{JK1IWuF@5@8ie;m*V_Uo zkWLnximbae08uU}W7$n19f|Z}gJHw3)qTd%C<_%a@39b7WO!DYEq)}!SiLLKt-mhPC(6_Ql&hDf z7u$zpYOrP#EY7X ztQamCwz(72X-CfEDUhKtAKrC!Kyef zMK**T3tyKdZ-9h)AMM+qPOLVHtteV6&T9umGzg{+5B z-T=p$(&ItgMoT}Guy`QkecH5x@Wsp4Pr2v0dn+G9-U;yqU_W|ba7*nU%vh$!08PUm z_RTb>M6IEZ?5py@`I66xPV6ND`=4k(CCiw+y7qieS%b$@-VmrY4Vka^Q;HeGcG6S~(JU zc^Vsd5Niizw0Y$fzQoSC5o4T7n&ZS_)*gk0ZU!txZUl9#?=#Htab(S)IlYq-SkBJH zH9Ecnkah`hYjl2Rh-0A-{fn}-)?5nRv>A_|4z?t+oLzND)m-`xrjF&P^+=sZSxJX> z>NpcI&m$V0q^EYl7vy|s7i7cM==>(0dp7YH?eVZ1rduF5H;YdG==sFY@^n49O6Frh zb+)Yrkn^`0@?b|Um-E|oVLR4Rh8?^o`62{4sTijp;Ix0tHBix4fY7lX=9DFHr#7j4 z3Cbs^3?JdY%;hR`_(O+~IlPQlCdqh2$=J4^ zj5lh}t6VPMo=26OtM`+0cwHQgdN$jh2_@sZZ%Kw{l8paSGJabiV}(At5lq>kxECxu zW)44Z!LVU1pka#b(?T8s{PZB?OMaa=X>7k~3NfAL7Iu$%X)UIhnf4n2uO}T0d7~2z z-jFxsn@FdSAAum+TGjbOz64q5J`aveA~t4iy-@>K8_JYrB3@q}jTdJkKT!)(IL0G-tQ}2~%3}~IltVfPDBbIg+M*?o3VlWZ}m*WO&_F>zrrF?EM zUbIg`a^6K~9E#YM>m@y6Uf0Jw19c-H`@9z4HKc4L=vKt*1}D-1i=g8M;za|Iphb63 zR>W_Wr5&&9pU6Z|QM~Ag9-4dpn(lYt8nR*yKGzA^-E)9>T_?F+3ady%T*tAmzy3Ov zY3YncLT(TT0ydULObfF(G?Dx~SksZ98!9~_Z9pb^j@9sXFI6b(V|C95$~h5~tSiQT zG#<#zE#q9fCNnn_GUs1__9CMu)3y^`nat+3`DJRsD>~=5HTfc@Pwtf?!PGQ#ANI+< z1>Z+DWMll$1NMT4CHKPnarJ^G_gdULvZq3xc^{BGN4y7D%WjEv;240I&Ey#%upq}Z z0|)Cy*}>d1P0>h^?3v0;EFChV#YOp^DVl#YRg&)*>_&tete{@A>w@Tb*d98+9ORHy zw%`Hk1dK-KT}ZH^zRFBn49fnQ1}&%>p8PBngot3sb{*a|&W(FrPZoPBqZ%xm+VQ@> zzy5kQZ%ZWX2HkKb;-Y{Xp2)aiJ_qt5oesFJH)ONGwi#a6ok&NDLv|#R3VC6(0XG5| zi7Z~_b&K17+tCWKV0T?50PKN|I1#CzG1Q*Av>fWTWA;o8x!{bYT_=`Cq6~>LN|Yl}j)d!+ zN2`$QS2s?m%kfqqGl`wCgRMHX!3q2jclxE4nNDv=f{i$IkQZji+Dg^@RZd_7#Cs~*_teki zg@UWc^U+4#uC;955m|WH$vdJB)FP}Mv^g?cwpu%=7^~Ngs0yl&^z)}WtR1vts*f;H zva!1URCNK#+CjT3$(#I7DyeGS$=k5ycDmo%$s4ggJH0p6hx;qa;N2L~lp_n{_Kzef z(FH=OQ?f$$Yl@Av&m;b13o#RYVBl~LZr$`7_CzM2?>HN2OiyolgIDJbX9`aQIDsWl zQfU{-R7ylFqT<}$M|>nH6Tw6JvIc;1iZh8tya*z|S_qe7(JV8t>Uh$$#KF;HMhD$M zWhN3LF?b|TJkTUnB$G-e)~6y9-YE>zh`3RB9;>Xy##8S#u{o~|sgPaX;&>8% zm~=Cvv6(XsL=2FAIwWVXu9vsadOuF>)IGc-PWknn)RY;^J?VaZC$&K)L-zQYKs@o_ z(ZBJaL-n0Fe=~Ptr(sTn@L1&q&@8Oi*jOZgigY9rvSSP0O_mag#Uf=Pr)qtq+>Jyk z+?eCW+_H2e=9ZHeE3euRDFY?A@PiQSR_rhNGbEzKjpYPe2l9LhJZ=daVcKfH7QS#L zC>Fz(_jZ(HQLrMFu}BrGQ4KX}+)A}L#@(99RVF`8+zGeRaVy=bbfnU)COxa6XB8;H zg}*j2PtkctxCy&8<<_`nL1&a@ix;^G3~`B@aP2gtd4j1{uOu14)NGhVOEr}bx>-hW z6z4f?m2@j}4R;Sz^%kfa^2J(wsK(bl9orC$djllOzHsB+VcMD$_Q!&K`#|y5*$|h= zM2f9QH0974gIf~IL`p2zzt3<>u!$O|^IJGGa7zFho(G*5yvP(cOx+QVlwwoVQs#!; zDHCZp=qY@&cG5)Jb=m%4l?{}ccnL<3&SiqREFDY71L=wkIQ?!IwT3&cGBEa2;zjL8 z5zIuUx}{ujRVt?HV+pI=seB~{nVK`9^4+OVu1aA&bf;9Te{vOShGAVgrbSIJB-5-WAJEc|x!kf6>!9Sp$~N7d z?uOb-Hx!v}VG%9C-}K~9$fTxO?ljsjI+i=VrNy0YxFMM_!=2{NNK^g{Jqh8o8Fz!7=hL(;%S={Ix*9l_ zmUkZDgnmZ7Tyg-Jd{fRlbdxifJRxkNWR2P_jjrnW9(HFrPnNWlr zfeGxUcrer1BS1OvXOkIDEil4J%Ef<$=9-D5*q&0br!E&T6RA^b9el>usTp7A%2aD^ z#@E4Yut}Gel*&!}l-vpXNl{1YD`&nFLUbF=j^Tbc%tvO!axdr?ABU5g1vAi-Uxc+d zw455TFm^MZ`=4kUDrYg#B)k&OPYd$1mOs`<_j0 z+yC6m+JQSblkUeYPx9yB&o)~goW(NbttGb6fQSIz99Z)3%exBh{Mkva%TN^JK^8 z`lc+F8w`uLZ8))5guPH0;ZsTM5^0B{4*I&#%zU}BY`I|S#5}b;PwgpGY5F9UqWJ=4 zS(SpR0!8z}RnJ*+lSP4w+4DmrF)pS*HB(QX4%O^+K_B8QD)3k{6+zhHk3pLNqFe0M zvF$If^*kG|t3P7%8#paf` zUlD&a62z$p9tVC9Z87*kox0JC1Ui0zRf0|ieAor}LjF41_69OF-80E22YlVT(H5^8 zfJ$MUi6Df84{I=~y*OUUq6=OH7jDpL)93mZ{F*Q?1;F?X%yZm2ei#`Mw*t>YXqocx zs+&fwMXrn8v|AK1kNOf7Dncc=+jrgc-jHR`rQ<({ZnFl(ZgIAXcQaM?0CbB)BPE6p ziX&a>mU`L@Po$J;E5=_bSBKTH$Z(5*wnV|v>beqzgQ3jU8*6jjDs0j#BTlX$w!h`HA+B`Gn!o18Vr`|^xCa(a z(WzP&Lm24rA`Q_>Zgt2z?`JfW)v*OXqN_9i86H{i*0{m?4%g2f7gu#NBwk z;lmvZ1tugkQCcHQVshzh=H}C!YPp%;GY4$>xgeAGMYh{iDM+QB`NFmwZ`3f1=$VEorRIF!^o#x7w4)0*HJbF5-_XXc75|Ju(b#|u?0oc@4k6ny5RO! zR?+zvsA}`mUON|0uq9G&=b!#h_8JXtgKMSTdNMa!w`D1}0b@7EoukIMWm7cLWVrQi z15L_ix0xqpGfm1l_-oEi%0@ZuVum}%l!9#akXlZ`Cb!XT%0wF7#*m3A*qG}Kw@FSx zTozFUZ-JIAnMeaV0ha*#HQA*B*EfUQ-TW@PU!!Nx0ghj;L$BZr!Z6Ho57u-# z{E3DT_5L_UAL|5sdO)nh6;F$>=10eGhufv=ZMa4eAc2COVhyh`&~^bU65h_BqBz8F z#LFbuyhXh6;(fX|KAqot8K^Vj#Rxz2DnSOHXb!VDRi7#7@VkNZn$Z`)5#nAi9^RRK zycS`TR2Mb+5MlwHu>He2Rg(@`ZFo*=VQ+T?KqhEs_jEY{%)ZJeH+4aIZT)dB+lM`! zWm~kFGf)%ct6Mm5Ai3A*)BDuJO}YFo!IE z8FWqTLa*)9=^0DV3+DmHfiobAV8IEHh2_+YcyY>Xrm>bzM8f!U@mGw$2>wbu(f$y& z+Lnuh)`-uF2akLaj<@6Y0J_(;io+8jkByD|6XbvVuD@cA55pJe_$C;8HWlkofx!X#byb^j1pItkfD_~+Efb8QXGuA#dIk~1*J6m zs5lxrUFTFejvmm|Kyk@Lab$8+gJ^1i))zO>f?BCxTAgei(D;yzpT8E3rv^>pK>PCD zvlM34F^||Vw*GW^y7Mv^fkfKRRF$>xn7DE;6@#8wm#HLuB=f(GI$dy-U;u*D;~4y641OTubnL@zZlRctv*~C^ws}aknZ{!!mSZ^%Ch~G!HHg3v}p=Bafm|N<3ifw!C(M)`f3ah+np{cjv}xRHkE?rc0A5cPdr^N zHNf<-JHn==oF`=dmNZfkbK&c0%aWZem`-qn9cW`ER&!rl107<~@rF<8gmaUF3UbTc z3gr0Qa(etz&iAf>8SI?snNP%<^>bO2oZ>3CO1e34$rSEgCA(Y2_pS;l=X+N#q{~}Z zFI5xqdekG1eit!Kw6}&6FT+`%9r1Q}FlFFb=nVf8Hr@_FXC;iV5EJ{VTHphIu&>B5 z{C2?hEK3LJF(RG@!c%y#Ux#mT5q&}O;Y4h5B3@*9L%3IifbQchArh|_A^OznSdO&3 zw;QHCeNac#Tsc-2U&u*5NvcmyJ%5jQn}A#u|jLxe+0*L_Mh?1Xe|9JlCZAtgHg zh5aa8cKnY0sm0@X7Hg_0&NM*-|Fp&>vxLd*fnQ<%V9t!t} zSi!5{tSa%Gz^$^}>bxZC{yBYxr0nSCnX*Hhr?6l(v<1{EIe?W=8h2q8M{l5oQ#LqK zQxS=G800krZk%p+R2!#E(w^o{tmUXlG;ZuP6r0wFn`S%$Kq@o21-RRY^Nhg#Q7Rs# zQ{??@A)05vtxm8!-B{*MwcNyHIUy(0coIFxXhD#Lo0wy|2}EENaJg|c$MOZ;DQb=t zkfy1=DIiUieG^CD;LgvjIqJW$n#Cg1;7LHiL?tH%i$u5sukpBtba2n-kIcezfqXA= zLBxF&kfis^6Wp1q4+}`MxDVZAWF}7alkSX0AMQ_+naGUPjnT;L)PUvA-mi|@NhDS- z<<`bp0_5r4*=XhLRFV#lk(lh%Yi&1`$+`9UuhAxFwi|4|4$noqKKqx~U(W`nWRG+U zmhAeNTc398(vgPj(u9R7WMj#;=nl{IV#&tgKP}nVt@6r`CHr84aCeGZ#5%8AhY&%x zj+bn}3~mxBv?yZbmTZN}C3}uqvT-ZPDJ%cM`&JeK_vkaThVtvy7)vFp!!iPl_eo z+Htqcg=hqY*>>`K#N>@O+Ioy%3Gh<s1+`|DeD9i!@tr64f zYdgYl&DJ&qZM7j0uyEdfL$+d$w3oF@37Lv~T>DL=BzJ*~S%=O1fSP?%_F2Z9BJ@a+ zL5Bp%A5u;hjY_mlc37Fj(01Bm;%q{`U&60|;LQPq3F5JV>M_*uUPzMFhCF4mMk(s%vq7AR%M!`G;P>PA1BCqj0DkXOzS|TMrPJi^2W&d;c=V^P z4-F)mzO>UmAznGC4zFn*7~+GdrT^KJw6BCY5*scngNof0HIwH?o%M3pH=J&m<7Dq3`@xK9p7oS@F#$tVOi-#Wx zWFG?H4ak)cY9!$t@LH*^oBUJ{mXV}ziQ0t)gwGIx+XMF^;8ZZLx1-$#Hcqhpx%Ac? z;lL0f1&2cqY;=Ck1K)~BuFir7qu!?~PDn4eT1)n)Mf+2w`_pDDw=(^7GU1h5w6Ew~ z$2ip{D`33MujuCx9VfSRRS%AEerAe`p=TTPHt;OP#^Wi2pX1H6R0st+hB3>(p`QJJJZAYqw2@<$cOxOgQ?XSn zj9JDb&9@x0e32CPZymD?M^PBFd|)@e9kcwe9kYBM2KbOM%QT(dbj&iD%3Fw8X4(72 zEVGDzG-erlRo>SeK4w{R9WG|MP)?LrB&^)<^PqKWpXtKk6A`Y%)w%oslWj-%LNnS&@szA@BY)Ms4<57pCsIenEWbi&@V(iXR{xQo;J$TG=2IJVy=bh$=-?rn4 z^=mnH`F!Z3VwdrgO)_@bkI|VNyIlRR6T3_v=${_DOs5S6vCD*JW0$GEHx;`~%j^Fv zc3D;NZVX{T@ar4IE>m@fiCxZW^LFg=VPlv7g3Uv*K^ZF8?2oUH%(+ zhJRME~gRe_10pS_hGqy!`NknZyzpp8KG@_w)~I9E^8#^e;m8arL*y> zc=IgDN*z3Qc|YEyuJr#mi(SSGEr*C*#up0ZX};In((tmar{IkX(@KWl^WQl@am*)@WzsyOcOK}UoI+?J(&K9hi;l0O3k%eD5UD0DUtK^Brt5? zmwPDDWNkx~HNMzm;+M-Yo>D4*WD;+2%u%`c0uNW570vTy{v_G>LX%&Q&TyE~szr1@ zsh1Tmh2sl7-B1G`p(1H!?a!rJQe!iXpJtCX7kQ%1e$$hd#B6y(3NZ2GJR||1#Fo5s_!srWipV(7(fuC2FGK4eO}u6iAToD#Y-K*&T$Qkd7zPmXSq_G`_OLZ8B7&=@VtDnaS2{BjtNDG*=w-rNK?g zNP}<;3!mALaY|yU)OpdJ^H6=Qk(^H#e8gc}9VKe?ZE?Duuh*3t<(O`GRp=-p;ELaY zK>>V#i>_2+RyOfYO3cKjcH0I@VG<3aXQelkK8ofL^^KfEFbVTOf2iYI#5{)#gI;9x za5v(M$m5vU_{dDmlUCDc6XEbf{_!nH+2flD=PLCblvg;1s4Tv3wSzeI9mGjL4bA<$ zDyI_)S@^wqypP5l`VK0E0jENEjgjXDKBUOTsV9#{8)aS^U#9J*-jyhR9GMkD1@zl% zm`bY6B!7|$N~WF_MtY?&K}HurHU<6q$KobFiGsz&IoDHHi7(W~V8ya#$t)+v5Nr!S zQmCwfI0?$rqi;Lx5Di0=`4)Mz4|Gt3x%x5|_)r~9bckpG7EfFG)(UtLt)h5oYX`%&UZ*)D1fwTl~*01`8! zIcBntS#PEeC|-tUmH4IIl^smJ>0uuHo>uKMXf_6yKi2L+v!bI>l;ryzqpDAYY zDnjukzwtXE8fy?XO7sKiSYCNcb`M?G%hfFV{h%c%GR_(Jv>knmMSkH?EL^>8p@tn5 z{R`ml%+`j+hL*;bCTzUu>-6yFLUiK0I_C+o8P{Ey!P8c69~&MybsJ^9xL$}0VMx=~ ztQK3b@d|@BZSk6=-MH=oPVJtyaPuhG_#fu6cKqI(Qa+kIuW2phT&ea2_=Eijv{Y;Y zp#m~}NW&9~bMZ&(A@N!G!&HT8Pru2L4(R5t}P1_xFKcxRzKIdUw{R~f!PN}lQ zyMhEy4IDSc61@SE{I1YNrIz?~6~WyBf<4s)pD(A}PYX(af#HWsFDiA!U1AR;Y>5&5 zDAujbe^F@&AIhiz|J4q`Z4Tvb)Ck_h&>Q?~1>Sc8rzO76?Rti5*I3RatixPR4>7C` zR#ZA746TYqtwWk<#Z>m`XuZc0A7LHtFQW)`njRJx0y`3UHH^Ik zY?s&|W-<0j&Knf98ui&Zz%po02V+ku>V@2{1_IU<&_lv(8@b3~lX zScI`r0bhll{|JULBesfDH3v16Rts^C*ru>&q4PPI-KT0gI4{86=CC+VVZTA%usB~~ z+d1#u3R}w99)+FE*kuZ9W9);#aKb89SCEtssl4kr?^lLR54rEx|qBu(! zyH#=inK^G$n1|c&dBs`7oL^FTQ#tQW%0r*M7AIeI4`ylvx=di4&Jp*D(-_O3UnEAl ztSK+VMj5*gocX-30-GuBu57etiaXs_`?y_;qV2Y23H*TRal5Ye%m@6IPVjMt-)Hzd z!=EvHk>MX1zMJ_kV|Xv%vAa&eSM847WtOzsQ^W<{qX0KET;V184;K-Ya*6U0JKwGv zZ5h=I=!z2W3cE?X%Q+VCea;%YRqS#yfP0*i?fzXaL;wC=eI~&NJp{kUa4l0p3@y~& zziW)??`6sr4BMHqhT+#)&JP({5#qlet?1wN4TksW>yYkd&Ie3N_j3B%oPLI-{g~nF zHB{=+a)PZ)IfG^1#Ocip6AZt=@V5+4W@-C4{d=TK#9C(%5_W*nB$iiE+k2}rljvC+ z2>3UludcQT+BU(@BCUx(myTdGd(jq6EJ(B?T@@ziFP*P)HQ|jCkEG{0HSr5Bdo;?* zTurn>f+prx5TsHPwrIzTF}p=`^|JtX8E4s+IL_G#cuCDVz_X^T0F3*016EJz1pH>= zV!)fjlzV?QQLg6n3tmbOx}YoDe%juHwmcdq_#9xCzSP+b$`30DmT3fE2h8SvxB7BW zE{+iV6Q_44F3;uuEOZs<#idu}C{x_)KzSv6U5+xp^afCRN^Zze&Z_u0D8mf3l8@); zA5VN5^nmqgXnve?8{kIg4#3}YTR!Kf7JZ!IPq;T;VfdIIZ4|GS+y!`_Mg1dt>6?jr zbCUmTKLE)~;{=P!Pw)|xlqZ<-3_;bC)2&BP>e%=r z;AyEQxTBJED2c4Y_!FE3k&U*>dE*BN{jlOa5h{BTbPx#Ige_`w@ajEqQ-!9?S z5$9E6w*6V(`Jz%`FZ;geyFfHa%zG?mz=h&SmG{TkqvnNTF=JPVmm?2p7mH&R_F}}) zFA*my&ax?Y`SyrS3j1u)UB1i2*$TVVdcgNVafQNu6?@osh4>GJ%`JW0_aSks!d{3y z;JZfLt+1DUKk!{E9wrQ<`D|jZ@5B7V-qeQ2#IJnU;Wyl5i@qg(@4H?E6n263iti(0 zy28e_zxh5Y8Wr}eW;i#BB?^03^Eo$(euZ76hnM78rtK_8)| z+Aq*Z3p&4iHU8#qk+93%VBI60sCtX>vO&!X3$Xx`s`Bc zIOj{Em9fi(uc*)YirB8Oqf7goyT#`jyI4%Yq0fEd2MT)`kIwHC)*O|yLe@h*7X`^4uNdr*6YWqwg%vW71y z?2G;Z=RWZaV?Wovh)3yP6~B~u;w*#>e^rzuCP&U(0N$gOqd$K?}*Q+JhH6sh}#rKmi36Z zLt$iDkBU1LMwa!MxKCkZJx_>lD2%M^SjgCw;t$&8&L72c#;y`ytuUNFiL}Bt#;sQ#7kp~=G`+&mU7kkw4YtJd{tl0ORkoGr)E%N=+3~PxtDYM!4sOf5b3OghIBPXJ* zCrrrwM5#8cFxpR)YNtz1@1D}1I#KOH#;y>5^#0O}YS$}lJp4;DrrpBWmEuwDmrj}X zxWZn*J$Qxoy28F*{Hjx>9f{XXg?6R*T(RztYikvDQE|{eP5Y9w)1*}@>~Eg$J1yGu9MVu zad=ntV*gRvQwn=$=_-Gx_EUx3R*)4?C|cE<1ZZ_;ij4EA!S^=2jg=h{DDq zqyG13FDYzw;uZfMt)x?OuC3Mrmunpgd%C(PaE+E$7`5Re+ARueD!J1CG3{}MQHwsM zJ-rZ8Ft*FAs=(*8XXyb6^!%y+qyD?JJopHtY)(dofH{a%GV7_ASk)E`yY9nty0)%pZs;!1omYDw^T z{hCtq_ypQI0NM4qyxlk`0~>?i(H^tD^4oV4q$`Ywf$ zEp697rZBRlv-O7+Mz%Drzo;;>r3>}Ih?GLM^j`fWg^?|NK)+C7WJ@2?-!n?ONL%`_ zzUM4ij%?}U`hv41Mz-`B{X~V4E#0nfR2bRP9XcO|D_go--*_&`yh@NQ-KUrAk{H?2 zxAY?wMz-|0eyhUBmcFmQx?6IREj_OvbFsw8mVU0A@0S?a(#!hi2~)Q87yUtnku3=$ zeVNQ7Te6J{6-KrcGS2;g%p+SWF>Y2E*;2W2hr-B~YK#{ZMz%EFSai88N48XJ3@VIl zsnNKdFt(+Wf^&^~6-KtyW}N*&nMbxX&$wD)WJ^aFpHdjv($U7fgehC4xk1H%tGaNj{c#1Kp=SE{fVZV!C9o%TV#286AT5L2nUCp(#)|-qm zg^|{qjGYo=tv4BuD2%k;VqAVrUh7fgKNUt=j~RbpY`0kA_lRvq*|o&ETLfx865MXA zRhSO!4C6|LeG_ds)A$5qvbEER@c+P)w@Lc2jj9o6iQudkPxG|xyE6ctRe3$Xd z9Os?ET?XAS(VeL%e;|0FQLL~d%D)}F*r-(4^JPy2-($>RY`6Cl7|r(?t&GwB`iH^y z8J}ZJj^_J~FDs1t<^9IP64S^+-fz%XxoG^zLOx(z&lp;Zh?y&tWj*MjG0I~!Mpqjz zGN)|qwT8H!q+BJ!Wj_vHYfR5!zX@J%^yjcY1#d9UR@kq7BJ?ri)*R*!eae`~VP&Du z8AbmgWj^DZ9{PfDWDaW&-D6yt!+JyCG@fSca`BLFL+HE4jE~525BbJIPaCZ{>|LQB z7@Kq0rJ=pXH41BsULX3Y@kkE)WayX1OA4!x-Vu7yNPbkxtdBkr`h#(H4tp~6nsJB1 zdaGXu?K56b*!{lWg>+Ll!m@>8D| zQdUH>qo9d{rwy?ey{&O?>&!!1Whs;CV__N>%JobtMRZxpoJ1#kFvPoElJ+Tv(n2JK z^pUN|wxy5zq${4K>mr}d@|0ENbF*nWRX)8ePyYq{m89rwhtdKI>(a!hE2(FsCQS~N z9mII)q8WB#h`Vf}NJ}OjUHmRaly|!{A~H?0K+Xke`FNttI*>(W>y;8DMfOG^h2{v& zAd+)nDXE*J7xEO!kvtRJCx1lmX`+O6AgGJbKS0UrQ>fda)7g=g{i8#ZF3daXVY1KP z#U$mxTruH=^#Y72g~{-7;-W8LsPpRFj*nWem${>hg*bbAk!Q&{ToxrLnhbXLOy zDIdc4>UhdSy&=<=@rX-5EN$R)_N#@QazrkNZR=uF6_u5C@}Nc(hwbDkmi#^e}h~ zaVO8IeSYGU>1UXSpe~-^^tDm48975`i!NqQBH;&lm5D=wCJNJA(IQ>!0o}k8L&_~@ zJ&A5G-BffrQ}(BO#D!eyKsozUOvUN7s7416cKa67vQO?~-M#>5ijM(e?c-i8tq8#n=inAciC(@uh%C`TJ zvU0ccWv-XLDrbt<0JD8wNMFd-dmum2J$Pn)=u$G*Vx4WJ^P%KWyXeXI;iMJv6t-RJ z-^BVX!%WwNv@~fI)D}|^k4MP1JX$a=GS|ap7uci&@z`P}_`N*ie4-3#N4$)^fnQmL zCqQ$jP`;EvWwRq&*iy07&!I#=%2+%9;0rZKz*h@I#ukdDmwypr4#qTfYNtQ!pAqUXnQqmeV@fi0Z zX@cDybKV2ZJ$SZGoMf@Kk}0(fGG{OR11ydAW8`V^h#nf+;&*6ER%T(_KMsC5Y8LBY zGryPRPv)l?WD3boHb77LN%8~G%FCsE;z4*whs*kvZn70L7MOhpNcb z{c}0x>UJoZa{q834_TK-$a!&KuI#Z}dEOmp)wgs1Q?4bbH#8x63U$cKr?`$c%R>@$ z?(?kQBMm)D!lNwbqdXUic}4yKEK2Sb^-1ese#OkLYjS-&kSw%+>#}9#c^ygC#qY8A zDAek$)F^eMlO;_^DE;t(^*#@8OuA8vWQ~QjOF5E9p288QsW=PURah!d&&tf!LjAda zyRvOB?CnEoD|=Y>QXikk%AKAZwL%Jwf~iiK4o{c&hIxLE8gW_b&E!(;=A=?mE9&j+ zi7wqsTHe%?-J3nV?>5a zeV#e1#M8y6iRlch#74xZP6s?s3~RTR-i`EaH4kz68Klpz_!(fGFRWFGhhh~PrKbZ@ znraN=4ZSaxhP8lL>qFeM*jc(*J6&66uLjR;HG?Qs=ldYw&eBgX{UPlft*-iUys<-N zTg7(!7aHDCioXngiaBf*_s3oZ=L*}W6~8pS=#_gtI_%8!!rBQpUK}X+e8@3rn%DuD5*Grt zAX>fw@6bLD*d=}hxLgFo4PrIJ6Ga))8yKD@n!{b<0!Zt^DbNwt22O9_^aY&0K&9U& z9&xY0yOBp&H#7Zarr#{yC;sSuk?Hq~S}`8JpLP2N!>5?OLAwH_I(4dL9mCxWr zbtcg-V7Sgh=^HG9_cNSeIL#)?H2aHyU7YUX^af6E;B>-IJPR1E^;3;EFnl3M>FyB0 zb)nCQ_lI{!J|kWY-w3$gors_{c>R;gCK#@Zae6lCwt(SUhPxQv!0>*C6AVQyaV8ip zV7QjyE`~QSyr1C&Ly=;Bh6@<3Ww?u>sHfU-HYeVTXaDrij~y?zz4(%e27kf7iYDNU z=+|)YPp4li;Oh}c^@}vT$%n)(;zi8sIob;CByFp*_dzaGA=c)Fg|L0(sc0F?`7CiL^~`4E%gEFcth9&*oM~X7#Sa6 z2lv6zXtR#DT#LYY0-!EVMC)~|4-vp&yiKf&8}LTDCT;}O#ZBlX9X_=ZuvDuCtkB|s zmD&`*8f_Y2LYo0NMVkd!tIY;XX(_;ZtsbyJn*(^X)&$szm*{n|P@4;Qvepi`UYiHF zQ9A;-F4+yEcqIdTA#9X5lZ1zuEZ1?>xY( zMX*vwpnF7+*PDaCM*KD5uNi+W_-n=AT>Q16kK3`vci?XxB+du-5%{BjLGd|i6!;yS z;R?ST3i}AM{f6eIQK z$2BTa*XWk5V?*1v4Gqf7T*exdI3TNgC@%(_O>G^W9gWT1bDLV{^(<=b?poB*);_nn zXKqJFPghS*M^{UC$K00IxpU_=wRSFSX>Dz5ZENgo?`iL7TiDpzgZE0CL|>EW92;AA z&M~8-o7XmpRZwx|$mVlaZ(TP6bm_t~PB~?0?D(L zfU^D^f@%DnI<$SDXXKR8u?<7OwoSrNZb{d`*y#4n$m$!~qS#lTvu!&gTi0!eb|WCK zSvs;Em*a;w50Z9B;w=Z#84x-*v(6y4wL&9+H2xqEkV@jR95}(zZ4KFx86F*JfCggY zhE^7ZqG-p)b=x)qhtO6Qny1c1ueOQqp5Fe>6+P+xH9*izK<=+LXf(G?bS&(d*VWzC z);Moo%iOt*U7a1>osAtW9qkJjb6WdVhqerj z&`^$!bPsJ`H@tbnB3~R!#jts5aqIfrtt{dzg-mqPv+t!^qq|m<3^uX%= z&aUO1y}c`0Fw6$TZZWT;t+T7MtEsuAv13t7PuIdlJ?%}+-Cd1sjjfm-3%fd7x-ey$ zd)ntMY;J7oY+cmZ)!H*}UUzrv-0nr~J)*C(zYBKTxu|FHn$A_-1Kmqe(Tc7fv8Z!( zfA7kb$8cVrOpWU6>07xfGq7fL=i(l2B^oh!T(3;iJm~LS*xN&MfpVeh!D!t}yZV=| zT+s0jE{iHu%W0O-JqHGQgR zS8va8J;+P4vh?r4NuN5Br>mDFAB*6 zkgS%G9JzDz=Ftse+ZcplDh&+^no+nAD_!MUH7JI7%^DP^tUGgb3@gv-twYEq%g$1i zWH<|%w`yqf(7J6yed|WnoeEZkt{K^)2=IWzpu^P;jX{UgkY2rU^o-4e3x{a=p{#8x zF*dZ78h^_0*cOn7N4B1^y=!!2+Zlw>0)rnG+Hl&Mk#(DgPaOe?eCvKBT2WSSTsMZ? z{*7a!XDvOYdwAQ{QLMDQ*eus{}zowL2ZToOv0EX}1P z%nOPfMKfkrAyB9Q`> zpbChnv;qVbYC%x?gHj4=)l{^FO0-RK{d}L9cXxf~K>nes)ULhr&OGzXGtWHp%rno- zyzjdg<{qu&SDF`sC-bfR?D8^G>xFrUwuRljAmeCxqg0&olY(chJXJLvK;P=j$fZI{ zsw^OG%yM}{u^R0(J&B}uq}B$4G)no_VpDd1vE-&FOpS<{<^yk#TXOT2a-pQ+RM9NG zxs4i@n4mhaid>O=rRiaH%Q;*;lLyV+UTV#itJVBcrF5c%-=eaCl-K6@KoAeZQ+CZ- z8=u42MKpe|hPH-F$QVMe*XpS7)J9=7UnN~&fT7U~YN3-4 z;$wwMxUS~QRUe)4>TQUN+*mB)$P%YyH9fdw z9;KT@hiHF?379Ln!^I*#T&=N5ZnN12a)RWU(s@7(-VuzGugqfTnD4HJC5tGsMV5pX ztb>DzauJPn%e3`m-J(^-%0ew9CYaXAmR;<0^>V(6DhEf#F;0YGiti5N>#r?UHjm+6 zK`ei`#Vgg8+T>5IEtQHG;4WmQ*^yHhD3y@P+^!h`x;^+Ot7n)bmdomBO)A|&o!CjK zRVU>pOH1vQ72$Vb$7$$xNW}9(xz&s4b+yw=T?z~N2HJ77fo81N8mGG%*qLHk&Sk#R z1%QLb;#H1p_JNL_w?{}>r2!2E{An%(@UJ#!7*??S7a5y?Fm<8H=GGg_5( zVG$KG5Y^3YMi|NlZR%?rUrVM5eYFvAcm zIgKSB3r@gs#BheOD&GU84N0L*mK)3>%B;0pO!M6|rmnKdgu?tBp>*i>z?f}1des9K zYEQD_=jvIjqX#ZDJh2|gF>Ht2Bh9@XOH;Jev2wLLFPj^&ZKk$$>vqUmenXQ-w30U} zvYh6uSZZ-eu8c)BePP1jw8>)HZwN#g>d@Yef(U-$K7r+|+E#-_Wtr5UY(1R7#qj!S zIasJkns0H*!6Gdiyy&}6ZnJBsN%4g;B0cD0&@r>s8M$so*qk_vA)@kHd(HQSK@7Dg z?V0ESAjh6m&3e&F$`)J1`yUpaZ`Vy$N6T^$>BYsS!m@?l2z zVh1R=Gu2{gV>{ycAoD$p5_PSb_InpJS}BB-_oza`7z%Nz(ptG1Qof5r0uB~?y;_{b zu8ZsNUtm0-F8a<7DXFcDeq%ELD&`gMXvEfW(eV-RQg1$ZI*7i2Ys#!&IL#UZVdX1} zh1C+NO0C8M#79e2AF&QE*~krxBpCS?U zer&I~`b^cp+VrH;?7~pAt5;apWFNF#D8mR1sJb-;Os%?8_++xxScA!ik|(inb&OQ07&OS8@m`S27YZ!6y~h$1vT@jkgNr<# zkjw{YSbUV0E3$gED(l4HFC2{_d_}O z&c!Z#`@*;jKipVhNndSY+h>kEWlHi|5DmJ*7s?J)q!(*1RX{&L)TX z^?V)wzf^bSx~tXw%8?L_Wip_vC$z2$eHc-Wjsybu!ihd;iuV(oU!R49pZ?tp67cHO zP{YkiMQOio*#-i|RB$`WHc-(!5=J>Jo}8yNU0zv@AW>aWx~HxP1d2)+@gCSwR!-Ys zzNo+vR>@Z14fkrPt1Wh4!`@ntFYaDl=&Xa!&(iAJkH;XeyO|UMK$DQUD?Mox6QvJ; zB|8kUkvpbUwr8$p z78s?7V&0l`QuTnqM#pNcfe~)lI#1?^ZJ@rouYk!%cU1sTuh|#~Ida&2-X}<@`#u zCbzcfOxbO(p_3i$hnNg&^=LaJq84RqX!LHEboUW(9*pN^Skf}AINm5VU3F=mp~VE= z4DB#C#?;0ji^rFsx>l=dY1E`W3Z)6Qjpm|7nNJ-n7aFx@ZMnt9jZAdDve_&*aR|+} zbkbP(M&3r2RS^_-Dq7n%H7~JW(E|vcV`R3+yDQbEJ5p|StXhZqwRS*BihsaR{Xt)caeXEU4=?TFis_T-fd%Xl*C9C=elfDY{@{5p3>I(`e#ud4h; zKV*yo9#m!1V%n56Jzha*+SVtQZdcGepA;KyHP(B z0^E}f3HCwQ6lJc}KCrFQz4IIZH4n%2)iyT%PK()UacU#P%d*(E>Fvt?g6HHTYDkn* zTlcX_6hWwqHy^yCq9wStG<6ZI)5M~;pyueg1>Bwr<5zR3P0#_CsoHkb*?8BMF^Xgj zKrjudi$dRfer;slH!$k+i$Mfulvr9s8@-5D^4!jhL5e`N)MX)nQ^K{on8ok2ZGS&C zxMkeb5Utr{VI6}O%h7Y1xS}}Dmch;-z1q5C73|8zjj#i4t1Um6ebk%(5ciaQqO7Hw zv3$PDYk@N_r`oS_;C(&BIttEKN03rS_~B@2(d+e(Ku{kmn@I#BcW!K8$=S@cLw|;) zEuWY&d^)e23QQDcSha(->0qt4t^p7%phZR2Yhh&Wy?JDU*02wWH8YuxewoPE^JwN~ zgdG`8PhAFkW_b#0wTXH(2=&>;xf{QHw9MTw4SqhNz)4({zZ!RG<~Wlp5mv#d=vOap zFB3llMuj_gisaz&@z(^a#hoP;t}GXb>)zx7xoc2RnLMyE=_+@ju*JOIQ>y>e$9JV#2fE(I?KccQ0qo!YLQh5r_m6{=0$_I7K? zai5KwxR!iQX`s<07D-{v_%B$iF67b6qiwZAqwiYhUOsg}OE4EkTdk{6hj?|DiknSq zt?CnJ;?P~CySwwogL*(x6<^)5yg)8{igdT7l)5@ct9$uwgc2*1_j07%{+*>*v^M6c zr?(eP?l!<7H$m#Zo=Si(@@Ykjv)aOOtrk92czCM}lSx>~Gp(FSswJJNF8d6`FAE8tu>)>K;Mah<@D zMHzSc{NvlN{QRvq{>{YJr+@W3-}~2}|997ybFoA&7E31yvU?OAO6KCRAwmGRS$J1+ z&*)k#nYi*Tqnjx*N4FkKCsMKOeZowq`)`bKuf6LVh@~Mn)R&5-FG?lT>8X+AaFQUo zC!3EY$x0I|#ggfqyK?LIM|Qhp&JkXeN`N*d+%aC`8%vI+(tM)QMk1T)@8VeOqLElk z)UI%2S8xj`@qBEz6J)m=;s=mAdGH69!{RBCw)(TTj3qBo9hQnNmdNCc=x&!L7NP^W zxblEOqCY}Ur{h<+)IiK6)1Qmc5M&aEs_To5BuA2&RH`q#FFlszZdzY{Y_}UuC4gB~ z7D$Tw5SB@Zvf2BL12rX`f#2-D?0wmNz`VfHgRpJV0dqk4_nCN9=P-Xfmk}Qt8p@>N zV_ww#dq%hZIL0mX&LX=VoWe$0y~s5lAC9H0Y%-&p@I_-_fDXJY;bNDi6FD~=lLidO zNCQaqrH5m54DZ0DwF3x0GZK%DT*1Ff4j_;uWm9}Jd*M@q zsEy(Zd>s#^r&8Ak2vv%9CT{r=4J*)rq|-zR_$VfNif4FnrHW^_9^pTUptjBqG8~K| z1yULspv|NX(q!65%9l1;*{#oH(x9m>T{Fl_b#&MdwWubtLCL5epxc+_M^%8N5@Y}# zPmQHRscr*At(&q+JpIf>>PEQv;`U6@;{#>!;y=SlB`^4hp^TXLN_OkB>0PO$+Gd;# zC+U&wE$Jkkv-MOz;-URq#J>zhkc|2P(F_g_q8aHy!yDv1iyD7c^dZPlM7<@mA}lK} zdt^h#=U|*v(b@EquU&jw_NxQ8roZ?LyU%^;`oF$w>k)3|b%`y#tCKs7;{>En%pcy* zZMEE^9CPJs+UxJx^Yz-Fo%-l&Pk-l!FZsZOr*BGJ`sdG_d&Bbc(|`BF;~%@?%fGX_ ze&{6+jC^zU>Di~hb>O3)`_LmlbNt<(efG`&Hnp00#ewI3kT`yF^)KH4#zzM~`PG+| zzu%tx%Wq7N9DHjdQTf=1p84Rp$}{i&6L-%`_rL4C`4exby`y-=H&Q=1H2YicNuT@S zsS}U=Ug9(G1TO@7!(u|w1oss4SdaqVp~9m~3K;z^p*w(i_mN(T`}t4z^=J3S(L3g)L_da%^mLq`f9xO2@Fjbg z>u@^WpGo$Qq3GnliSI2!{#ylqrQq)sJd*5}UMu>rf=3m6Siutto>cI01)osxjDmlN z_xD?=reVSkya(2uxGXnfF_p1xvYS2??bQ=jHHVAe^W!Pmpalz6K!#}8Nq!6`El}y6Qt3@hp!X!C?8fJVc3fMiHA6?&n1CWiWQF4dpwi>1=)wKz<{L^=+KTnS;tW_-zwB)$!7 zdi(4!nAlK(f%7b9Y}h;`(#hc%gn)`fDoXG(H?d5Vz<-DhMu|a)GFc2K(ViGzi7A$r zPmoOI%+pHZ`8nuA7Ly$}&&MJX(Ma}|cp`_+(ZfatHbu~8YS`6m7Ng4yq7>wr6kL3S z=^byxqf(bzLbf{B3aN3L)d{(%OQU%R2K;np2y%vE@gb~Jb{evp%OBBokgIs2)x=Om zsk|Qx!etQD<9Ux{xI@_2F58p=J=r_bs=?tFIjL= z6`#Nq?H1K2S8%|4TuM$yfQpzQl!aCaj5W`0nsBg50U&A09Em9IqD#Bf2 zbTv*Hjr`G7BFbKcZ?Ki5!agjAngM)-%A|XvtFmdRJg~7O?zJ?*1LGLV?3)6Zc_N{N z%Ed=rMscD_#Yb?n@lDixE zI`^A)PK517@Ma7=c|mMI#N1`$$EOx_A1OQZ`y$un4898lhA!^h!>A+eN`BLBcID?} zM~;;%wdMo4I~w`>OY0m4@*BD_mkKw#Id?B-7buqW=M2QogGG}?4CmWSluC`kB=B+o}u)@Y~ zdug0nZt#qC__|?dQc075YMq=qa`JY^n+V*Sf+Oc2qWTH=b@Frnhicl|p{D&e|0LRk zqVe->G50H{<*hHocj?}q^<%iaWuB%2keR8j;>U6*EVMg$tJ{x5qg$k&aZz^e@$qlp z^}fgV-gnnCBagrR`_)f=>5^}Kx^&AEPoKGAE*lPk-d5 zf2#cH&F_5U9UH?(e(t;1zvlRD_hz2{=XZbSm%sL#!}H&M`-uFoM>MxA{+N%b}ZX0^=`E~c)=bnGVd#=9tx#IOt+%F#f z<iv(ke)_Iwt8W*L$y@K`wpg9-HSgU?aqs1}%$+l8OZVR!M&G^FQj77sBi?wu zxa8)i59=Acz`)6f0UCMT^OKeAZTq9zWu-gYdsDqTfE&^Sy3sJ8PO098VHo2p8To-`D42 z?%|#WKHPT@=qWePp7<#@#cz?%yBRm@j+34tJW5=jPbI#G#_K6+)sXC5Uu(Huu2EkE z5bqXIiBq7`2`E6*%&z z1YJsBH=G-3xBolH2@EoT=5>^vptd#s{3$^b_#6;_=Ad)0ob$Ws(MeZ#AK#xn%CkLs&in>NYKTiwb(W}NH%;{j^D!`gj6e60iF?o6Q||2@!% z+BivWlRbL1UFR*HBjMe_*@h_TeHd!r`CN~ORta|>Yp5>G?dMI(-w%}oe3_c5|0Uo` zw*Ev6SQ->BfIllK;-LnC%a~9Pd z$wDdh;)`#4FAuLXxz0gbr*>^*+UjZ5i>FET=$7QUo!0qM?Um^*_-rxW-sDfabe3AN f6R{UAGj95;x;h`WyS2CW#H%}y|Bw71lE8lhopm*s literal 0 HcmV?d00001 diff --git a/dep/K4os.Compression.LZ4/K4os.Compression.LZ4.xml b/dep/K4os.Compression.LZ4/K4os.Compression.LZ4.xml new file mode 100644 index 00000000..2ccb4269 --- /dev/null +++ b/dep/K4os.Compression.LZ4/K4os.Compression.LZ4.xml @@ -0,0 +1,1211 @@ + + + + K4os.Compression.LZ4 + + + + + Action performed by encoder using FlushAndEncode method. + + + + Nothing has happened, most likely loading 0 bytes. + + + Some bytes has been loaded into encoder. + + + Compression was not possible so bytes has been copied. + + + Compression succeeded. + + + + Interface of LZ4 decoder used by LZ4 streams. + + + + Block size. + + + Bytes already decoded and available to be read. + Always smaller than + + + + Decodes previously compressed block and caches decompressed block in decoder. + Returns number of bytes decoded. These bytes can be read with . + + Points to compressed block. + Length of compressed block. + Size of the block. Value 0 indicates default block size. + Number of decoded bytes. + + + + Inject already decompressed block and caches it in decoder. + Used with uncompressed-yet-chained blocks and pre-made dictionaries. + These bytes can be read with . + + Points to uncompressed block. + Length of uncompressed block. + Number of decoded bytes. + + + + Reads previously decoded bytes. Please note, should be + negative number, pointing to bytes before current head. + + Buffer to write to. + Offset in source buffer relatively to current head. + Please note, it should be negative value. + Number of bytes to read. + + + + Peeks at previously decoded bytes. Please note, should be + negative number, pointing to bytes before current head. + + Offset in source buffer relatively to current head. + Please note, it should be negative value. + + + + Interface of LZ4 encoder used by LZ4 streams. + + + + Block size. + + + Number of bytes read for compression. + Always smaller than + + + Adds bytes to internal buffer. Increases + Source buffer. + Source buffer length. + Number of bytes topped up. If this function returns 0 it means that buffer + is full ( equals ) and + should be called to flush it. + + + + Encodes bytes in internal buffer (see: , ). + If is true then if encoded buffer is bigger than + source buffer source bytes are copied instead. In such case returned length is negative. + + Target buffer. + Target buffer length. + Indicates if copying is allowed. + Length of encoded buffer. Negative if bytes are just copied. + + + + LZ4 decoder used with independent blocks mode. Please note, that it will fail + if input data has been compressed with chained blocks + ( and ) + + + + Creates new instance of block decoder. + Block size. Must be equal or greater to one used for compression. + + + + + + + + + + + + + + + + + + + + + + + + + Independent block encoder. Produces larger files but uses less memory and + gives better performance. + + + + Creates new instance of + Compression level. + Block size. + + + + + + + + + LZ4 decoder handling dependent blocks. + + + Creates new instance of . + Block size. + Number of extra blocks. + + + + + + + + + + + + + + + + + + + + + + + + + Static class with factory methods to create LZ4 decoders. + + + + Creates appropriate decoder for given parameters. + Dependent blocks. + Block size. + Number of extra blocks. + LZ4 decoder. + + + + Static class with factory method to create LZ4 encoders. + + + + Creates appropriate decoder for given parameters. + Dependent blocks. + Compression level. + Block size. + Number of extra blocks. + LZ4 encoder. + + + + Base class for LZ4 encoders. Provides basic functionality shared by + , , + and encoders. Do not used directly. + + + + Creates new instance of encoder. + Needs to be true if using dependent blocks. + Block size. + Number of extra blocks. + + + + + + + + + + + + + + + Encodes single block using appropriate algorithm. + Source buffer. + Source buffer length. + Target buffer. + Target buffer length. + Number of bytes actually written to target buffer. + + + Copies current dictionary. + Target buffer. + Dictionary length. + Dictionary length. + + + + + + + Functionality of encoders added on top of fixed interface. + + + + Tops encoder up with some data. + Encoder. + Buffer pointer, will be shifted after operation by the number of + bytes actually loaded. + Length of buffer. + true if buffer was topped up, false if no bytes were loaded. + + + Tops encoder up with some data. + Encoder. + Buffer. + Buffer offset. + Length of buffer. + Number of bytes actually loaded. + + + Tops encoder up with some data. + Encoder. + Buffer. + Buffer offset, will be increased after operation by the number + of bytes actually loaded. + Length of buffer. + true if buffer was topped up, false if no bytes were loaded. + + + Encodes all bytes currently stored in encoder into target buffer. + Encoder. + Target buffer. + Offset in target buffer. + Length of target buffer. + if true copying bytes is allowed. + Number of bytes encoder. If bytes were copied than this value is negative. + + + Encodes all bytes currently stored in encoder into target buffer. + Encoder. + Target buffer. + Offset in target buffer. Will be updated after operation. + Length of target buffer. + if true copying bytes is allowed. + Result of this action. Bytes can be Copied (), + Encoded () or nothing could have + happened (). + + + Encodes all bytes currently stored in encoder into target buffer. + Encoder. + Target buffer. Will be updated after operation. + Length of buffer. + if true copying bytes is allowed. + Result of this action. Bytes can be Copied (), + Encoded () or nothing could have + happened (). + + + Tops encoder and encodes content. + Encoder. + Source buffer (used to top up from). + Source buffer length. + Target buffer (used to encode into) + Target buffer length. + Forces encoding even if encoder is not full. + Allows to copy bytes if compression was not possible. + Number of bytes loaded (topped up) + Number if bytes encoded or copied. + Value is 0 if no encoding was done. + Action performed. + + + Tops encoder and encodes content. + Encoder. + Source buffer (used to top up from). + Offset within source buffer. + Source buffer length. + Target buffer (used to encode into) + Offset within target buffer. + Target buffer length. + Forces encoding even if encoder is not full. + Allows to copy bytes if compression was not possible. + Number of bytes loaded (topped up) + Number if bytes encoded or copied. + Value is 0 if no encoding was done. + Action performed. + + + Tops encoder and encodes content. + Encoder. + Source buffer (used to top up from). + Target buffer (used to encode into) + Forces encoding even if encoder is not full. + Allows to copy bytes if compression was not possible. + Number of bytes loaded (topped up) + Number if bytes encoded or copied. + Value is 0 if no encoding was done. + Action performed. + + + Encoded remaining bytes in encoder. + Encoder. + Target buffer. + Target buffer length. + Allows to copy bytes if compression was not possible. + Number if bytes encoded or copied. + Value is 0 if no encoding was done. + Action performed. + + + Encoded remaining bytes in encoder. + Encoder. + Target buffer. + Offset within target buffer. + Target buffer length. + Allows to copy bytes if compression was not possible. + Number if bytes encoded or copied. + Value is 0 if no encoding was done. + Action performed. + + + Encoded remaining bytes in encoder. + Encoder. + Target buffer. + Allows to copy bytes if compression was not possible. + Number if bytes encoded or copied. + Value is 0 if no encoding was done. + Action performed. + + + Drains decoder by reading all bytes which are ready. + Decoder. + Target buffer. + Offset within target buffer. + Offset in decoder relatively to decoder's head. + Please note, it should be negative value. + Number of bytes. + + + Drains decoder by reading all bytes which are ready. + Decoder. + Target buffer. + Offset in decoder relatively to decoder's head. + Please note, it should be negative value. + Number of bytes. + + + Decodes data and immediately drains it into target buffer. + Decoder. + Source buffer (with compressed data, to be decoded). + Source buffer length. + Target buffer (to drained into). + Target buffer length. + Number of bytes actually decoded. + true decoder was drained, false otherwise. + + + Decodes data and immediately drains it into target buffer. + Decoder. + Source buffer (with compressed data, to be decoded). + Offset within source buffer. + Source buffer length. + Target buffer (to drained into). + Offset within target buffer. + Target buffer length. + Number of bytes actually decoded. + true decoder was drained, false otherwise. + + + Decodes data and immediately drains it into target buffer. + Decoder. + Source buffer (with compressed data, to be decoded). + Target buffer (to drained into). + Number of bytes actually decoded. + true decoder was drained, false otherwise. + + + + Inject already decompressed block and caches it in decoder. + Used with uncompressed-yet-chained blocks and pre-made dictionaries. + See . + + Decoder. + Uncompressed block. + Offset in uncompressed block. + Length of uncompressed block. + Number of decoded bytes. + + + + Decodes previously compressed block and caches decompressed block in decoder. + Returns number of bytes decoded. + See . + + Decoder. + Compressed block. + Offset in compressed block. + Length of compressed block. + Size of the block. Value 0 indicates default block size. + Number of decoded bytes. + + + + LZ4 encoder using dependent blocks with fast compression. + + + + Creates new instance of + Block size. + Number of extra blocks. + + + + + + + + + + + + + LZ4 encoder using dependent blocks with high compression. + + + + Creates new instance of + Compression level. + Block size. + Number of extra blocks. + + + + + + + + + + + + Algorithm selection. + + + Intel and ARMv7 version of 32 bit algorithm. + + + Intel version of 64 bit algorithm. + + + Checks what algorithm should be used (32 vs 64 bit). + + + + Existence of this class is an admission of failure. + I failed to export internals to test assemblies. + Using InternalsVisibleTo work, of course, but with signing (which was requested + in https://github.com/MiloszKrajewski/K4os.Compression.LZ4/issues/9) it is + absolute PITA. So no, I'm not using InternalsVisibleTo I will just expose this + little class with some "redirects" to real internals. + + + + Pubternal wrapper for LZ4_stream_t. + + + Creates new instance of wrapper for LZ4_stream_t. + + + + + + + Compresses chunk of data using LZ4_compress_fast_continue. + + Wrapper for LZ4_stream_t + Source block address. + Target block address. + Source block length. + Target block length. + Acceleration. + Number of bytes actually written to target. + + + + Naive wrapper around ArrayPool. Makes calls if something should be pooled. + + + + Minimum size of the buffer that can be pooled. + + + Allocate temporary buffer to store decompressed data. + Minimum size of the buffer. + Clear all data. + Allocated buffer. + + + + Determines if buffer was pooled or not. + The logic is quite simple: if buffer is smaller than 512 bytes are pooled. + + Buffer. + true if buffer was pooled; false otherwise + + + Releases allocated buffer. + Previously allocated buffer. + + + Utility class with memory related functions. + + + 1 KiB + + + 2 KiB + + + 4 KiB + + + 8 KiB + + + 16 KiB + + + 32 KiB + + + 64 KiB + + + 128 KiB + + + 256 KiB + + + 512 KiB + + + 1 MiB + + + 4 MiB + + + Empty byte array. + + + Checks if process is ran in 32-bit mode. + + + Rounds integer value up to nearest multiple of step. + A value. + A step. + Value rounded up. + + + + Copies memory block for to . + + The target block address. + The source block address. + Length in bytes. + + + + Fills memory block with predefined . + + The target block address. + Value to be used. + Length in bytes. + + + + Copies memory block for to . + + The target block address. + The source block address. + Length in bytes. + + + + Copies memory block for to . + It handle "move" semantic properly handling overlapping blocks properly. + + The target block address. + The source block address. + Length in bytes. + + + Allocated block of memory. It is NOT initialized with zeroes. + Size in bytes. + Pointer to allocated block. + + + Fill block of memory with zeroes. + Address. + Length. + Original pointer. + + + Fills memory block with repeating pattern of a single byte. + Address. + A pattern. + Length. + Original pointer. + + + Allocates block of memory and fills it with zeroes. + Size in bytes. + Pointer to allocated block. + + + Free memory allocated previously with . + Pointer to allocated block. + + + Clones managed array to unmanaged one. + Allows quicker yet less safe unchecked access. + Input array. + Cloned array. + + + Reads exactly 1 byte from given address. + Address. + Byte at given address. + + + Writes exactly 1 byte to given address. + Address. + Value. + + + Reads exactly 2 bytes from given address. + Address. + 2 bytes at given address. + + + Writes exactly 2 bytes to given address. + Address. + Value. + + + Reads exactly 4 bytes from given address. + Address. + 4 bytes at given address. + + + Writes exactly 4 bytes to given address. + Address. + Value. + + + Reads exactly 8 bytes from given address. + Address. + 8 bytes at given address. + + + Writes exactly 8 bytes to given address. + Address. + Value. + + + Copies exactly 1 byte from source to target. + Target address. + Source address. + + + Copies exactly 2 bytes from source to target. + Target address. + Source address. + + + Copies exactly 4 bytes from source to target. + Target address. + Source address. + + + Copies exactly 8 bytes from source to target. + Target address. + Source address. + + + + Represents pinned memory. + It either points to unmanaged memory or block of memory from shared array pool. + When disposed, it handles it appropriately. + + + + + Maximum size of the buffer that can be pooled from shared array pool. + + + + Pointer to block of bytes. + + + Pointer to block of bytes as span. + + + Pointer to block of bytes. + + + + Allocates pinned block of memory, depending on the size it tries to use shared array pool. + + Size in bytes. + Indicates if block should be zeroed. + Allocated . + + + + + Allocates pinned block of memory, depending on the size it tries to use shared array pool. + + Pinned memory pointer. + Size in bytes. + Indicates if block should be zeroed. + Allocated . + + + + + Allocates pinned block of memory for one item from shared array pool. + + PinnedMemory pointer. + Indicates if block should be zeroed. + Type of item. + + + Fill allocated block of memory with zeros. + + + + Releases the memory. + + + + + Skeleton for class with unmanaged resources. + Implements but also handles proper release in + case was not called. + + + + Determines if object was already disposed. + + + Throws exception is object has been disposed already. Convenience method. + Thrown if object is already disposed. + + + Method releasing unmanaged resources. + + + Method releasing managed resources. + + + + Disposed resources. + + true if dispose was explicitly called, + false if called from GC. + + + + + + Destructor. + + + Unsafe memory operations. + + + Reads 4 bytes from given address. + Address. + 4 bytes at given address. + + + Writes 4 or 8 bytes to given address. + Address. + Value. + + + Copies exactly 16 bytes from source to target. + Target address. + Source address. + + + Copies exactly 18 bytes from source to target. + Target address. + Source address. + + + + Copies memory block for to + up to (around) . + It does not handle overlapping blocks and may copy up to 8 bytes more than expected. + + The target block address. + The source block address. + The limit (in target block). + + + + Copies memory block for to + up to (around) . + It does not handle overlapping blocks and may copy up to 32 bytes more than expected. + This version copies two times 16 bytes (instead of one time 32 bytes) + because it must be compatible with offsets >= 16. + + The target block address. + The source block address. + The limit (in target block). + + + Unsafe memory operations. + + + Reads exactly 2 bytes from given address. + Address. + 2 bytes at given address. + + + Writes exactly 2 bytes to given address. + Address. + Value. + + + Reads exactly 4 bytes from given address. + Address. + 4 bytes at given address. + + + Writes exactly 4 bytes to given address. + Address. + Value. + + + Copies exactly 1 byte from source to target. + Target address. + Source address. + + + Copies exactly 2 bytes from source to target. + Target address. + Source address. + + + Copies exactly 4 bytes from source to target. + Target address. + Source address. + + + Reads exactly 8 bytes from given address. + Address. + 8 bytes at given address. + + + Writes exactly 8 bytes to given address. + Address. + Value. + + + Copies exactly 8 bytes from source to target. + Target address. + Source address. + + + Reads 8 bytes from given address. + Address. + 8 bytes at given address. + + + Writes 8 bytes to given address. + Address. + Value. + + + Copies exactly 16 bytes from source to target. + Target address. + Source address. + + + Copies exactly 18 bytes from source to target. + Target address. + Source address. + + + + Copies memory block for to + up to (around) . + It does not handle overlapping blocks and may copy up to 8 bytes more than expected. + + The target block address. + The source block address. + The limit (in target block). + + + + Copies memory block for to + up to (around) . + It does not handle overlapping blocks and may copy up to 32 bytes more than expected. + This version copies two times 16 bytes (instead of one time 32 bytes) + because it must be compatible with offsets >= 16. + + The target block address. + The source block address. + The limit (in target block). + + + + Static class exposing LZ4 block compression methods. + + + + Version of LZ4 implementation. + + + + Enforces 32-bit compression/decompression algorithm even on 64-bit systems. + Please note, this property should not be used on regular basis, it just allows + to workaround some problems on platforms which do not support 64-bit the same was + as Intel (for example: unaligned read/writes). + + + + Maximum size after compression. + Length of input buffer. + Maximum length after compression. + + + Compresses data from one buffer into another. + Input buffer. + Length of input buffer. + Output buffer. + Output buffer length. + Compression level. + Number of bytes written, or negative value if output buffer is too small. + + + Compresses data from one buffer into another. + Input buffer. + Output buffer. + Compression level. + Number of bytes written, or negative value if output buffer is too small. + + + Compresses data from one buffer into another. + Input buffer. + Input buffer offset. + Input buffer length. + Output buffer. + Output buffer offset. + Output buffer length. + Compression level. + Number of bytes written, or negative value if output buffer is too small. + + + Decompresses data from given buffer. + Input buffer. + Input buffer length. + Output buffer. + Output buffer length. + Number of bytes written, or negative value if output buffer is too small. + + + Decompresses data from given buffer. + Input buffer. + Input buffer length. + Output buffer. + Output buffer length. + Dictionary buffer. + Dictionary buffer length. + Number of bytes written, or negative value if output buffer is too small. + + + Decompresses data from given buffer. + Input buffer. + Output buffer. + Number of bytes written, or negative value if output buffer is too small. + + + Decompresses data from given buffer. + Input buffer. + Output buffer. + Dictionary buffer. + Number of bytes written, or negative value if output buffer is too small. + + + Decompresses data from given buffer. + Input buffer. + Input buffer offset. + Input buffer length. + Output buffer. + Output buffer offset. + Output buffer length. + Number of bytes written, or negative value if output buffer is too small. + + + Decompresses data from given buffer. + Input buffer. + Input buffer offset. + Input buffer length. + Output buffer. + Output buffer offset. + Output buffer length. + Dictionary buffer. + Dictionary buffer offset. + Dictionary buffer length. + Number of bytes written, or negative value if output buffer is too small. + + + Compression level. + + + Fast compression. + + + High compression, level 3. + + + High compression, level 4. + + + High compression, level 5. + + + High compression, level 6. + + + High compression, level 7. + + + High compression, level 8. + + + High compression, level 9. + + + Optimal compression, level 10. + + + Optimal compression, level 11. + + + Maximum compression, level 12. + + + + Pickling support with LZ4 compression. + + + Pickling support with LZ4 compression. + + + + Compresses input buffer into self-contained package. + Input buffer. + Compression level. + Output buffer. + + + Compresses input buffer into self-contained package. + Input buffer. + Input buffer offset. + Input buffer length. + Compression level. + Output buffer. + + + Compresses input buffer into self-contained package. + Input buffer. + Length of input data. + Compression level. + Output buffer. + + + Compresses input buffer into self-contained package. + Input buffer. + Compression level. + Output buffer. + + + Compresses input buffer into self-contained package. + Input buffer. + Where the compressed data is written. + Compression level. + Output buffer. + + + Compresses input buffer into self-contained package. + Input buffer. + Where the compressed data is written. + Compression level. + Output buffer. + + + Decompresses previously pickled buffer (see: . + Input buffer. + Output buffer. + + + Decompresses previously pickled buffer (see: . + Input buffer. + Input buffer offset. + Input buffer length. + Output buffer. + + + Decompresses previously pickled buffer (see: . + Input buffer. + Input buffer length. + Output buffer. + + + Decompresses previously pickled buffer (see: . + Input buffer. + Output buffer. + + + Decompresses previously pickled buffer (see: . + Input buffer. + Where the decompressed data is written. + + + Decompresses previously pickled buffer (see: . + Input buffer. + Where the decompressed data is written. + + + + Returns the uncompressed size of a chunk of compressed data. + + The data to inspect. + The size in bytes of the data once unpickled. + + + + Returns the uncompressed size of a chunk of compressed data. + + Decoded header. + The size in bytes of the data once unpickled. + + + Decompresses previously pickled buffer (see: . + Input buffer. + Where the decompressed data is written. + + You obtain the size of the output buffer by calling . + + + + diff --git a/docs/Setup.md b/docs/Setup.md index 905c2649..a850a1eb 100644 --- a/docs/Setup.md +++ b/docs/Setup.md @@ -18,6 +18,8 @@ After getting the client, you need to set up a web server to serve SiteConfig.xm 6. Compile MHServerEmu with Visual Studio or any other tool you prefer. +7. Copy `Calligraphy.sip` and `mu_cdata.sip` located in `Marvel Heroes\Data\Game` to `MHServerEmu\Assets\GPAK`. + Now you can actually start everything and get in-game. 1. Start Apache by running ```Apache24\bin\httpd.exe```. diff --git a/src/MHServerEmu/Assets/GPAK/IMPORTANT b/src/MHServerEmu/Assets/GPAK/IMPORTANT new file mode 100644 index 00000000..356d3e5f --- /dev/null +++ b/src/MHServerEmu/Assets/GPAK/IMPORTANT @@ -0,0 +1 @@ +Copy Calligraphy.sip and mu_cdata.sip from Marvel Heroes\Data\Game to here \ No newline at end of file diff --git a/src/MHServerEmu/GameServer/Data/Database.cs b/src/MHServerEmu/GameServer/Data/Database.cs index 91edb6ce..84bc7089 100644 --- a/src/MHServerEmu/GameServer/Data/Database.cs +++ b/src/MHServerEmu/GameServer/Data/Database.cs @@ -1,5 +1,6 @@ using System.Text; using MHServerEmu.Common; +using MHServerEmu.GameServer.Data.Gpak; namespace MHServerEmu.GameServer.Data { @@ -8,6 +9,9 @@ namespace MHServerEmu.GameServer.Data private static readonly Logger Logger = LogManager.CreateLogger(); public static bool IsInitialized { get; private set; } + public static GpakFile Calligraphy { get; private set; } + public static GpakFile Resource { get; private set; } + public static Dictionary PrototypeDataDict { get; private set; } public static ulong[] GlobalEnumRefTable { get; private set; } @@ -16,17 +20,23 @@ namespace MHServerEmu.GameServer.Data static Database() { - Logger.Info("Loading prototypes..."); + long startTime = ((DateTimeOffset)DateTime.UtcNow).ToUnixTimeMilliseconds(); + + Calligraphy = new("Calligraphy.sip"); + Resource = new("mu_cdata.sip"); + + Logger.Trace("Loading prototypes..."); PrototypeDataDict = LoadPrototypeData($"{Directory.GetCurrentDirectory()}\\Assets\\PrototypeDataTable.bin"); + Logger.Info($"Loaded {PrototypeDataDict.Count} prototypes"); GlobalEnumRefTable = LoadPrototypeEnumRefTable($"{Directory.GetCurrentDirectory()}\\Assets\\GlobalEnumRefTable.bin"); ResourceEnumRefTable = LoadPrototypeEnumRefTable($"{Directory.GetCurrentDirectory()}\\Assets\\ResourceEnumRefTable.bin"); PropertyIdPowerRefTable = LoadPrototypeEnumRefTable($"{Directory.GetCurrentDirectory()}\\Assets\\PropertyIdPowerRefTable.bin"); - if (PrototypeDataDict.Count > 0 && GlobalEnumRefTable.Length > 0 && ResourceEnumRefTable.Length > 0 && PropertyIdPowerRefTable.Length > 0) + if (VerifyData()) { - // -1 is here because the first entry is 0 to offset values and align with the data we get from the game - Logger.Info($"Loaded {PrototypeDataDict.Count} prototypes"); + long loadTime = ((DateTimeOffset)DateTime.UtcNow).ToUnixTimeMilliseconds() - startTime; + Logger.Info($"Finished loading in {loadTime} ms"); IsInitialized = true; } else @@ -36,6 +46,24 @@ namespace MHServerEmu.GameServer.Data } } + public static void ExportGpakEntries() + { + Logger.Info("Exporting Calligraphy entries..."); + Calligraphy.ExportEntries("Calligraphy.tsv"); + Logger.Info("Exporting Resource entries..."); + Resource.ExportEntries("mu_cdata.tsv"); + Logger.Info("Finished exporting GPAK entries"); + } + + public static void ExportGpakData() + { + Logger.Info("Exporting Calligraphy data..."); + Calligraphy.ExportData(); + Logger.Info("Exporting Resource data..."); + Resource.ExportData(); + Logger.Info("Finished exporting GPAK data"); + } + private static Dictionary LoadPrototypeData(string path) { Dictionary prototypeDict = new(); @@ -97,5 +125,15 @@ namespace MHServerEmu.GameServer.Data return Array.Empty(); } } + + private static bool VerifyData() + { + return Calligraphy.Entries.Length > 0 + && Resource.Entries.Length > 0 + && PrototypeDataDict.Count > 0 + && GlobalEnumRefTable.Length > 0 + && ResourceEnumRefTable.Length > 0 + && PropertyIdPowerRefTable.Length > 0; + } } } diff --git a/src/MHServerEmu/GameServer/Data/Gpak/GpakFile.cs b/src/MHServerEmu/GameServer/Data/Gpak/GpakFile.cs index ea484bb4..ac70fddb 100644 --- a/src/MHServerEmu/GameServer/Data/Gpak/GpakFile.cs +++ b/src/MHServerEmu/GameServer/Data/Gpak/GpakFile.cs @@ -1,4 +1,6 @@ -using System.Text; +using System.IO; +using System.Text; +using K4os.Compression.LZ4; using MHServerEmu.Common; namespace MHServerEmu.GameServer.Data.Gpak @@ -10,7 +12,7 @@ namespace MHServerEmu.GameServer.Data.Gpak public int Header { get; } // KAPG public int Field1 { get; } - public GpakEntry[] Entries { get; } + public GpakEntry[] Entries { get; } = Array.Empty(); public GpakFile(string gpakFileName) { @@ -18,15 +20,18 @@ namespace MHServerEmu.GameServer.Data.Gpak if (File.Exists(path)) { + Logger.Trace($"Loading {gpakFileName}..."); + using (FileStream fileStream = File.OpenRead(path)) { byte[] buffer = new byte[4096]; + // Header Header = ReadInt(fileStream, buffer); Field1 = ReadInt(fileStream, buffer); + Entries = new GpakEntry[ReadInt(fileStream, buffer)]; - Entries = new GpakEntry[ReadInt(fileStream, buffer)]; - + // Entry metadata for (int i = 0; i < Entries.Length; i++) { ulong id = ReadUlong(fileStream, buffer); @@ -42,12 +47,46 @@ namespace MHServerEmu.GameServer.Data.Gpak Entries[i] = new(id, filePath, field2, offset, compressedSize, uncompressedSize); } - Logger.Debug($"Loaded {Entries.Length} GPAK entries"); + // Entry data + foreach (GpakEntry entry in Entries) + { + byte[] compressedData = new byte[entry.CompressedSize]; + byte[] uncompressedData = new byte[entry.UncompressedSize]; + fileStream.Read(compressedData, 0, compressedData.Length); + LZ4Codec.Decode(compressedData, uncompressedData); + entry.Data = uncompressedData; + } } + + Logger.Info($"Loaded {Entries.Length} GPAK entries from {gpakFileName}"); } else { - Logger.Warn($"{gpakFileName} not found"); + Logger.Error($"{gpakFileName} not found"); + } + } + + public void ExportEntries(string fileName) + { + using (StreamWriter streamWriter = new($"{GpakDirectory}\\{fileName}")) + { + foreach (GpakEntry entry in Entries) + { + string entryString = $"{entry.Id}\t{entry.FilePath}\t{entry.Field2}\t{entry.Offset}\t{entry.CompressedSize}\t{entry.UncompressedSize}"; + streamWriter.WriteLine(entryString); + } + } + } + + public void ExportData() + { + foreach (GpakEntry entry in Entries) + { + string uncompressedFilePath = $"{GpakDirectory}\\{entry.FilePath}"; + string uncompressedFileDirectory = Path.GetDirectoryName(uncompressedFilePath); + + if (Directory.Exists(uncompressedFileDirectory) == false) Directory.CreateDirectory(uncompressedFileDirectory); + File.WriteAllBytes($"{GpakDirectory}\\{entry.FilePath}", entry.Data); } } diff --git a/src/MHServerEmu/MHServerEmu.csproj b/src/MHServerEmu/MHServerEmu.csproj index 1455c165..f3698090 100644 --- a/src/MHServerEmu/MHServerEmu.csproj +++ b/src/MHServerEmu/MHServerEmu.csproj @@ -15,9 +15,15 @@ ..\..\dep\protobuf-csharp\Google.ProtocolBuffers.dll + + ..\..\dep\K4os.Compression.LZ4\K4os.Compression.LZ4.dll + + + PreserveNewest + PreserveNewest diff --git a/src/MHServerEmu/Program.cs b/src/MHServerEmu/Program.cs index f196ac9f..e5e532da 100644 --- a/src/MHServerEmu/Program.cs +++ b/src/MHServerEmu/Program.cs @@ -47,6 +47,10 @@ namespace MHServerEmu _frontendServer.Shutdown(); else if (input.Equals("parse", StringComparison.OrdinalIgnoreCase)) PacketHelper.ParseServerMessagesFromAllPacketFiles(); + else if (input.Equals("exportgpakentries", StringComparison.OrdinalIgnoreCase)) + Database.ExportGpakEntries(); + else if (input.Equals("exportgpakdata", StringComparison.OrdinalIgnoreCase)) + Database.ExportGpakData(); } _frontendServer.Shutdown();