From 03312f44b6fd62e0276288210a2864eeb70a4a1b Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Mon, 14 Mar 2022 19:09:25 -0400 Subject: [PATCH] 2.3.1 --- VERSION | 2 +- docs/CHANGELOG.md | 4 + images/start.png | Bin 31466 -> 31454 bytes launcher/CMakeLists.txt | 2 +- launcher/src/bootstrap.c | 6 +- launcher/src/client/launcher.cpp | 49 ++-------- launcher/src/ldconfig.cpp | 49 +++++----- libreborn/CMakeLists.txt | 12 +-- libreborn/include/libreborn/config.h | 3 + libreborn/include/libreborn/elf.h | 55 ++---------- libreborn/include/libreborn/exec.h | 60 ++++--------- libreborn/include/libreborn/libreborn.h | 1 + libreborn/include/libreborn/patch.h | 4 +- libreborn/include/libreborn/string.h | 29 ++---- libreborn/include/libreborn/util.h | 12 ++- libreborn/src/{ => patch}/patch.c | 2 +- libreborn/src/util/elf.c | 53 +++++++++++ libreborn/src/util/exec.c | 113 ++++++++++++++++++++++++ libreborn/src/util/string.c | 25 ++++++ libreborn/src/util/util.c | 8 ++ media-layer/core/CMakeLists.txt | 2 +- media-layer/core/src/media.c | 28 +++--- media-layer/core/src/screenshot.c | 4 +- media-layer/proxy/CMakeLists.txt | 4 +- media-layer/proxy/src/server/server.cpp | 4 +- media-layer/stubs/CMakeLists.txt | 4 +- mods/CMakeLists.txt | 44 ++++----- mods/src/chat/chat.cpp | 8 +- mods/src/chat/ui.c | 25 ++---- mods/src/compat/compat.c | 4 +- mods/src/options/options.c | 8 +- 31 files changed, 353 insertions(+), 271 deletions(-) create mode 100644 libreborn/include/libreborn/config.h rename libreborn/src/{ => patch}/patch.c (99%) create mode 100644 libreborn/src/util/elf.c create mode 100644 libreborn/src/util/exec.c create mode 100644 libreborn/src/util/string.c create mode 100644 libreborn/src/util/util.c diff --git a/VERSION b/VERSION index 276cbf9e..2bf1c1cc 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.3.0 +2.3.1 diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 151aebc8..b8e69b19 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +**2.3.1** +* Internal Refactor Of ``libreborn`` +* Remove Use Of ``/bin/sh`` + **2.3.0** * Switch To AppImage For Packaging * Prevent OpenAL From Crashing When Out Of Memory diff --git a/images/start.png b/images/start.png index 22ac89829d9ae9d26caac3b4e5cbaa15cc581140..8e775c90a40325e4767fe4a812d58aa2368cd13a 100644 GIT binary patch literal 31454 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�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ᦪR7NbrvR;yf4<#xZv{cjEi1&SY#!z8|mi#vm+ z9N!(!r@`3m&0aR;kNE>!de+##wW#WO7fNi?i=8u4KlYv9W2cU4c!!(FT-Q*mBvbNc zdyg&!B|8#-3EF?@UugMb7c%iV?1HV@AIRy&je1i}Dwyks%T5J7$wDQKG#b;tG9Kz} zL^m)nWK6Ccm3?t3_JKF6Z?Q^!`sUuE5fs86a66Q6XlW@je_0QruqE$wgMf!T13P;Ct-gQ@40 z=icZc%mgx`ug!Atz+i(Lrp>Fg150BNbJHxkS+wsidDt)OD_+F@?2`!HG@`fdU_*E8 z?1Qj(BQQQnd>q>{=6<{{FkFCQ_*OJ@)NlnWkGyecAD-YP{6w-w% zS)0IgA;Lb*Et>CbG;itAmfNOHNjUH_=HWwDdEpli@!mF0?Wri-M)4o?>4JOSIT{$f z?OUgQ9l7|1Z|d*K&~I$*)m&k3{(-F#wY0Cs`2BryA9oXeeUn}Dp5q-awiUfbvmt^E zo?08R%Aqul9xZp=`gzj#ll^3aN&HZW_~O_IXhmhQ8RC=(Z;HT2N!`R){HB@Z@ar;G^1Rf7E_8cB8>9(?P~P*vl(@i|MDzgXDG$&E}<3s$3%GlW6)(xSx!g-6%AD}N=tXMOGPKeYgNu#@Y2 zyz%?mgPO@e9gzp!$6vB;`9_KaABTsPR{Y_g5{&Gf>vbqC5Vu8iLp|p}Z-%f9H$iP9VYx@3z%{lg(di`2uULbj^UdE{&73cO z;ViU}*UlROj1~0R*XaN-u3+))?qj)}agR*5xH`W~88awJt&9cFI|{F@sNaRphbe__ zcDq}xKwGoT@!l`VO+=wFM8#}X(wrmvb;8wNq8)n_hCv7w>Ts+ka zCPiB=VDPBsR`lQ{IAqif>&I6KbO#qvkp2+e4?2#52#J zN6XiBhVw(TuKwICan%XGGBiYKHTk0kcu>_{u1o3@ZMN0>Uf)mWnh_Q|bWu^4wWK(D zQU$R|R_$I>AvkmVzGVHd=O}Z0+!)n7I!bmYIhluj-k~BC=SPtv!!1^;*~1PUpCDxI zCEIg%cVwyeR+3%K@|Y#!{3i3#bE1!2&fU4SGj;V{Rn|$(DbeS3z+2C})_TYoMsa#I zrx(@J!!wW01wgIDRkw4K6tFy`q!Z8ckh?KxIN%~S$y=81f9u?&?_{u|wx7+*^Lmo{ zTs|;Xm%d9+#IbL3+%PklrD~i9PyO5%K+Aja>c3ve%S8b+QM}mU{&M@?3rthUIw>Kq zexIW5xgE6*`S-yzR$fm)v_e!KeDY&Huj&4Z<7SVYGUF(9xO6j3+D(;!NMdWtFmRPO=U)1K ziDlyOi-`=~1WV5efwv^gj9;nlJPIrym+rz6p06_3g|V@H%AmcflUV;))jvgp@<%h| z%X~6>n4t<~gbt+q-l?}p377k%pjqIMJTS$7e-GG`{8cjlqe^SEq#4d(>8w@GR5B-X z9i4d2mCG)d=PKoXJH2x+fbyodJ*&AmjkEYP&eRB*4iM8Jb0bzZVrwQYogwezc@f`- zu2(t<zz?)3{gPN=+i_?xsJq#!6q2r7r2UW5r1^uq z9q$ay0&O-zEvLbQi zr4%0N0v@X(U;HpxJKg(x_l1m_b6@?KCg!9!w$Cmcd7I39O_w!+12WJ0Nn5gIjpPht zS1l}_-1)B9e6B-WuJOs&SA$K5Xm4Ygxk`}nd(C`RO-oO%6+_jyr%WLb0^BMNJqA*^ zi(1@kJm1gqkA4kmreGgcl?3UO=jwvY>QMbL&6l{p*X>^2cuVOzd-__J$Y5{?C9x@f zPqLW*gyaSl)wxMnu8KPv&ns9>hZW?;XyF&)bIQPMgswxTPGzm1+gjvnM-uubu`o67 z{eyF*qm{!uBU3@tzo>+u@vR_s$O{TTIRdcxU#&;airnx&`;HU^D%yKZsX(s))Vz5* ze)utq3_zrLP-9~FqpV$s!=7u~`+mhr;&La6Kw6Gp=+dbMuFTbElB?&jbv|_QFc<2k z1UGr*qN9Lg(XN|qQ`8(sN5NQKr|*>7AgPFAOa-~b9xJw zm*{8FTMy=<`dhj|TE+$BInqs^V+-TuFJkU&j%7tBroGlU0AY-V12T-Tg`_g2Er6V? zJIf|_aIpUpkm#)x>o7@K%kfp|sGk0F=|2Tkhr~VLRdr1aVzXXP4$1n=Tv>~^@-mDi z{Gg-!x4oeS_xLY6-CsE0rBxYq3i+C;ST^|_Or8PkM*yuMUR|Wapx~a@*06J3_|)24 zVS$E3US1ywD25f-fLL-bfTXFT$c{uHtPF=$$DpWVq4&#|iyn9mr*A$Lq326iPA<88 zCseIUK;fb+iC%JFys-Pcqgn1RyDHhvWGe?fl+3;{9jZ?nmXEMvoqowsm7Czzsti%w zW8W$!R-ObTr;#I?^1#n^H`RO41lfEm$#I~Er#G~9RH(X};LsdTDe`aK%UhEFJWk2b z%Pf%P)$5rc%~m({g_*mNvB9LCSr|o#O)X-*+V@!c6PK}eQsq$k5>zrN7+7H;|5D&z zfT@ptUQXkpob$A6YC3OJY22S;KOHx3op7;zuXtF~GJZNkS5J^DU8J;X%9{EfiQjKm zMR8Ygs}j=rS{=Qb=8HtSzlKaWh~c!CxN`+ztO$naK(<2Z z5kCMzeBUM%!0TxEzKc9WxTwWDohw7%9Dg!%PixI{P52zMwu`KkmN0#OxQ`ur$$D?B77q_xi~Yw1U1J8)R!w8D>1u%=Z@X#XbP1diG5)Y^5g~d$P$Ys znE=viD6R0y)-3Pi(2T$5f5dax<%x)lgW*e8Pv;CF;5`V>UQUfwrsrM;PdSecWL%k+ zid|i&XR5l#(z4o;3%+jc?J!C;elGS=y}H#g18PnDwo$Nd&&N)HYSU4nR=tZ!IC*dO z!d4(4n5|?}UR*Iz93vlg7$!wdNeI%b4#EszJ(QU$t>G;mYGLqgEL80H@R1AL`;7LA zr)LGseNY(U{o`EhVfm(OZ>O03fm6HKx1+Sjfi3G{zE8rQ+_xAUZ4I%VE?Rv(DaIH( zE_{bkJJERkmsfMyIZCbXL;{m8Br(@K?p?&NFPJ8*jN|^ z@g$A0y5nCSfI*;s#TL^a4CMuva4l>1{7KyQO5`CsKfSmqEwZCRASyz73F^vrBtA{= zn$8cTTZri0SW`OckiW!DVk?{Itrg~zng$OjjNVGz0tDCUGk*Z3*$QelZvk&~zcfNN zb5T3fAFq}K8XY>_U7{J0%V%AtG8F0*%bk_a3LuFN0hG zxS4k~A-JhhzE&Klw(g2x9{;-fg&-?gjN>Ke(^<#%VUqCI6~spR^HzTV?92(jRrb}( zvB^>~20-ZiQTl>rBXyIcQ=sD5V0aZ2ZGG`lqlmMTr!qe|Q5t64Q+t%jp5@5bQR0+^ zz_>~e)!JG@K9xnhgOMM>rahk$f(1cZ7oeVv0((((`g^R0apyQ!>_%EkN3EQ`-Kt(b z1jn*U7Y>HSPAti5H?s$#tOX{3%1ug|P$*==NS%``#+UTVY9YlCi4l>CV~H_Qg+v-i zY?P+GJ99}RTUP1{tznhX5qW<;VVZ?dW=N=})%w=%a}uHHg2Tsmp!nikM<)w`du1qv zJql>&!-K?>I<$KJMWGNSwm{KrbZ9pa{?P-kjJ`Ui1h1R=XY!T-GH}2jb0^T!*K@N_ zp9E{_iOS23DV8jo$5M~AsZXZpVQI3Q#f0&~7ms44(|O($^XYt(lKpG(6A?xpjN zJp8c>xzw#%sW``ED7v@fs>HP>*v??!42`wLkfw9?B20tt-IQm8u|=xT_#YnO18U** z@8xp(kM=v*>q5&{b?n1YQZV<)=7QV{0*O@D<`)M+`Zv`(_dv3%e1V?`_O2&J@j@{7 z-0hA5lqlf%-iXa( z{4NmgTxdOQ?qRLIY#F$Vgw#zOIdfd;ku1*Byvi|LOMVtU2p_^pLFvh{M;9il3v{!_ zf+?7{*cW=7*N;*2MDRWB*NKiIf)cnQ($vjgiY?fjd zWO&ZH5wB>Yeny1@uWHEPGBT%m$osYUFJTEndEv#!Q-ag7i8L$Pf<<9vkmh8V&%A>* zhhUWkb6|u*<`9QV7JzkBYydgBFwn8mY*4mjo<3d>|3Z zp|`pt9a!isONL^!47+b}g^r3zqEv&7;YjeqHWtpa^|Wif^LvD2eusyVXHHJ2QQlfx zyXdk4!d>l@T|vYuPFuJr%tbM~@OQ@Swv5b^9lru`o7b>G!=c!;>=p(2{yVNfs;ZUB zuyP?ZaU{ItMZ~Mm;4QpvMYbmkc`1NRnEQ;=xjXCm-iA`os}xyu5nkpC-C5-?Av=~@ zf2?hi#Ewr`^Y~*!U^Ssg&Zao(FN7@cM?rZuuv+ClaUnu-X2t@OR3m`jQIHu5KPjhS zheswK*e(9_jJ??NU94u7s9rG}M-y*)UG4L`VECB2P#?R8f4PRbe> z8t9-7RyLzdnb9CY0buhwcddEWeR;iqzU^)1N^8EqD3>DE~l>DMUmc4a+pm~8??E}HhjOIB#wrdC!M*_-2Yvfe_7G&Bx zmss|UsaK$gZx}!_vMovO)b3_Q-q;^bXr8!4KtSh;SU*bL(XE~mwjy=e#lCe%v0=2zydGn7x2e{3K!6jLOY zr|X_w8ZVV?ftU+C{tblIz_z%ZkLS%gWc-(nO^72qtLKcq|Y8+}~sYz<9f+gWBHp zE!|W&ccw@os7bAN@=q=@zTo|%n%VHj<`HJa@!`Sha_vB#7g%I~Wl7i*6YA~BZCNk* zgSI)V2-6NKG&9B?{0zJiwy*H&pKWnfOY=*pmwz)Utz-YDZ>xcLc41qJ=fJ(gHO;ng z^1#3p7X+GXZu(vm2m#V*QF`*}u?@~kg_*WXrXQZ5YMD2m= zd}nz9!CC7_V_WN8rgzzm2fkEQx=r|XvmpzaS?FA#CksqhWd$+2&sgk6?!nK_ZulSA zz4VCZLw(%v$i|*KE%LN^Hr1M4sqd0L(~DW95p1>|)*H{973C@Pu}nY0S#FM_>m7|S zw4Xvk<(L&#zVWjmb^3ZtYdFF~xuS*2Dx4v;G0l_D-D3$(2Ya z4PFg6yo5%jt62t6(j(b)^3cOFB>qprm$zLdu&^rg0w0!kkB=sc1`Re^m#gJ#|4=i< zJj4pvh=XisxQkL@@$J-74R3Wum4=6Zd<+;BLZU|ixEA4$OQJdNPCr5V$8bl=j;xPP!O5x8n znOB*Id@W7Q`Y9`%s%FSa|J~Kj-GIMd7NuqS95=MZF2Zo=)|`YAkSV9f0iOCm(}tK! z=PB9RGGW#97mxhjISaw%&e23}?b5dEX-cbDDAKL$1FeBNsjdRIM{bB-| z^{eQQhX=j)O|V^t4wBM9qU<$a8ghU;@|L7zK1JIh*HS}p`(xj%4S zOQ#pTJiC!x4Y_=e^&wB%qsNl|M{s{pCRbDwU;7inlxR*pPT(0A81`K?$pfs`4f^WA zt2Ev#40PFtuTg!M&HRMKP{Mt)19Jg=(q-61g2mGTtC%0fT|4T~xakQBx zTy>~e!maMX!#YNg$CAxW$WYGQa8TgNK13psF%X7S)fYWX+g+u*y!33mn-P>ShDZxs zna#AC2xFO`dR(Zf~D&iXWu@;<#@aZJpWQt8Wfa@7<`;P`bue780F z?@(ce;Py5l~pm1b}3GQA4Kkf`S>Vc2a$s-5*yTXzR4Ow{56r8}fmN8*<>^d>)YT(_9bp zc7U*F8NHE0{$0dtaKbp}2}NZdl~5P!;_c)#J@_#9QFr~#fX&3w%e|Z&V9ES5Rf`jQ zG{ti+CEf%&=A#MIq&I+vrAB5A3m*oGb#-;wFIB%ZJAMYet(QskQWy@rn{_Pr#$s(g zdMM=fB5c@;8GJp}{&h8IK*r3~0HZpJ`|uJ~AKzWYfw{X%Z^8P)&>pmg$nqHZb!J@v zK7{!KH!QIi=vc{mEt46GIE3RY-xx4rSE+^JuRh#*A>&TU)k_@i{4 zp8Fg}8n6G7|Fm-tWoE}Hl_h4jgZb9l_h?3g1C0(;oII+*TaxLN20Xg!U0sf2@?8QC z>j!SNi2u`V8s91m;@CKeudQpvXdFd?Cd4`x3Vt2j;|8#_(dHv|XgAuTDWoI> zM^B&f&!oW%>xy;D_r-cwEshf9z{F}=^93@MMjR<47I4TDVi%FEo|cvt`mj$+`guHh zVr9?NnwVqR`4Tf*Ga|z&@5H&0xAH3OKk9xN8U4c1DyCEsE;N~8`I9d2UJ~o*u&dpO zJ4F>;mP9?FTXPXFgDNltQz&+6mQJDBjBIUf7rb8v3fwc$*bXn`P!aJ`{iYu---Jf$~fIC%nCybsnZ4g=Y`-IY3C3bhbcHo%E6-5(np zv(Gm}IDVpj{V4W6Ti~E5%J{q0bwRh;(Vy~!*ym~EDI$|(bYwd-mR-%9BJISM6iU4h z=OCs%smXDF7OHkEz~w~r!MpreKYs7&eQV!Ec-|-Uxx9ElU9Ml@63acapjE7EpWjfj z+1%M6a~z!?A_-%1^hf!81I@Y{nZ6M*FBVZem#4arCt+ePK>P&A-ozsvjduU6*x2#p z6CRcA+WwK02uxTFZnDFpDtVy`@M(*MgY&vmX%sx$yZNf62~u;FwwL3NWz$Cil9G!0 z)~&vxjXZ+YpJ?D(&Xj_W2od z%iTikz=G&tY0K0uZK)-)>>xE)IZL3_N17ualLJ__Vt9pwS@nW4vs7DeQrP`h6?PUp zo@0KD_QwB~gx%a_^8Ln)ZHintst=f~k(EF>+Q;L|xqgDmd~qR!gP{r%zPGXY4NQ@e zTwV`JQGHQ`MtNh6G~nbmAB)OlyKjb5qKI|mv+FLKwawqs3d4;!HrjDlljwxV3V3c#G(|X+-S4MlB z=z4exZMvlIbAmi-27;xM#hSGs-Zoeurs=V+s6(~?{vMNAAY+u3gC*p1Ieil1^8jM{ zA>!n1=JxX(&1bCp#G3c36=%N=6!eV0ci?7!m!{ft1^|QT9;$v{^jzCv5*_s~D0C#c z2x@ue)hxqzx&);FNJwP3ig*r>+MkK(>AyX1a%zCQl;yef^&i_o;xvsR8^C})Z;E%Y z!{&6I7_&S z7yh!mZLDuwR4;F8D=pXZ(xRyguz7fkRxL3C@s*C^QL=yg`QwwoQWcq1bz`Cn9m6g{ z)Y}iajMCRt1=PQ|ALEDYyc_v@5^^KBh|BXf9Px?OO_$Azj8>eHdf|?0+Mi2QHhJ{g zp51L!=GFI9R`Pb-14C{Mq52 z+!55x;RN+<3=S${++?)r{K_nL&Ti@x?mvkwj+6hLODdhfwH9MnK3=%UTJt-RK6RTT zX+m(tKwo}F-z$pEInSItx-5Q@t%8U$H-zHQ0Fb}yF?+9N6qwtizgOXIzooo?{Zo^kkzC#E*VyH zp_vrIwFh}Z5w#`q&$Kw=?{GzEG1@KgjgHpef(Tkz5<7|Spc=pjfq+gL6R5`$cO~(p zTz{meb~?`hc!t)|$w~a0+CBsCf}8=%=v=I)Y-0xi(BoPD9qByu{aaMl1Qb()g46DY4&{{oZ`b$2gn zNiE=fsOOWJdSu9w#6DVGPT!Bvz?M?i>2h;+76>PR6uSA9FPE=xHQH*6xbT6JqtUG&@=bO#>KQX+i#_jwJWc6==o1ZpC%Cx+v#99-? z1Qvoxm!_vb)sEa9YY5b8>%z;Y_ezL%IXIk;TcJfJXa+I8 zvXK2mc6uWTyr`)KluH|c*KpBCpNh{u6+X4zBDE{w;e^!G4_Vac9#L}MXJgFcMerNh zi2qM5z{1w#-pg$|esb@)^gWCQ3N)4KI^Sr&)LTRYbAMkn^-BEvw&p3i9Av;mhVW}x z;HqEXY9~3r2H48u67|dv>`5%wc(0~=pfU{a+8z#z~e@12gjNYeqIJ6Fn(rD!Yn zFotOL=lFmw-asf7m4QS+eikf4?E2t4%g&gcK)^=15W4%NkkBM0==)LkVQIWbu6I;+jxehTAWPJF*~1rAxGXtV8$!S zmBQh?@b`4sj{4SDKV!h=WG#%B&+qqBngHMDT(+y(3+V2$oo)nDZ&&8&{lu}i`3Nr! z4Wbb0VD}|#Yby@8nYSKx{AqL6=+^C@A>iK&q&1_7u#R)+(f{NjMRu6}3R&@L`$!n$YzoY%?N9*Z=fTQcjBCyQ<)}s}t~9|#dmTgQYIk?2 ztI$80J5pDFyAA;|n`eG1{)^*m^@l^F)2JVf->!a-hCb?!&D`do_boVt_+(*W)D_=kR_bjtn6AsN40F=Zw^@|EU&|mrsuWsn}W+ zu~`(cZ$`f{8v9O!#~r=|*N>I2E=@l8`5j$N@~pY3Su_RmuUYmuG&W+Ae{J5wilBf0 zzdC#Kc&OJle!R{(oldJZRMH%4BorcKRFZv7CK-ezdyGN0X+w4*vQF7cM%l+QbCL)# zSqFnT)?vn&7-O0B`UfNy>dra-$os!(^j!S2G$;KK2=`k`FJAvI+ae@9Mmp_3BhS?y1Vk${L(q^ zQg^Sbr1*8uu{S%2XqGo(qhHo*)7|{pkB3)mtSS2)>IC`yijWz_ry?H1(l5Pg7l*E- zz4`n;z4q77`wBj42j392RrCton^n`ez|$s&p1Rh}M_6}t0_ZwR??pAx@&>exho2;5 zgWqCrIdJ-wCxcZfbCm9^kdyD%%*HIAC0^v$bS$j9L{`mV1HzU4`-!LHkn`PkH z=dlnQ8tD{bg=UBhDy1epx^()>J`te408}3j!wHWDVs~Y!ZEQOc>G#mctKsO2fPNh-ilc)a zCgdzHD2Q7Qv?c+p^=v>gCWOcWj$+y26T=Yg%#hdYJ-XCT~x6= zRgeUbYyJ~F{?6>?zwCl!??uXM{pPC@mpSlG-&p=obX$jcR0^;YQUz#0?%gs3J(rNW zW2)!VyvXi>fRB1ggxzT!##h@bQ!ZX)Jp8vY-yEGFlK}-wdWCQReC-cPgUd$JV%$tt zt4Mla;KJ<@RQCJ#jSh>$rUjnzvnOOUHO4CPzvx%q)po856enIU@eDohCt&UjLW1fDhKt5_nh zI29*GJ|?qk5|tf3?%5lEX{YR$V?6*p4xcCgV%2)#bgy94wLe1oQbcc8l`4JwaLg9# zD9=AfWGA16ZQr}LC7=xY8gHmIsnXA~O7sH6K1Gt50&gJ_(o{Hl;a2oSW#mxs9kYdFf^&H(b+unnMU z3H~)@LHYkGk#W)zfcPNB>l!}|xkJbK%7aouS*UEO!Iy4FYW&6HSai?mZzzfp^oyy= z6<^?2c^mJrs=@_ma$c=onol2B&KkUc4afDuBx*~69tdc3?Jh5P_;IB;9>2?lMe$M? zP)1ETxTgebanp$`Wng1c96A@V;ye)Pf3^+I?V&i+!&3X3m&!|v=~%8^ihf(P35(KJ z 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 callba } } -// Run Command And Get Output -static char *run_command(char *command[], int *return_code) { - // Store Output - int output_pipe[2]; - safe_pipe2(output_pipe, 0); - // Run - pid_t ret = fork(); - if (ret == -1) { - ERR("Unable To Run Command: %s", strerror(errno)); - } else if (ret == 0) { - // Child Process - - // Pipe stdout - dup2(output_pipe[1], STDOUT_FILENO); - close(output_pipe[0]); - close(output_pipe[1]); - - // Run - safe_execvpe(command[0], command, environ); - } else { - // Parent Process - - // Read stdout - close(output_pipe[1]); - char *output = NULL; - char c; - int bytes_read = 0; - while ((bytes_read = read(output_pipe[0], (void *) &c, 1)) > 0) { - string_append(&output, "%c", c); - } - close(output_pipe[0]); - - // Get Return Code - int status; - waitpid(ret, &status, 0); - *return_code = WIFEXITED(status) ? WEXITSTATUS(status) : 1; - - // Return - return output; - } -} // Run Command And Set Environmental Variable -static void run_command_and_set_env(const char *env_name, char *command[]) { +static void run_command_and_set_env(const char *env_name, const char *command[]) { // Only Run If Environmental Variable Is NULL if (getenv(env_name) == NULL) { // Run @@ -93,6 +52,8 @@ static void run_command_and_set_env(const char *env_name, char *command[]) { } // Set set_and_print_env(env_name, output); + // Free + free(output); } // Check Return Code if (return_code != 0) { @@ -107,7 +68,7 @@ static void run_zenity_and_set_env(const char *env_name, std::vector full_command; full_command.push_back("zenity"); full_command.push_back("--class"); - full_command.push_back("Minecraft: Pi Edition: Reborn"); + full_command.push_back(GUI_TITLE); full_command.insert(full_command.end(), command.begin(), command.end()); // Convert To C Array const char *full_command_array[full_command.size() + 1]; @@ -116,7 +77,7 @@ static void run_zenity_and_set_env(const char *env_name, std::vector #include +#include + #include #include "ldconfig.h" char *get_full_library_search_path() { - std::string output; + std::string processed_output; // Run - FILE *file = popen("/sbin/ldconfig -NXv 2> /dev/null", "r"); + int return_code; + const char *ldconfig_argv[] = {"/sbin/ldconfig", "-NXv", NULL}; + char *output = run_command(ldconfig_argv, &return_code); + std::stringstream output_stream((std::string(output))); + // Check Exit Code + if (return_code != 0) { + ERR("ldconfig Failed: Exit Code: %i", return_code); + } + // Read int running = 1; while (running) { - char *line = NULL; - size_t length = 0; - if (getline(&line, &length, file) != -1) { - // Convert to C++ String - std::string str(line); + std::string line; + if (std::getline(output_stream, line)) { // Remove Newline - if (str.size() > 0 && str[str.size() - 1] == '\n') { - str.pop_back(); + if (line.size() > 0 && line[line.size() - 1] == '\n') { + line.pop_back(); } // Interpret - if (str.size() >= 2 && str[0] != '\t' && str[str.size() - 1] == ':') { + if (line.size() >= 2 && line[0] != '\t' && line[line.size() - 1] == ':') { // Blacklist RPI Legacy GL Drivers #define RPI_LEGACY_GL_PATH "/opt/vc" - if (str.rfind(RPI_LEGACY_GL_PATH ":", 0) != 0 && str.rfind(RPI_LEGACY_GL_PATH "/", 0) != 0) { - output.append(str); + if (line.rfind(RPI_LEGACY_GL_PATH ":", 0) != 0 && line.rfind(RPI_LEGACY_GL_PATH "/", 0) != 0) { + processed_output.append(line); } } } else { running = 0; } - free(line); } + // Free Output + free(output); + // Remove Colon - if (output.size() > 0 && output[output.size() - 1] == ':') { - output.pop_back(); - } - // Close Process - int ret = pclose(file); - if (ret == -1) { - ERR("ldconfig Failed: %s", strerror(errno)); - } else if (ret != 0) { - ERR("ldconfig Failed: Exit Code: %i", ret); + if (processed_output.size() > 0 && processed_output[processed_output.size() - 1] == ':') { + processed_output.pop_back(); } + // Return - char *output_str = strdup(output.c_str()); + char *output_str = strdup(processed_output.c_str()); ALLOC_CHECK(output_str); return output_str; } diff --git a/libreborn/CMakeLists.txt b/libreborn/CMakeLists.txt index 6bddb827..3739236b 100644 --- a/libreborn/CMakeLists.txt +++ b/libreborn/CMakeLists.txt @@ -1,12 +1,12 @@ project(libreborn) -add_library(reborn-headers INTERFACE) -target_include_directories(reborn-headers INTERFACE include) +add_library(reborn-util STATIC src/util/elf.c src/util/exec.c src/util/string.c src/util/util.c) +target_include_directories(reborn-util PUBLIC include) if(BUILD_ARM_COMPONENTS) - add_library(reborn SHARED src/patch.c) - target_link_libraries(reborn dl reborn-headers) - target_compile_definitions(reborn PUBLIC -DREBORN_HAS_COMPILED_CODE) + add_library(reborn-patch SHARED src/patch/patch.c) + target_link_libraries(reborn-patch dl reborn-util) + target_compile_definitions(reborn-patch PUBLIC -DREBORN_HAS_PATCH_CODE) # Install - install(TARGETS reborn DESTINATION "${MCPI_LIB_DIR}") + install(TARGETS reborn-patch DESTINATION "${MCPI_LIB_DIR}") endif() diff --git a/libreborn/include/libreborn/config.h b/libreborn/include/libreborn/config.h new file mode 100644 index 00000000..596d4fdb --- /dev/null +++ b/libreborn/include/libreborn/config.h @@ -0,0 +1,3 @@ +#pragma once + +#define GUI_TITLE "Minecraft: Pi Edition: Reborn" diff --git a/libreborn/include/libreborn/elf.h b/libreborn/include/libreborn/elf.h index 5922ee51..fd62ef2a 100644 --- a/libreborn/include/libreborn/elf.h +++ b/libreborn/include/libreborn/elf.h @@ -9,55 +9,14 @@ #include "log.h" #include "exec.h" +#ifdef __cplusplus +extern "C" { +#endif + // Find And Iterate Over All .text Sections In Current Binary typedef void (*text_section_callback_t)(ElfW(Addr) section, ElfW(Word) size, void *data); -static inline void iterate_text_sections(text_section_callback_t callback, void *data) { - // Load Main Binary - FILE *file_obj = fopen(getenv("MCPI_EXECUTABLE_PATH"), "rb"); +void iterate_text_sections(text_section_callback_t callback, void *data); - // Verify Binary - if (!file_obj) { - ERR("%s", "Unable To Open Current Binary"); - } - - // Get File Size - fseek(file_obj, 0L, SEEK_END); - long int file_size = ftell(file_obj); - fseek(file_obj, 0L, SEEK_SET); - - // Map File To Pointer - unsigned char *file_map = (unsigned char *) mmap(0, file_size, PROT_READ, MAP_PRIVATE, fileno(file_obj), 0); - - // Parse ELF - ElfW(Ehdr) *elf_header = (ElfW(Ehdr) *) file_map; - ElfW(Shdr) *elf_section_headers = (ElfW(Shdr) *) (file_map + elf_header->e_shoff); - int elf_section_header_count = elf_header->e_shnum; - - // Locate Section Names - ElfW(Shdr) elf_shstrtab = elf_section_headers[elf_header->e_shstrndx]; - unsigned char *elf_shstrtab_p = file_map + elf_shstrtab.sh_offset; - - // Track .text Sections - int text_sections = 0; - - // Iterate Sections - for (int i = 0; i < elf_section_header_count; ++i) { - ElfW(Shdr) header = elf_section_headers[i]; - char *name = (char *) (elf_shstrtab_p + header.sh_name); - // Check Section Type - if (strcmp(name, ".text") == 0) { - // .text Section - (*callback)(header.sh_addr, header.sh_size, data); - text_sections++; - } - } - - // Ensure At Least .text Section Was Scanned - if (text_sections < 1) { - ERR("%s", "Unable To Find .text Sectons"); - } - - // Unmap And Close File - munmap(file_map, file_size); - fclose(file_obj); +#ifdef __cplusplus } +#endif diff --git a/libreborn/include/libreborn/exec.h b/libreborn/include/libreborn/exec.h index 0b6106ea..9c1d5b31 100644 --- a/libreborn/include/libreborn/exec.h +++ b/libreborn/include/libreborn/exec.h @@ -4,60 +4,34 @@ #include #include #include +#include +#include #include "log.h" #include "string.h" #include "util.h" +#ifdef __cplusplus +extern "C" { +#endif + // Safe execvpe() -__attribute__((noreturn)) static inline void safe_execvpe(const char *pathname, char *argv[], char *const envp[]) { - argv[0] = (char *) pathname; - int ret = execvpe(pathname, argv, envp); - if (ret == -1) { - ERR("Unable To Execute Program: %s: %s", pathname, strerror(errno)); - } else { - ERR("%s", "Unknown execvpe() Error"); - } -} +__attribute__((noreturn)) void safe_execvpe(const char *const argv[], const char *const envp[]); // Chop Off Last Component -static inline void chop_last_component(char **str) { - size_t length = strlen(*str); - for (size_t i = 0; i < length; i++) { - size_t j = length - i - 1; - if ((*str)[j] == '/') { - (*str)[j] = '\0'; - break; - } - } -} +void chop_last_component(char **str); // Get Binary Directory (Remember To Free) -static inline char *get_binary_directory() { - // Get Path To Current Executable - char *exe = realpath("/proc/self/exe", NULL); - ALLOC_CHECK(exe); - - // Chop Off Last Component - chop_last_component(&exe); - - // Return - return exe; -} +char *get_binary_directory(); // Safe execvpe() Relative To Binary -__attribute__((noreturn)) static inline void safe_execvpe_relative_to_binary(const char *pathname, char *argv[], char *const envp[]) { - // Get Binary Directory - char *binary_directory = get_binary_directory(); - // Create Full Path - char *full_path = NULL; - safe_asprintf(&full_path, "%s/%s", binary_directory, pathname); - // Free Binary Directory - free(binary_directory); - // Run - safe_execvpe(full_path, argv, envp); -} +__attribute__((noreturn)) void safe_execvpe_relative_to_binary(const char *const argv[], const char *const envp[]); // Get MCPI Directory -static inline char *get_mcpi_directory() { - return getenv("MCPI_DIRECTORY"); +char *get_mcpi_directory(); + +// Run Command And Get Output +char *run_command(const char *const command[], int *return_code); + +#ifdef __cplusplus } +#endif diff --git a/libreborn/include/libreborn/libreborn.h b/libreborn/include/libreborn/libreborn.h index fc969c18..35766b08 100644 --- a/libreborn/include/libreborn/libreborn.h +++ b/libreborn/include/libreborn/libreborn.h @@ -1,5 +1,6 @@ #pragma once +#include "config.h" #include "log.h" #include "util.h" #include "string.h" diff --git a/libreborn/include/libreborn/patch.h b/libreborn/include/libreborn/patch.h index e21eded0..268e60e8 100644 --- a/libreborn/include/libreborn/patch.h +++ b/libreborn/include/libreborn/patch.h @@ -6,7 +6,7 @@ extern "C" { // Patching Functions -#ifdef REBORN_HAS_COMPILED_CODE +#ifdef REBORN_HAS_PATCH_CODE void _overwrite_call(const char *file, int line, void *start, void *target); #define overwrite_call(start, target) _overwrite_call(__FILE__, __LINE__, start, target); @@ -23,7 +23,7 @@ void _patch(const char *file, int line, void *start, unsigned char patch[4]); void _patch_address(const char *file, int line, void *start, void *target); #define patch_address(start, target) _patch_address(__FILE__, __LINE__, start, target); -#endif // #ifdef REBORN_HAS_COMPILED_CODE +#endif #ifdef __cplusplus } diff --git a/libreborn/include/libreborn/string.h b/libreborn/include/libreborn/string.h index d04573b3..5679ef12 100644 --- a/libreborn/include/libreborn/string.h +++ b/libreborn/include/libreborn/string.h @@ -26,26 +26,13 @@ } \ } +#ifdef __cplusplus +extern "C" { +#endif + // Sanitize String -#define MINIMUM_SAFE_CHARACTER 32 -#define MAXIMUM_SAFE_CHARACTER 126 -static inline void sanitize_string(char **str, int max_length, unsigned int allow_newlines) { - // Store Message Length - int length = strlen(*str); - // Truncate Message - if (max_length != -1 && length > max_length) { - (*str)[max_length] = '\0'; - length = max_length; - } - // Loop Through Message - for (int i = 0; i < length; i++) { - if (allow_newlines && ((*str)[i] == '\n' || (*str)[i] == '\r')) { - continue; - } - unsigned char c = (unsigned char) (*str)[i]; - if (c < MINIMUM_SAFE_CHARACTER || c > MAXIMUM_SAFE_CHARACTER) { - // Replace Illegal Character - (*str)[i] = '?'; - } - } +void sanitize_string(char **str, int max_length, unsigned int allow_newlines); + +#ifdef __cplusplus } +#endif diff --git a/libreborn/include/libreborn/util.h b/libreborn/include/libreborn/util.h index b8372a49..186dbafe 100644 --- a/libreborn/include/libreborn/util.h +++ b/libreborn/include/libreborn/util.h @@ -43,9 +43,13 @@ } \ } +#ifdef __cplusplus +extern "C" { +#endif + // Safe Version Of pipe() -static inline void safe_pipe2(int pipefd[2], int flags) { - if (pipe2(pipefd, flags) != 0) { - ERR("Unable To Create Pipe: %s", strerror(errno)); - } +void safe_pipe2(int pipefd[2], int flags); + +#ifdef __cplusplus } +#endif diff --git a/libreborn/src/patch.c b/libreborn/src/patch/patch.c similarity index 99% rename from libreborn/src/patch.c rename to libreborn/src/patch/patch.c index 09fbc9c8..02f467fe 100644 --- a/libreborn/src/patch.c +++ b/libreborn/src/patch/patch.c @@ -11,7 +11,7 @@ #ifndef __arm__ #error "Patching Code Is ARM Only" -#endif // #ifndef __arm__ +#endif // BL Instruction Magic Number #define BL_INSTRUCTION 0xeb diff --git a/libreborn/src/util/elf.c b/libreborn/src/util/elf.c new file mode 100644 index 00000000..a4498437 --- /dev/null +++ b/libreborn/src/util/elf.c @@ -0,0 +1,53 @@ +#include + +// Find And Iterate Over All .text Sections In Current Binary +void iterate_text_sections(text_section_callback_t callback, void *data) { + // Load Main Binary + FILE *file_obj = fopen(getenv("MCPI_EXECUTABLE_PATH"), "rb"); + + // Verify Binary + if (!file_obj) { + ERR("%s", "Unable To Open Current Binary"); + } + + // Get File Size + fseek(file_obj, 0L, SEEK_END); + long int file_size = ftell(file_obj); + fseek(file_obj, 0L, SEEK_SET); + + // Map File To Pointer + unsigned char *file_map = (unsigned char *) mmap(0, file_size, PROT_READ, MAP_PRIVATE, fileno(file_obj), 0); + + // Parse ELF + ElfW(Ehdr) *elf_header = (ElfW(Ehdr) *) file_map; + ElfW(Shdr) *elf_section_headers = (ElfW(Shdr) *) (file_map + elf_header->e_shoff); + int elf_section_header_count = elf_header->e_shnum; + + // Locate Section Names + ElfW(Shdr) elf_shstrtab = elf_section_headers[elf_header->e_shstrndx]; + unsigned char *elf_shstrtab_p = file_map + elf_shstrtab.sh_offset; + + // Track .text Sections + int text_sections = 0; + + // Iterate Sections + for (int i = 0; i < elf_section_header_count; ++i) { + ElfW(Shdr) header = elf_section_headers[i]; + char *name = (char *) (elf_shstrtab_p + header.sh_name); + // Check Section Type + if (strcmp(name, ".text") == 0) { + // .text Section + (*callback)(header.sh_addr, header.sh_size, data); + text_sections++; + } + } + + // Ensure At Least .text Section Was Scanned + if (text_sections < 1) { + ERR("%s", "Unable To Find .text Sectons"); + } + + // Unmap And Close File + munmap(file_map, file_size); + fclose(file_obj); +} diff --git a/libreborn/src/util/exec.c b/libreborn/src/util/exec.c new file mode 100644 index 00000000..82be982f --- /dev/null +++ b/libreborn/src/util/exec.c @@ -0,0 +1,113 @@ +#include + +// Safe execvpe() +__attribute__((noreturn)) void safe_execvpe(const char *const argv[], const char *const envp[]) { + int ret = execvpe(argv[0], (char *const *) argv, (char *const *) envp); + if (ret == -1) { + ERR("Unable To Execute Program: %s: %s", argv[0], strerror(errno)); + } else { + IMPOSSIBLE(); + } +} + +// Chop Off Last Component +void chop_last_component(char **str) { + size_t length = strlen(*str); + for (size_t i = 0; i < length; i++) { + size_t j = length - i - 1; + if ((*str)[j] == '/') { + (*str)[j] = '\0'; + break; + } + } +} +// Get Binary Directory (Remember To Free) +char *get_binary_directory() { + // Get Path To Current Executable + char *exe = realpath("/proc/self/exe", NULL); + ALLOC_CHECK(exe); + + // Chop Off Last Component + chop_last_component(&exe); + + // Return + return exe; +} + +// Safe execvpe() Relative To Binary +__attribute__((noreturn)) void safe_execvpe_relative_to_binary(const char *const argv[], const char *const envp[]) { + // Get Binary Directory + char *binary_directory = get_binary_directory(); + // Create Full Path + char *full_path = NULL; + safe_asprintf(&full_path, "%s/%s", binary_directory, argv[0]); + // Free Binary Directory + free(binary_directory); + + // Build New argv + int argc; + for (argc = 0; argv[argc] != NULL; argc++); + const char *new_argv[argc + 1]; + for (int i = 1; i < argc; i++) { + new_argv[i] = argv[i]; + } + new_argv[0] = full_path; + new_argv[argc] = NULL; + // Run + safe_execvpe(new_argv, envp); +} + +// Get MCPI Directory +char *get_mcpi_directory() { + return getenv("MCPI_DIRECTORY"); +} + +// Run Command And Get Output +char *run_command(const char *const command[], int *return_code) { + // Store Output + int output_pipe[2]; + safe_pipe2(output_pipe, 0); + // Run + pid_t ret = fork(); + if (ret == -1) { + ERR("Unable To Run Command: %s", strerror(errno)); + } else if (ret == 0) { + // Child Process + + // Pipe stdout + dup2(output_pipe[1], STDOUT_FILENO); + close(output_pipe[0]); + close(output_pipe[1]); + + // Close stderr (But Not In Debug Mode) + const char *is_debug = getenv("MCPI_DEBUG"); + if (is_debug == NULL || strlen(is_debug) < 1) { + int null_fd = open("/dev/null", O_WRONLY); + dup2(null_fd, STDERR_FILENO); + close(null_fd); + } + + // Run + safe_execvpe(command, (const char *const *) environ); + } else { + // Parent Process + + // Read stdout + close(output_pipe[1]); + char *output = NULL; + char c; + int bytes_read = 0; + while ((bytes_read = read(output_pipe[0], (void *) &c, 1)) > 0) { + string_append(&output, "%c", c); + } + close(output_pipe[0]); + + // Get Return Code + int status; + waitpid(ret, &status, 0); + *return_code = WIFEXITED(status) ? WEXITSTATUS(status) : -1; + + // Return + return output; + } +} diff --git a/libreborn/src/util/string.c b/libreborn/src/util/string.c new file mode 100644 index 00000000..55f2d63d --- /dev/null +++ b/libreborn/src/util/string.c @@ -0,0 +1,25 @@ +#include + +// Sanitize String +#define MINIMUM_SAFE_CHARACTER 32 +#define MAXIMUM_SAFE_CHARACTER 126 +void sanitize_string(char **str, int max_length, unsigned int allow_newlines) { + // Store Message Length + int length = strlen(*str); + // Truncate Message + if (max_length != -1 && length > max_length) { + (*str)[max_length] = '\0'; + length = max_length; + } + // Loop Through Message + for (int i = 0; i < length; i++) { + if (allow_newlines && ((*str)[i] == '\n' || (*str)[i] == '\r')) { + continue; + } + unsigned char c = (unsigned char) (*str)[i]; + if (c < MINIMUM_SAFE_CHARACTER || c > MAXIMUM_SAFE_CHARACTER) { + // Replace Illegal Character + (*str)[i] = '?'; + } + } +} diff --git a/libreborn/src/util/util.c b/libreborn/src/util/util.c new file mode 100644 index 00000000..38a9561d --- /dev/null +++ b/libreborn/src/util/util.c @@ -0,0 +1,8 @@ +#include + +// Safe Version Of pipe() +void safe_pipe2(int pipefd[2], int flags) { + if (pipe2(pipefd, flags) != 0) { + ERR("Unable To Create Pipe: %s", strerror(errno)); + } +} diff --git a/media-layer/core/CMakeLists.txt b/media-layer/core/CMakeLists.txt index 31c7781b..41759bf7 100644 --- a/media-layer/core/CMakeLists.txt +++ b/media-layer/core/CMakeLists.txt @@ -20,7 +20,7 @@ endif() # Configure Media Layer Core If Built if(TARGET media-layer-core) # Link - target_link_libraries(media-layer-core media-layer-headers reborn-headers pthread dl) + target_link_libraries(media-layer-core media-layer-headers reborn-util pthread dl) if(NOT MCPI_HEADLESS_MODE) # Find FreeImage find_library(FREEIMAGE_LIBRARY NAMES freeimage libfreeimage.so.3 REQUIRED) diff --git a/media-layer/core/src/media.c b/media-layer/core/src/media.c index 01c9f346..0e79e22b 100644 --- a/media-layer/core/src/media.c +++ b/media-layer/core/src/media.c @@ -6,7 +6,7 @@ #ifndef MCPI_HEADLESS_MODE #define GLFW_INCLUDE_NONE #include -#endif // #ifndef MCPI_HEADLESS_MODE +#endif #include #include @@ -14,7 +14,7 @@ #ifndef MCPI_HEADLESS_MODE #include "audio/engine.h" -#endif // #ifndef MCPI_HEADLESS_MODE +#endif // Allow Disabling Interaction static void update_cursor(); @@ -222,7 +222,7 @@ static void glfw_scroll(__attribute__((unused)) GLFWwindow *window, __attribute_ } } -#endif // #ifndef MCPI_HEADLESS_MODE +#endif // Track Media Layer State static int is_running = 0; @@ -235,7 +235,7 @@ void media_set_raw_mouse_motion_enabled(int enabled) { if (is_running) { glfwSetInputMode(glfw_window, GLFW_RAW_MOUSE_MOTION, GLFW_FALSE); } -#endif // #ifndef MCPI_HEADLESS_MODE +#endif if (!raw_mouse_motion_enabled) { WARN("%s", "Raw mouse motion has been DISABLED, this IS NOT recommended, and should only ever be used on systems that don't support or have broken raw mouse motion."); } @@ -249,7 +249,7 @@ void media_disable_vsync() { if (is_running) { glfwSwapInterval(0); } -#endif // #ifndef MCPI_HEADLESS_MODE +#endif } // Init Media Layer @@ -289,9 +289,9 @@ void SDL_WM_SetCaption(const char *title, __attribute__((unused)) const char *ic // Init OpenAL _media_audio_init(); -#else // #ifndef MCPI_HEADLESS_MODE +#else (void) title; // Mark As Used -#endif // #ifndef MCPI_HEADLESS_MODE +#endif // Set State is_running = 1; @@ -307,7 +307,7 @@ void media_swap_buffers() { #ifndef MCPI_HEADLESS_MODE // Don't Swap Buffers In A Context-Less Window glfwSwapBuffers(glfw_window); -#endif // #ifndef MCPI_HEADLESS_MODE +#endif } // Fullscreen Not Needed In Headless Mode @@ -340,10 +340,10 @@ void media_toggle_fullscreen() { } is_fullscreen = !is_fullscreen; } -#else // #ifndef MCPI_HEADLESS_MODE +#else void media_toggle_fullscreen() { } -#endif // #ifndef MCPI_HEADLESS_MODE +#endif // Intercept SDL Events void _media_handle_SDL_PollEvent() { @@ -359,7 +359,7 @@ void _media_handle_SDL_PollEvent() { SDL_PushEvent(&event); glfwSetWindowShouldClose(glfw_window, GLFW_FALSE); } -#endif // #ifndef MCPI_HEADLESS_MODE +#endif } // Cleanup Media Layer @@ -376,7 +376,7 @@ void media_cleanup() { // Cleanup OpenAL _media_audio_cleanup(); -#endif // #ifndef MCPI_HEADLESS_MODE +#endif // Update State is_running = 0; @@ -437,7 +437,7 @@ static void update_cursor() { } } } -#endif // #ifndef MCPI_HEADLESS_MODE +#endif } // Fix SDL Cursor Visibility/Grabbing @@ -483,7 +483,7 @@ void media_get_framebuffer_size(int *width, int *height) { glfwGetFramebufferSize(glfw_window, width, height); return; } -#endif // #ifndef MCPI_HEADLESS_MODE +#endif *width = DEFAULT_WIDTH; *height = DEFAULT_HEIGHT; } diff --git a/media-layer/core/src/screenshot.c b/media-layer/core/src/screenshot.c index 42f00d09..f883beee 100644 --- a/media-layer/core/src/screenshot.c +++ b/media-layer/core/src/screenshot.c @@ -122,8 +122,8 @@ __attribute__((constructor)) static void init() { FreeImage_Initialise(0); } -#else // #ifndef MCPI_HEADLESS_MODE +#else void media_take_screenshot() { // NOP } -#endif // #ifndef MCPI_HEADLESS_MODE +#endif diff --git a/media-layer/proxy/CMakeLists.txt b/media-layer/proxy/CMakeLists.txt index 3e8d9fc7..e421e91d 100644 --- a/media-layer/proxy/CMakeLists.txt +++ b/media-layer/proxy/CMakeLists.txt @@ -9,7 +9,7 @@ if(BUILD_NATIVE_COMPONENTS) # Build Media Layer Proxy Client add_executable(media-layer-proxy-client src/client/client.cpp ${MEDIA_LAYER_PROXY_SRC}) - target_link_libraries(media-layer-proxy-client media-layer-headers reborn-headers ${MEDIA_LAYER_PROXY_LIBS}) + target_link_libraries(media-layer-proxy-client media-layer-headers reborn-util ${MEDIA_LAYER_PROXY_LIBS}) target_compile_definitions(media-layer-proxy-client PRIVATE -DMEDIA_LAYER_PROXY_CLIENT) # Install install(TARGETS media-layer-proxy-client DESTINATION "${MCPI_BIN_DIR}") @@ -20,7 +20,7 @@ if(BUILD_ARM_COMPONENTS) # Build Media Layer Proxy Server add_library(media-layer-core SHARED src/server/server.cpp ${MEDIA_LAYER_PROXY_SRC}) - target_link_libraries(media-layer-core media-layer-headers reborn-headers) + target_link_libraries(media-layer-core media-layer-headers reborn-util) target_compile_definitions(media-layer-core PRIVATE -DMEDIA_LAYER_PROXY_SERVER) # Symlink GLESv1_CM To Media Layer Proxy Server install_symlink("libmedia-layer-core.so" "${MCPI_LIB_DIR}/libGLESv1_CM.so.1") diff --git a/media-layer/proxy/src/server/server.cpp b/media-layer/proxy/src/server/server.cpp index f84b68e7..606955df 100644 --- a/media-layer/proxy/src/server/server.cpp +++ b/media-layer/proxy/src/server/server.cpp @@ -77,10 +77,10 @@ static void start_media_layer_proxy_client(int read, int write) { safe_asprintf(&read_str, "%i", read); char *write_str = NULL; safe_asprintf(&write_str, "%i", write); - char *argv[] = {NULL /* Updated By safe_execvpe() */, read_str, write_str, NULL}; + const char *argv[] = {"media-layer-proxy-client", read_str, write_str, NULL}; // Run - safe_execvpe("media-layer-proxy-client", argv, environ); + safe_execvpe(argv, (const char *const *) environ); } else { // Parent Process _client_pid = ret; diff --git a/media-layer/stubs/CMakeLists.txt b/media-layer/stubs/CMakeLists.txt index ca0347a2..10ac05ca 100644 --- a/media-layer/stubs/CMakeLists.txt +++ b/media-layer/stubs/CMakeLists.txt @@ -14,11 +14,11 @@ if(BUILD_ARM_COMPONENTS) # Stub EGL add_library(EGL SHARED src/EGL.c) - target_link_libraries(EGL reborn-headers media-layer-headers) + target_link_libraries(EGL reborn-util media-layer-headers) set_target_properties(EGL PROPERTIES SOVERSION "1") # Stub X11 add_library(X11 SHARED src/X11.c) - target_link_libraries(X11 reborn-headers media-layer-headers) + target_link_libraries(X11 reborn-util media-layer-headers) set_target_properties(X11 PROPERTIES SOVERSION "6") # Install if(MCPI_HEADLESS_MODE OR MCPI_USE_MEDIA_LAYER_PROXY) diff --git a/mods/CMakeLists.txt b/mods/CMakeLists.txt index 8231239d..b4545282 100644 --- a/mods/CMakeLists.txt +++ b/mods/CMakeLists.txt @@ -8,80 +8,80 @@ add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0) ## Mods add_library(compat SHARED src/compat/compat.c src/compat/egl.c src/compat/x11.c) -target_link_libraries(compat reborn media-layer-core) +target_link_libraries(compat reborn-patch media-layer-core) add_library(readdir SHARED src/readdir/readdir.c) add_library(feature SHARED src/feature/feature.c) -target_link_libraries(feature reborn) +target_link_libraries(feature reborn-patch) add_library(version SHARED src/version/version.cpp) -target_link_libraries(version reborn symbols) +target_link_libraries(version reborn-patch symbols) add_library(chat SHARED src/chat/chat.cpp src/chat/ui.c) -target_link_libraries(chat reborn symbols feature pthread) +target_link_libraries(chat reborn-patch symbols feature pthread) if(MCPI_SERVER_MODE) add_library(server SHARED src/server/server.cpp src/server/server_properties.cpp) - target_link_libraries(server reborn symbols feature home misc compat dl media-layer-core pthread) + target_link_libraries(server reborn-patch symbols feature home misc compat dl media-layer-core pthread) else() target_link_libraries(compat input sign chat home dl) target_link_libraries(chat input) add_library(multiplayer SHARED src/multiplayer/multiplayer.cpp) - target_link_libraries(multiplayer reborn symbols home feature) + target_link_libraries(multiplayer reborn-patch symbols home feature) add_library(sound SHARED src/sound/sound.cpp src/sound/repository.cpp) - target_link_libraries(sound reborn symbols feature override media-layer-core) + target_link_libraries(sound reborn-patch symbols feature override media-layer-core) add_library(camera SHARED src/camera/camera.cpp) - target_link_libraries(camera reborn symbols media-layer-core feature home) + target_link_libraries(camera reborn-patch symbols media-layer-core feature home) add_library(input SHARED src/input/input.cpp src/input/bow.c src/input/attack.c src/input/toggle.c src/input/misc.c src/input/drop.cpp) - target_link_libraries(input reborn symbols creative feature media-layer-core) + target_link_libraries(input reborn-patch symbols creative feature media-layer-core) add_library(sign SHARED src/sign/sign.cpp) - target_link_libraries(sign reborn symbols feature input) + target_link_libraries(sign reborn-patch symbols feature input) add_library(creative SHARED src/creative/creative.cpp) - target_link_libraries(creative reborn symbols feature) + target_link_libraries(creative reborn-patch symbols feature) add_library(touch SHARED src/touch/touch.c) - target_link_libraries(touch reborn symbols feature) + target_link_libraries(touch reborn-patch symbols feature) add_library(override SHARED src/override/override.c) - target_link_libraries(override reborn symbols dl home) + target_link_libraries(override reborn-patch symbols dl home) add_library(textures SHARED src/textures/textures.cpp) - target_link_libraries(textures reborn symbols media-layer-core feature) + target_link_libraries(textures reborn-patch symbols media-layer-core feature) add_library(atlas SHARED src/atlas/atlas.cpp) - target_link_libraries(atlas reborn symbols feature GLESv1_CM) + target_link_libraries(atlas reborn-patch symbols feature GLESv1_CM) if(NOT MCPI_HEADLESS_MODE) add_library(benchmark SHARED src/benchmark/benchmark.cpp) - target_link_libraries(benchmark reborn symbols compat misc media-layer-core) + target_link_libraries(benchmark reborn-patch symbols compat misc media-layer-core) endif() endif() add_library(game-mode SHARED src/game-mode/game-mode.c src/game-mode/game-mode.cpp) -target_link_libraries(game-mode reborn symbols feature) +target_link_libraries(game-mode reborn-patch symbols feature) add_library(death SHARED src/death/death.cpp) -target_link_libraries(death reborn symbols feature) +target_link_libraries(death reborn-patch symbols feature) add_library(misc SHARED src/misc/misc.c src/misc/misc.cpp src/misc/logging.cpp) -target_link_libraries(misc reborn symbols feature GLESv1_CM) +target_link_libraries(misc reborn-patch symbols feature GLESv1_CM) add_library(options SHARED src/options/options.c) -target_link_libraries(options reborn symbols feature) +target_link_libraries(options reborn-patch symbols feature) add_library(home SHARED src/home/home.c) -target_link_libraries(home reborn symbols) +target_link_libraries(home reborn-patch symbols) add_library(test SHARED src/test/test.c) -target_link_libraries(test reborn home) +target_link_libraries(test reborn-patch home) add_library(init SHARED src/init/init.c) target_link_libraries(init compat game-mode misc death options chat home version test media-layer-core) diff --git a/mods/src/chat/chat.cpp b/mods/src/chat/chat.cpp index f086b7e4..6435ce74 100644 --- a/mods/src/chat/chat.cpp +++ b/mods/src/chat/chat.cpp @@ -11,7 +11,7 @@ #include "../feature/feature.h" #ifndef MCPI_SERVER_MODE #include "../input/input.h" -#endif // #ifndef MCPI_SERVER_MODE +#endif #include "chat.h" // Store If Chat is Enabled @@ -39,7 +39,7 @@ static void send_api_chat_command(unsigned char *minecraft, char *str) { send_api_command(minecraft, command); free(command); } -#endif // #ifndef MCPI_SERVER_MODE +#endif // Send Message To Players static void send_message(unsigned char *server_side_network_handler, char *username, char *message) { @@ -115,7 +115,7 @@ static void send_queued_messages(unsigned char *minecraft) { // Unlock pthread_mutex_unlock(&queue_mutex); } -#endif // #ifndef MCPI_SERVER_MODE +#endif // Init void init_chat() { @@ -131,6 +131,6 @@ void init_chat() { // Send Messages On Input Tick #ifndef MCPI_SERVER_MODE input_run_on_tick(send_queued_messages); -#endif // #ifndef MCPI_SERVER_MODE +#endif } } diff --git a/mods/src/chat/ui.c b/mods/src/chat/ui.c index c316ab01..c303f4dc 100644 --- a/mods/src/chat/ui.c +++ b/mods/src/chat/ui.c @@ -10,27 +10,13 @@ #include "../input/input.h" // Run Command -static char *run_command(char *command, int *return_code) { +static char *run_command_proper(const char *command[], int *return_code) { // Prepare Environment RESET_ENVIRONMENTAL_VARIABLE("LD_LIBRARY_PATH"); RESET_ENVIRONMENTAL_VARIABLE("LD_PRELOAD"); - // Start - FILE *out = popen(command, "r"); - if (!out) { - ERR("%s", "Failed To Run Command"); - } - - // Record - char *output = NULL; - int c; - while ((c = fgetc(out)) != EOF) { - string_append(&output, "%c", (char) c); - } - - // Return - *return_code = WEXITSTATUS(pclose(out)); - return output; + // Run + return run_command(command, return_code); } // Count Chat Windows @@ -44,7 +30,8 @@ unsigned int chat_get_counter() { static void *chat_thread(__attribute__((unused)) void *nop) { // Open int return_code; - char *output = run_command("zenity --title 'Chat' --class 'Minecraft: Pi Edition: Reborn' --entry --text 'Enter Chat Message:'", &return_code); + const char *command[] = {"zenity", "--title", "Chat", "--class", GUI_TITLE, "--entry", "--text", "Enter Chat Message:", NULL}; + char *output = run_command_proper(command, &return_code); // Handle Message if (output != NULL) { // Check Return Code @@ -87,4 +74,4 @@ void chat_open() { pthread_create(&thread, NULL, chat_thread, NULL); } } -#endif // #ifndef MCPI_SERVER_MODE +#endif diff --git a/mods/src/compat/compat.c b/mods/src/compat/compat.c index 5c2e0e81..4ed17dd0 100644 --- a/mods/src/compat/compat.c +++ b/mods/src/compat/compat.c @@ -18,7 +18,7 @@ // Custom Title HOOK(SDL_WM_SetCaption, void, (__attribute__((unused)) const char *title, const char *icon)) { ensure_SDL_WM_SetCaption(); - (*real_SDL_WM_SetCaption)("Minecraft: Pi Edition: Reborn", icon); + (*real_SDL_WM_SetCaption)(GUI_TITLE, icon); } // Mouse Cursor Is Always Invisible In Vanilla MCPI @@ -106,7 +106,7 @@ HOOK(SDL_PollEvent, int, (SDL_Event *event)) { return ret; } -#endif // #ifndef MCPI_SERVER_MODE +#endif // Exit Handler static void exit_handler(__attribute__((unused)) int data) { diff --git a/mods/src/options/options.c b/mods/src/options/options.c index 81bfe09c..dc1cf68e 100644 --- a/mods/src/options/options.c +++ b/mods/src/options/options.c @@ -30,7 +30,7 @@ static int get_render_distance() { ERR("Invalid Render Distance: %s", distance_str); } } -#endif // #ifndef MCPI_SERVER_MODE +#endif // Get Custom Username static char *get_username() { @@ -86,9 +86,9 @@ void init_options() { #ifndef MCPI_SERVER_MODE render_distance = get_render_distance(); INFO("Setting Render Distance: %i", render_distance); -#else // #ifndef MCPI_SERVER_MODE +#else render_distance = 3; -#endif // #ifndef MCPI_SERVER_MODE +#endif // Server Visible server_visible = !feature_has("Disable Hosting LAN Worlds", 0); @@ -99,7 +99,7 @@ void init_options() { const char *username = get_username(); #ifndef MCPI_SERVER_MODE INFO("Setting Username: %s", username); -#endif // #ifndef MCPI_SERVER_MODE +#endif if (strcmp(*default_username, "StevePi") != 0) { ERR("%s", "Default Username Is Invalid"); }