From d18efe7156c84572b7bb5ab7bc18593c1228ab95 Mon Sep 17 00:00:00 2001 From: CommonLoon102 Date: Thu, 22 Apr 2021 08:34:22 +0200 Subject: [PATCH] Merge 0.2.9f changes --- .github/workflows/build.yml | 4 +- CHANGES.txt | 5 ++ CMakeLists.txt | 2 +- Makefile | 2 +- game.h | 7 +- icon.bmp | Bin 0 -> 196730 bytes main.cpp | 1 - mdec.cpp | 2 +- menu.cpp | 171 +++++++++++++++++++++++++----------- menu.h | 3 +- mixer.cpp | 2 +- mixer.h | 4 +- monsters.cpp | 24 ++--- resource.cpp | 6 +- resource.h | 6 +- scaler.h | 11 ++- scaler_nearest.cpp | 26 ------ scaler_xbr.cpp | 46 ++++++---- system_sdl2.cpp | 79 +++++++++++------ vcpkg.json | 2 +- 20 files changed, 242 insertions(+), 161 deletions(-) create mode 100644 icon.bmp delete mode 100644 scaler_nearest.cpp diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a109c72..4c9bb2b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -71,9 +71,9 @@ jobs: cd ${{ env.build_dir }} mkdir ${{ env.package_dir }} if [[ "${{ runner.os }}" == 'Windows' ]]; then - cp ${{ matrix.config.build_type }}/${{ env.app_name }}.exe ${{ matrix.config.build_type }}/SDL2.dll ${{ matrix.config.build_type }}/getopt.dll ../${{ env.app_name }}.ini ${{ env.package_dir }} + cp ${{ matrix.config.build_type }}/${{ env.app_name }}.exe ${{ matrix.config.build_type }}/SDL2.dll ${{ matrix.config.build_type }}/getopt.dll ../${{ env.app_name }}.ini ../icon.bmp ${{ env.package_dir }} else - cp ${{ env.app_name }} ../${{ env.app_name }}.ini ${{ env.package_dir }} + cp ${{ env.app_name }} ../${{ env.app_name }}.ini ../icon.bmp ${{ env.package_dir }} fi - name: Zip Package diff --git a/CHANGES.txt b/CHANGES.txt index b1e93b8..01f1df7 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,8 @@ +* release 0.2.9f + - added 'linear' graphics scaler + - enabled PSX options menu + - fixed '1x' scaling + * release 0.2.9e - fixed graphics glitches with PSX backgrounds - removed 'inih' and 'libxbr-standalone' dependencies diff --git a/CMakeLists.txt b/CMakeLists.txt index c0b5262..db64a0e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,7 +52,7 @@ if(NSWITCH) add_definitions(-D__SWITCH__) add_custom_target(${CMAKE_PROJECT_NAME}.nro DEPENDS ${CMAKE_PROJECT_NAME} - COMMAND nacptool --create "Heart of Darkness" "cyx, usineur" "0.2.9d" ${CMAKE_PROJECT_NAME}.nacp + COMMAND nacptool --create "Heart of Darkness" "cyx, usineur" "0.2.9f" ${CMAKE_PROJECT_NAME}.nacp COMMAND elf2nro ${CMAKE_PROJECT_NAME} ${CMAKE_PROJECT_NAME}.nro --icon=${CMAKE_SOURCE_DIR}/3p/res/icon.jpg --nacp=${CMAKE_PROJECT_NAME}.nacp ) add_custom_target(nxlink diff --git a/Makefile b/Makefile index cf08144..6e41552 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ SRCS = andy.cpp benchmark.cpp fileio.cpp fs_posix.cpp game.cpp \ resource.cpp screenshot.cpp sound.cpp staticres.cpp system_sdl2.cpp \ util.cpp video.cpp -SCALERS := scaler_nearest.cpp scaler_xbr.cpp +SCALERS := scaler_xbr.cpp OBJS = $(SRCS:.cpp=.o) $(SCALERS:.cpp=.o) DEPS = $(SRCS:.cpp=.d) $(SCALERS:.cpp=.d) diff --git a/game.h b/game.h index e3cd14e..09f08d8 100644 --- a/game.h +++ b/game.h @@ -161,12 +161,10 @@ struct Game { bool _specialAnimFlag; AndyShootData _andyShootsTable[kMaxAndyShoots]; int _andyShootsCount; - uint8_t _mstOp68_type, _mstOp68_arg9, _mstOp67_type; - uint8_t _mstOp67_flags1; - uint16_t _mstOp67_unk; + uint8_t _mstOp68_type, _mstOp68_flags1, _mstOp67_type, _mstOp67_flags1; int _mstOp67_x1, _mstOp67_x2, _mstOp67_y1, _mstOp67_y2; int8_t _mstOp67_screenNum; - uint16_t _mstOp68_flags1; + uint16_t _mstOp68_flags2; int _mstOp68_x1, _mstOp68_x2, _mstOp68_y1, _mstOp68_y2; int8_t _mstOp68_screenNum; uint32_t _mstLevelGatesMask; @@ -204,7 +202,6 @@ struct Game { int _xMstPos1, _yMstPos1; int _xMstPos2, _yMstPos2; // xMstDist1, yMstDist1 int _xMstPos3, _yMstPos3; - int _mstHelper1Count; int _mstActionNum; uint32_t _mstAndyVarMask; int _mstChasingMonstersCount; diff --git a/icon.bmp b/icon.bmp new file mode 100644 index 0000000000000000000000000000000000000000..9cde77be01dd2e1af365b526fef212c65bcc435b GIT binary patch literal 196730 zcmeFa1$-3A);`?b)jbn;cOgQYkdP1}kc0$C2qA&s?z*@JcXxMpcXxMK+?~bQMHX4+ zd#Y>FJFwjQ-tX@2{qMco^DCOpOiy?9>F1pDoKvT2lFN7F2uEMt@UJD^;n_c)bE1FY zMYuO?eNDgjjjssvOCs1i^m zph`fMfGPo10;&X538)fKC7?<`m4GS%RRXF6R0*gOP$i&BK$UO?e zNDgjjjssvOCs1i^mph`fMfGPo10;&X538)fKC7?<`m4GS%RRXF6R0*gOP$i&B zK$UO?eNDgjjjssvOCs1i^mph`fMfGPo10;&X538)fKC7?<` zm4GS%RRXF6R0*gOP$i&BK$UO?eNDgjjjssvOCs1i^mph`fM zfGPo10;&X538)fKC7?<`m4GS%RRXF6R0*gOP$i&BK$UO?eN zDgjjjssvOCs1i^mph`fMfGPo10;&X538)fKC7?<`m4GS%RRXF6R0*gOP$i&BK$UO?eNDgjjjssvOCs1i^mph`fMfGPo10;&X538)fKC7?<`m4GS% zRRXF6R0*gOP$i&BK$UO?eNDgjjjssvOCs1i^mph`fMfGPo1 z0;&X538)fKC7?>6Kmw#-NBv)wz~4s#0>?=lXT)=wzt26Zclx)N0MBt~4Ee@9XZ*Jp zh$?`;j|6~x!c&`1kuNvZ`2T&L{@?jdDe^f=bqv7|6_9x94%C+_f&Wzrfae4Gz`6EI z%Mb7~@~`6mfA!J-o$m{%X2=KlfqaH`Ws4mJ|7!l{?~IfG$$JO#6|)bVo9Nj-FT);N zwnObdR6~>_;{QoGs5kX@lYnCOfohG+Z~%T&o;UllnaC;t{G%Zk1Z7cQss#SqB>?b) z%rLeO@WZwP=cWR0!Sj{^ZC1Qs#q;I@RR<`Ao&xdoD{2(*-~ROfp8W@IS!0Aplv6qk z{6M~$K+$d`@U{Z)$P;%#V=D;w6K?|lXwVTfM928Lssevc2>rL*wSsf(0Ir6OnSFqt zAs?(prv1Eyz}o@&g5V}d0fJemVC^Ij=z?Aa1nL6t72scW0sk#9`1|zpE1UzO%*bO0 z^d2KwYzMGs$On>ve3*Sodw2?RdqHsFB~L*cM6B{O5fQ}BOE7W~2$TYJ2;Kn=U%?k~ z22}+^NDk6!Xq-&dfXZl?KeiP-6?MVh5mA8ly23{BoQNdSfl{4gzuKjr;`j zFk)Ap6drC_Ct7eSELcJvoMjbYEC?=uQ6QWa7ghT6*AdbGWKY=R{yR$>_pX3k*`bD= znRj^$oGT4zH-tUIJhp(nksQpz>?5#cY~P+Ip1etrU>!*ulZn?%$JPto^AiR42x1>7 zSa|4FKmb9)w{QlWNA&{#lZy6NyD0|yU#)rp_fQPG0(k5&Sh6ik*8r>7G2CZK9tM8d z>-*aip|?oa@Z82a{i zwoCsRz(B%0UUc)t1;703i;w=>|G=%m&NJkLY%m(bOuAy&0d=JT>VPGhY~l68SnM5| zy+IC#rEv=KJT`Ku>niBMUz9kM77H~H<9E0W__+4X-GohT#MH8)e=*_|PM`v2o`R9H zAhB2gCuBs3c>$FO{RhAXAxpd<@`Awga^nO83d$7We?T1!VGr;t<{d}_)EW0stU3d> zf_ePO0FN#9X~ZgTLEX{7URYzC2YDWV-&ZgR5v(GKeKPT`E=7;kR{yc-x#@%F=<*-V zG;LN(EC!DdMeN}PK!m&mV|W3C$q0wPPyty_{{tBKn_mJpP(!oOM9T{j&$FBcK*H<~ zki*)4uawY#+z#b71;|%m&oIy6jeQUpnaKd>G>2m<&>ej)0jX$I1f^q zPDF4gqPJiaELapFw(-QRvKY}xO55oDluG`}~K3|(zO^i$yy`zakII)2j z@Dj90irN$Xpa4@nrljb(`uejaAlpD{eDL&9nxbV|fOMRkky8}m&+f(_etyO58}b}x z@EL)}Ez>5`7(=Vh3_IHb-bw?`8QQS}s)GjD+YsKC0Q|f=AwaUfU=|_R#1N+pF`%iG zFvY6L&D?!AR@}th-CcR_R;2^8?7nNSDP36#OCp}|0uh3BA;H8$(6|s{XP^S;r%HtW za7cXfgv=hAAQ0n;DK9WBV8W*=P@o0>MT}HIzQT4G?3ux5n$B|Xh8)Jws$-wQp5a_U zI{?obNIPwWHb=s{5#EDHK7w(mU5C3lX&y$|- zjhH^#vR*SOAxjL1C$2?_O^{#?ui;K4oCg<0mt+?}hlb&^Aa*qM_2)?du9m^WLLgSW zXwC~}ya14}a15Wqei|wQ(Dhe-F~XxDABwE-9A@wpGQ&U)W3Cu>X3SyF^?-+MF95u< zqep%L{=r-H#-C{X1d}4fx;TtK@oz3AOtfioveXLQHeG75R7lLt)>U^>ce%>Wr5@dT z8CR$+MU^H#F$e^Rm7iejDQKLDU@f}1{7Z>(P z^t1`q4O(fEvcf);=dDURTqTJRTnGz$3Pr<6NF z&w(Z6ZCKn2R%1_Lk3@}OLs_QiE=cY~gE&;*4EF^MDi+XCz3I+NF1(0JPCH&;P5~#x z**u8^4ihF=#1i{-;@e0nHr%4xwy-e|`(N5Kc@LhrB#K%QSmu7(_vT*Lb6Y}&kFcoP zSSpqw7Q$DG2$p^_{v8Re4n?1pED*i}JM$M2@?Q$NzrQn(D$v4=KXQE_2FyI#z~b?u zi(u?3m^kuUD@uoGb_frJsQ|tL(}eHR(BQA}fY1#_dz9M&iV#b(n*vq=R2L$6=#fug zY(0qv)o4C~Ng=^3KrlyTPB5_w5v+rWO|W1SOss>j6>LJ#=)R@DU`k7Ocsghcv(E?; zEH*rdm!R<%jEfME-zkL)n&?fe&q6J}4f zZ`4+kSWyg26rCf9RbjydCxN>`41%6R&P>RL_8-rS=>m3?hIiPTY|BuAPw`?~_RimP z`%MYpO~?v@Wq@JU(o8VKKV?DA)aZeintKQ)b_H}u)&fdR(eQxuK|#SI_vl400E zeiV`bo&fwi3ZkE287kPKh|5o~aHc^4%?`mApus>Xbci|vS^mnk3a}%issGXM_hV1;{TX7$Z;(#}KVxgIr}SaY`WWsl=xo38*NBR1(8;#Ly}vq7h%b zgP1zhs?KQJh8d!NFtKoyOZOGCYa$Qs0)RwLjTzuVM8ZN?Uih@ zFkh11FZ)y3%8!65OTn{Xj~H7#6t-0Wz(Asa0e}9^mm%ud*_3N3muCH8@BjbW{`MBw zss7Ise)9n3yN2NZjqzu}o!}#wMhNyri6geC=JJFAq#-~v}wJxS;IBe z7Mis=Q*`#*c4v<_UH7obp{7!NG_glqKxt1wQi$yU{|J^D{y|`X|0Lp^BNlE?@!$AZ z^8Amh-<2y~4knM`pM?OAw?4hzY~PxI-UH3E8c0RbMXy+57eOo#0=NmH1CeVX$0cQAiivHvCbnf+(@_Z2ijf=N+gl_a|8@$b;+X!5*|%id)cE5{3Z{38m6 zWUj2bcBAE?b%A|{T2^eVDOy(aiX--s#4(h&2!8e8ha*PP504&pvJ~-3Af6z` zAj!}OKf%-*0(Rw_Ud|e*qwx6Q85&`e1@o zyMh=PF4!PsKxXEf_@~K#k@yN)*#9WtpW1)$pZF>E4UUwY^ZPhG_8(pVCqFv> z#Ld=+*9Y||3aFGFIp4M2t|AkzRp0%2+0QqYTz9i_20rVg z)zS5PwcNa2Qz;JPQ*^ot5s~Ks+xbcb82`bAd;|^93;QqgfB!bxxQTYP55!M@JLg5) zoHjTokPqX}`2VGqS8ldGv@v+VNUO>%r06WEP$F@J?!bdk{EH%d3I^TqT%gW)`s_JU zJQq}-;$~T+MS60b9{!a51r-HT4Qz2d6OMcmy`UH#fCQWt2gsPw1I7>@|KdJFnu0O> z$r?K|P6CBxmtY-Z`vzO~ss0#)_bG1Kjms*JKXD9e-v&RfC7T8PVf60gtK;5rDtyXB zA)l7+LvH^Z{J=kkevp(vnPM$X)H>e*!;JDOldu%xgMbm`i*N)1f~gybF;B3>2Oa?~ ziVHykf2s-q{M11g1n=}V3&79Db74Pa=0n3$#quW*$Fib#H7U5I6pO$U_Ia&;-!o~; zpANdAJE3b`xiwIVS8mB%-F)3nuc3Xl898DQbia^b>Mju2`ETMM?;GMDSE(TSwbaCn zv8lE@dfdxN_b0WPh|@CsGyDH!`?DLsf5@QG)_JWo#VU$^B}FITKbU4>weTjcBE1%f zC{qDECWdyX19Tis*4XYi*UP6qE#WPa5O z&hYDb@D3W&L0pbC)(53abVr5_>)tm$E$F{;2D-!EEcl|~*_6ZBn>ZnBv>UQD0OqL= zmCwLr2(zzPenaLD@FT?yr;nsGYPu2y*G*pio^(F5(|u59sZ5p_REl^PCoV;3;?CSh zFv5_KrM$8Y?)q01_;ZL5HU0(Q$AiEf;a+eTw9f|22VB}u(1r?TQN)^ozlInJW-`Pg zd!B3S-9^V|FXh*iB zo0#^h_320RuV0N?JWgAyh8SH&^g{(>B(Z~nVD!^jRsq~QQvt>mVW|IbB7}1?+6hI2 zUgPd?kGKT}PGOu>XVT;1^$0)|8Yfz{~*B_3H%6R9!KoTiQWyQ zi2kPKRu<}hrNK_!J{>M0i3Iq^3;Vwu`Fx}8p)C=^#@SYHtBtE91_1xj#5#Yprx!_@dN!2(dtj z9ZPJ}MfaLgNWO8(e7Dx8GneYN{*n?<67%a6{C_<1%k3`5cN86s7@>nvi99hdRdkIZ z)=(ex8E(s6MClP|6(7m-|}CW8poC3xFC*mmLEMpj)tlvKS3zXan(4k zGRMVooNIw8qmNQoS-zjLG`~ zaO=wRxCHLeNxmXJ#f^9fd?d#u$(>}|S{%2jTGegoX*D>`ow@^xc}Jcv$_rIEt^tx2 z92YKM6z{W!K0qL4ISyZ`z;R*n@%RXrPylbZ!#c*cdWq?=L}2I47}!BuVv2pey~QU! z8+~hP`zgO(i{qZ{eEwk2#XWHor#Uq2Y?N3{3@$CYK~IYibEH6hP>Cd>DvysR!ur^l zXLIK~9XtB@!UfOg&3QI!#^WiIF8A)Wz}2}P9xIGLHT9To&o|dQn#)x&COhRu9d0D*^ce+OrQFFrw&z(kkjJ< zb?Dl<=*X*6Ce0`oU6{wIga|>HSv2zDoLN6Cn18xn?KFuni;Zn0A%i&XQhv85^XE?W z_w@jDMY63@r8D*GovmAEd*$3XPH*s;?L~)i6?dMm&2jtEQWyC92g}Gr?B*;8wTz5* z)UJ7#rWgNV(26$VhI7L`G2`xZJf=IV1J73g$vDZ5 zVcTCVczi5<-a@bM`kJOUkiwCcFG1{3nHC^u{bc)(w9IcQ5R}53Xi>vfM)WL3tmPp9 zM})LU>KD8ibA`qd_JV{1pEP<%Iqm_j=Au9P@qEJeW=7H!=iaDTbtVx_KNIFITsw3&J)V z8@%&k*%EZXo1O}35TO;vU2RbB!-ew$I4%&ROGa7=LeQ~KXHMyU`Lxc#t{hm3AcXS5 zWS;-9W7{t`uA`Gdgr-MesaONc!ZCF@?qX^Ru5zeo(FBefR#Z9}gbbP%H|*395mP z!EyXBd>D>@IcxgG>Qz7N+Ht6A9ux#$#HWlIDu6fKF*N?N3ns|_$o3!4*SC$7wBLR7 zn`P7m;Pqhn{kxS9FLUbNSyQH(1QqZBO$wvyJ85j>q^O|_fYC-Ez_V}uzAeZm)Kf-HjstYJmd^4|=Bp>asDmLN{GHQ{Nrk_V2!E<^NcBt<}v z7JIfkm9Z2tb(m|YKqv*mrRVgU^LpjWQ{|T}@#@getZXBxNQUU0Kpdbu`V+LW@Zw=^WlU{!=HEW=$<{D&T-Kqiv1{H zz=A4bSlL_i%NtPgl}@Ahi_`bHcFRzm{> z8UFx3{7ZF?I~Q9F-@@x?TwEe$Srq@+y7B_f|8&~aQ`M{0;W&^wa6hQt2##B8ZFzU> zX#6SbJHIF-bb?k3c2t=cMx)PHF6DEar`)j+K^SFev8#A&SB|UA^ZhvPabjIDC`|`w9xn(kj~_|%S{@6ymqyve8rN4 zEWj79UeW13Z!j~-l8?a?N5i8>WOcV|m*;C%BY1)Oz)}JK_z30~5z2``e_Z0-wk?mE zHaeJ=hLD7@DRda~GSK;_b7o&?+9U(I!&8$Ez(ZSOcmbLMVvk-S{0Zl{5JLBX#f}{J z^Pb)7BO{8+mw?9k2tr{wUVyKGx?OD9;$r8HY0N?V2LF2G7r0CLv4TirLO1608Hdxd zUZ-p^u&uV#JiB(U+Fox{t1X@;UUPcP{%QE5y+x*vwXD}dO3V@iOAu!qkDMXPh%yxb zToo4d`(1!S{#h1_;h*U&U<2?VVhtzeAV1(0O{5~jEVI@J^}5+|Kc*jQi57r2H2%o_ zXov>*zus!^mZ*VKoNDznPHZ3rXNw*sMZ4m}GMpHL0u>Sv0&ris0A?iu1mjfV*~zH% zLWkxRiFXol1fQ=i27RZA8E#Q=O+b%J@H@wJ5Em|umx+;y?wamSr<2=@j2drStGy;Z zPYOyA-C~FhA_y=y1n&?&jeiAxrL!ZTvfjfzN^iI*8iAH%p5GlEg^w1A1c3mQ0Q?@S!D`5Z9^0fwE;w$mvb?G!ZP<M;n^{kL*XzZuj_uto zcb`qZf27o$IWDd8jZ^DN;i!T`o)#3SuwYC_!LW8H5sDFhm9qoTvbm5?F&Y2#k)c2c z_KoKehT)?NxjC?t6c2c;JP($8v`qT*)vI85L8tZ3k>~N}&3bik{5Ch2M1jbvK~*W3 z=>-hFkJo-Ds%t;3TG5;1d_k)i_+?aJ_5{Flqg|U{9zFosSSb?So!dBgqm_C20?TiR ze>jUPjT_yYHZ_YwT|H)$Qy3$%*lcQoZ^3KK#W*|1o$c23aCU|G0{lY-fPYY?m-FU6 z7&rh93+5ePL~j*cmhVUpZ+x0U$ZQz@Et_!GIYydDxib{}GX4>^dHzYj-Tz) zeEB2}Vj3 z3>APEfQZnr<0*(3{>T*lhG{Cj?0W8I$t5Fv9mepG7_I|!lb<1l1UPH|*HkV?uMgNk-8JrD;&@AJMh@*taZ+4^Baz{|%SZ26n zGy)7Rg3ufIfAr|-m{EvlfKU0M^YHs1y}BFMW*KQC5u zim(M$rZh&UyXoqvyMIsj`lm%aS6mc&j@GuvunzS9)zq=_G7mbt*Q&B-zgKdpLGKdmmSg~Js=f$sPGeowIp(;OOgF)CR@ z3QZF|!P)eMaEJl~v82fF@b$#g+Y=t9$G>c>00TE(KrRSW=jF`l2oFJ7wqJU)D)tX*p(xHNMiX&-vQ5aSRj$U&MLf&=Fs}nmzmN(j{A*9ang`EqAn^?dj3V z(ZPrLdO4Scz9kW|g4b)}<*ZETwGUENynu07C!X?usLv1W+Muf+&zznQjl&8{vQk5R z3<3YuKK1D$a@t@Kw0HmsZ5YxD2!1s&!^|tK3+(r>&FOiAVKXR^U?WE_w!OUF@zmy^ zp+hYyHSS{HmQ$E=}Gprw}ubB)8@$I zO^;b0upQQJ*!xXy&sSW(!Z&}2dBtXkf~A0xw8GXJ)p~FN(5#Yzu*=sAPw-HDTnRZz z$UqK|6z2IQrpEZ_$%qk+WS)SJ>?k$jk>`X9_;~UBh5B{(RLb2`CjDA^Dvo?SdSp|5 zT&xEG@Q*NI6%p|1rOa|GWYUiP)2XHE(m^zOHU_zNEyfBZim4;1nSfktW)+8)x2bXOl_S7@_vL94yH}4JT|6uHBeCyWeQC+Mo3&|W0i9QIQfSN>FtKMWmML?j(Qr30GP>4F}K zlkDs5jvDuT$gM#g2P;^@&dmIoFDBmKUwrBW+q$i!;^jsE5_GbFC2B_}?l8swSHZ8M3;5{8))$Y*T|J&L zdu4Eku?{&sP2!tqLi0qAaxx2Ifw1BQARm9q4{_bt~W1E?1j%0}HNV4ElZ#xPXb%9U67jCfAh0 zP;mrrD@Oq)2!zWB#=B+wA5Sg?i^?oE9-lSOf!iRnjpuv5Tv>1oAX&!0-UXE6`EA9b z?u{M=*U*RK9{25qBNu9=f}r?U{DWX@v^2-3r>o?FZv#k2Bf{|Kk=SCTIm)`fz<=*v zx{n|Ba@@Ov2bKr;A?|Qf$|nlIPgMX0pW}AL6~8)iIL-<9y_-9io-;V83P+RwP#HJ{ z+`@yYQ*LzY0(S$A*4uxcM;QKQ{kk`sHegr{s*S8BP6D0=VuRbH_{SJA&u{gimf=`b zWCNzEa+rV#q;5Qh>UYP|7tL~R*1;$V1;z=YON3yB z+2xKXke2b!_zyw=@P9VIyxKd?J~)B@jT!)?lSGm32>6|N+Dl+~*M7WTy2wwOd_#J~^y z!&Vn3j=1H4CfOI$*PfYl61VpAt`GOx9@!E!V3K{U0j6aKnw6XEPZ(8HuYnWv&Ob5=xrUN4pgg`h$Fxgj#rV7uLNlT{qr?v~oK{60* zmZo2Rd^LyT3S-t}L3BVV5SFA61ep5wCys4!blB%eGpKiZ^{B(bNki+0`N%xK(~*YQ zr>j>@k;C30fa{h`AJ3ibz;Vc_GR=bhM;7s3-#)PaFy;W&`~CZlWo1H{{RvDQowG)f z&zg+?t>Ix0X3d<1dcT2#aQ;gvsjWCvg`6I0DFRZ{PSE^ zTzTDY-I<)#^WEBXF-p!8gG!37m<*2GDSrJ2_lpn!Dz6U#43?P2dH#(T@DFeD+reLX z1zN^QXxM@grHW#q_S)i8o$4G(p7~+vkNLH`<8p?uH{8cZho&BSz46u6dYiV#44UiJ zV2EXz&PK)ROM$tfM;Xy6k=Vo`9YipL5@`Xz{BTT%F{~TdzTErc4@0jVN|-g*xm8c& zbOc!$q91BDP!5VfxB@Xb=og6Rc(HO$CQpY}AxuCBR{d_<@9_TjB@?U5BT5vd1?f8k ztw2#U&#y(A{p{)YE0$t#7D>(^nH?!1Esn>xii2Xw3CVMHYXkq_JbgLt?eb;1S1+-H za1zI$G5p^L{y+aR0(3$YVV2+S*!FPf;9jT;f`O$fK=YiG1>x!(zcn=Y)r1Kz>Nh|? z?=@`Pm&4>cW2B_0pOW)DU{J4jZvSD_$hvZjz{UvB^G;B}?OTEWHgXOYaV{D#Oq<*v zKs}rn=31D&-L(@ZK`z&cu0n%!1Zck&)yPEzV`~3z-$Yl#MPkd*Gf)kpU;j0zS0u*3 z&zQeA5s~W%r4-sSk=U0PJ?cv#J&jXlIyc%|eEgF^S9i_d#U3964=TIjg5yA*&sJT# z$g_REQEF8wxFlA#6fir$#8t*Wz>m9ALg3%&0+{^=r-G=J@n75m$Q#Xn5)sUSm14qk zwa`Rj@ONwEpnLhJkg7H8xgq$m4{D_0L{vvSoB!ZKwbfgS_M7Eif3RhluG*OTQc$kw zSx$5X)&5E_=Z3`sIN)+eGyZ1t8`KRQMk&wq3d5vy?-=m)_nJt zBQ0}>SY%DKuemO$*Tw1^G5Fndnd+W8C-!GAQmXS-={b5 z57z@71gq}>{awEF^SLvKL+emSK*M2XY%_WOpofR<#&w+PS^oiJIolMvfQB*s?KAo6 z-b4T~`*8O3tC`b*b4rS2JBcgyh6CNb>+R-^{ZNv@aV6zZU~B^mQb4#nC?=5qa_y>R zHde3(pluLEh8DHTsG%}P8A)SKdv`$pT_s}hQ68bm+h{^Cnt;15kdnMp@F!*d@>IS; zbSkS+Br%I4Hl;=9%Azmw7{e^Hm-}`8zQQWqu1{qX$|~dsumS#~1EP>!Ih(g;k!QPZ zMk!S#%npG4$I8Yiik2W`fFBl{4FO=xK;VAY%b@s~S{oJuF#Idyzo@0ha1zF0>q#Mf zOiLpKK3j3QZo`K@4SVC-Um;)l6Zhip=C8Y?douO*_jxNe7wtFOy}?lHjP6Dynn+<) zM1;fcDa0W`b_B&~se=_pds(8Xmr2V#nOiKYMG%E!GWecTe8^ddc%M8LO80bQ@xh;o#zx#Gn-7UPg2tDmq=S zkOc_cYt&%2h99dXo5F+NZCr;$<=!BFz&lW)Z~`Ux;+e{Z9S=G|6xPYZ2k|!HAoP#(z{S1 z0>dxnQEvZzc6kKd7)?MW2DU>9d6M59?|;~{dnspINAfF5>KFDFlWK6dm^yS6vuL|i45*kFEcl4y+>_5I%6_qugDTPF48{JHHUENMmQ)335J z06z^Lk@J_+K4F4!jNbTD{Nx*#oa|V8d(mO{+8q6S_G=Rg%#@5s(qWy;>=pCfTX!=m zRh9A|7w{jfhl~l5AbZUC>+%1)`9Cy>l>kAfaAp<-;HJRe*it7b$YI5w$Xrzx{Xu~y z+E?EZHRNWS1Hh!aEpmt77zJQj06<3jMeA$Jv6o3wYbmm( z6p&3FflG>LpFnJ(2&j=r6WuV{g~fbUcy_crXE=Wr6dCU>3k)4V4q%Pv~#+hMFt z-Y|>u<7}%e^XqszbIIqmKex_>-J+U^Xb8P}(EH-S5;NwxwC-b4uCWxIEe6ygURz=^ zZVwoIFF*gzfC2YL48K2aZe?_l z96ECS<)fFgD4|FqMMpiFH|P268P68Xd%0}!lZ6Y;w`c~kRE92tNeKhhFW6h28|m(L zYtX>^V@F?X-!@ssAxwQd27;X&pX0kLmp>UcxSoiT35*U4n^M#7cFVuly3OT!t;ZNe z=SnChcfqgTVA&-%0Y$}p8*}4xo!Z^%+w11w0b@Pgyku?z5|zqxd+XG`+M(U$4sEaW z>T$V8_gf=|zTCFu;llYHP!=dNdD)Sn-h(fxB?CC_R9?>0IWu;rrdQ$Y;zZY2;(`@> z!UXFy(FQ7TrB$iyC5`7@oHbTm$=gf-2Ga}mJ`i|ru( z{loDO`UKGYmIdKS>FYn~5U>#n^5xkmo(-k&zNV$;d$c&5Jne@em){@$U?Lj-qd=J6 z5$v0LD7?V4x%aQu+qy4d+;aa8lN_oIFfZLnTfCtZk|+9Bpp)ob(nP0p(K%D}s4fO{ zHj1C^-0b`8)d;k$&8+D8iHMWjYvmE*Vof#97$B4r2`4LFN-oO@3a1yY9v zQBfg`AASlFtnjN(;A}ynfD7T$_*N+9`_L$Zg42T+5ZjiO-!CoCcK~&%D_=LbAdLf; z#LPHuu!tm9BwT$Qz|+MyN)pdh(Km`XhYEK1A%-|&UW?O$Ujh6$66Qj=ee5u!!N8BQ zF?kZVQG{R`i{!cJkR^IHkV1P><6moAt9~$k^3QW#bg2!JPG9|+ z_Qx-Ooy%Fdrf~O34mE~bWsSD2w9u;+H1WmQI|rs5z%dv^!FFt?F`sw+a;3(OjlqKu z2z1mY)t16DL?2WWAq#EuhzO0j34kj2g`XP?1g79M=Q7j+M|NYs={)=j;G2kdOh#S+);Ds^ZN3I zcM#CsYG-Y3u5UY;0k&Tc89ye(3_{+knTguSI&FjeflYD>>U)bfYZ{0 z3C8TG;^1*JMNqgCRRKAOpxBf5^;<)0N$s<|hN?Ve45m1m7G%^}elXhMltAS~xX3j$#} zgh4GH?g;f8$WJ1Rf>{9N>73lq?#4+I9jdJl?sYbI#nZ7jbr*Dv$~MH={?VWqlmVYs zFI^qvC=z7f!4v^VP&|U-7xcQIAW&8fa2qfmR0kn+Szv6u z(#@?uCT5CaQBi1ZY&6%-ZmEM?Yh#;OVwOOhDvCjdv#(X)YqWm_{(?UNe()vPuL1mUi&jPG@67r(CUJYCr@k5YXuapqW@2K7 zSQyue5bWRuP>_t#*n%5pu-Ahh2~P+UV>+0B2j-J3Vg(OW`=Ht_31Q><;NF+3tbf1d z-KZX;n5FvFM9T0$=I3tysogPSG1k*w8<#5vU=1kPD@34JV;Q^={GXP<34a*>K{xPX z6bKFlZsn^$SeA?cBS{mXbn=2tDdL=s z0X{O(m>ijPnC68h-M(CF%f5t(s|$CV>CzBL?r)aT!ziJfQHdVL2?Na2rn}UIn|RRg z+>^DB**UQt(t7B}RX?50S+*{?+feFG@Z|}IHKN5vrK0&(s zy1U&@A1E<(o?EMare&IGq99TfRF@#On2Z1e6)u<}JB^t#kT42V!trx^^yen=L)(~r zh6b|4cCba+h9w*^4#UAv2Pgt&D`2&{uM|OX3QQN^8lVx3Bp?uA%|$@5pAaDuaJwg% zUG?I{yIHez*RQX(vC0+67}4bO?mf>34C*adrHPm=f<@qCM_bfB8MWxg)z6=KDbRWWk1X``P8 z+`75^`pvxk)2*8~664E>0rJdT+YrGL*+^UtRn0P2q|^~ZU2u-DlgO7w2xef*Ncd-n zp0%aI9W-$xtST-m)Ztj^xlcykczf|3dq!V@f7ke3Z)QF{oxOaSZ^waV8I7eP^0YyF z3_k&T7>t7_#b}7U8WInz!eRrrlvw=km!S&)1%isPTqtBnNnOE27>uH#iUV27R1quF zLIuL1*JEufE%9lyH-7xZdRt!2c!2av=V};r;r>xX0OO2Llfz2>xbLTrdq3Rob>{n= zReOq$SsmDQwp*jIc9n-(W(+bfgAC6^hw5vC^Uqgbi=REJn2?RwmN}6N%P&11apg$r z)MWu}CplFcWt%<4rN)}zuIF-=ectf%ur?TU)gM_qvlblFwPyQvMU9@})VP;%N>eEk zYow%!uHdD`i8Zt!nvOeQi?tl_TYGjGEP%a5&LN4oV)z(EL}+*?UUWn?1u9rjR*Hk6 z4wx1J#VJq(%9NNQ6i}mIV+tBC$oVwnOh7Xz$tQvz5z<9|e@v9_)vM(k2jjm9GtMqw z8OsT6IPIfBLvH2O=*l_PlKio*Z%?DlrOx^HTb$ZAbzk46{oeO^w9Ri+Uz2S7GFmGA z`AhujfwjM2qNo7#LI0-;KuzT5iy|LP$}kP1zcz>|-a(zTWex^U{k-X8g_7lU3v~yJ z&Khi1wVo7USDnK|3F z;l{8&$IH#X(czG8I@Mm{+jf7_#CttY>kiUfv9i9zrvsmEw>!8ee#AoGW)mFq#yM1) z*laQD4fVQD%n`Vs??ZP0BqaG6I=KK zv;@&1iMXT?k21s?1=y%TMHy1A+^~2>1<5~CEL2AHE-ktv36My2!49sFDuMt-U>*U5 z9PkJLk`|Lj5hkF)Lh2hW^TTrSSTtJ$m*Fdj7(~b>+{I>%@AvN2gK&A0G>#*>BS$uS z1$O472Yvd#Te9?rRvm|lVI8!I_>HH7MQ40m_NII7E>5=2kjBN#4YM8EbkL^a_o0&L zTv7b+jfHHMDLH4s>Hmp;cqKW+L$APmEb%+bDa5C)RD7&egBv+}4ox}?nIpG$x7mr6 z9{un;Yc-{!=_0~mNBBqRK8vAb_K5OkaDHm}VN5XB6?JaNmba9mFmZL3OXD3;!*A5z zjZ<~0*BR$jU_cET&-uIf0S0%vo!TEid9G`-UdE*{;}uKa$V*vb9=9=mEd|AR7@Ptu z0nR_x_@h5~F%@7@0Q8Fm!gNGf_T7vHLDO-w@cv8%QmC!)hJ$OXDb~j{b-Z0wID%a< zBhKcndNA-jW(S{|cRD&Knw{P7mX&?^%3R;Ny+0rO{9()6hXc-Et-a-F>db9XgI5*q zvZPSk<^CPF6dicB>Z+fY{Wz{IioZDg@&)D{1bPO7Ze6LnX=`-9Ii8IsI_6DwuDYmD zi-XDIAN4!`^Rb_i31yizeC5de!#^&5az1C(+Mr(J?W%RtCN!49az&p^ns;?ABf6Fq z-Eku*aKc2(hEfrfkG9stVRf(e+JyGn_%@ojR+<>(BCrxs4Z5g%00;?Y=%)}@SrOn7 ztWhG46frE7j0%JaET)ikfdh>GHGToE0l1?ln;t*udu?0rsG(+68cM~= zioS4LZ~=%@;D#{M_FrK^F#d28P!`$vn;?$BstXmwpms*3m$>%$q22if`SWqDKFxb$ z<<(<}OQzek?4T{3Cx#@`Mdq=Zfw^3`g4z|@FtZh$AN~~K6^2T4ML)RO-o~lZoa%21 zA9T6u#@8!f?3%M1Csdw3cJL$Fw#GK#3_lFHdNghRQtx(yEy`oAQ?BS=N_2r>AyQZj ztd>tBUpoJVQgoX6`Gc4b`lGxQ7Q~_eg#{r$%vcblDo^Brrc)Jw0W3)zF}J-6z1fH^ zbUC=p>CSb*WDX=wJYRd`;}KVpz(2qABG&C^SNUdijE|swtt&SBanFx15znUIz1`}- znXDyy6UJ?g9zc_U(?HKhK(TxmL@QaW&^UHs4ol^0RSwudleu#Mq;1 z_fPBIpG;XY%drI}5@I@GGI0ZcL;QlM1Z)JTMm!Gl{!d&0K9!k2U6uhW&|~se@Q+ zQ2`L4NP*rmm|i-)WuH0{X6Z(uMtY=mHZoo7f_ok+HTC;i8y^n500RH>f%iS?_K+yg z`bKB~y`X#uaY1O(k?BWa=w7dS{;=1XOSLv1&scCMeeUV1YaWfg23mzik&va=t&bI1cvrH@k(?%217#=|i!sEsw6^JQHiB153tO$rEXoEVSVFx@GsaJ#* zn44Li_>?B@#fg0ku_`NC_2;BR#o{kitFzhFw-2YOD47no@H&%_am+9FTEi}z+-fZM z?S3g|Bm7;(=xoG$fK9M#D9UTE)Y-klC%?aGc?|Gjo=6GefNMeNfElFh5R?A%Kv*U} zSZ)in7S(2$6enGXRE0r(m1XJIcx3+>z=`od{xARxOK z<^`1D0C)k+Cp9EOa-@&tj8M8`lPuy_+C3_Zehs9McAA*pCMl?to9)_oZQ<_w6DFRi zvifG1qdzZyh5Y}@!7JkfO0Ztjf!!}(;O+RxQfof8#n?By-hk-7m~sDZmy?$oY`@fc z@6YSsEb6@o2f(T`!~SOfyL(-a9Y`L(Dy02v&-$}G>MjrbZhxsUce);j)iM!HXe}P} zh3A6Z{hlt=hS9?;evozAT(9~&A_tsKpL?_Jj)$F2Jsp1I#~Bab zEPnQO<%>59pS_%X?{S}tcbXl%n7j69shQh~3|&#EGsd7%#0zrUS)0&OimoSxVx6uE z@@!>zf2_fnC_2Q8_Gk&BeUj*yOk7Kg9>7LxQhb3!v+s-(5!^u|P#aKMvdQKxayjFQ z#3WTTNg`$yB>NhIQ*$wNkPtuFuFlr5AwTrE@MII!eI_mJz#HDnKesJ>_!w&}d|DjQ z3N8UH0}TT$M>L8j^-o+tLF9|D4w*e;EH;H-TgxDR%{2*=?V21*T=aR%hvc9nIgBD2 zL4!i&Tdj|82_8Dis!A&f>#P<+Vn3cLwKW=ckm|$=3Gj(cHIY34e%qRB0(+e(v*?Gx zS0Q$L3F|ulS|@|&4n2Oi``z>LcTQAX3Y#;@zGio$k`1J=@}dtCjB?m(jx4KO^FyaR zV?Am-M1cR#U_Owi^7y=qcUM> zt9q`)bemsxe|$RY-koumQBzv2Bx(dG5)fRw*}G!&?Pkl}dy_`34(qVMx5*;E=Ie{( zpU9r~a?;%!n{MDpoDgb;ohnH=bXC$OyLQG5o9o$lq;2*zw;G2_j(XH?|9gz$?a{s5 z{&LrZUDLZw?_H}`=iCl0%Qf#>wezUvBR3A+@NDyQDC1A_AK&kM`cmGyLy1#1g!EbD z*?OW~E!3m-G)_ikR5O_uu!R)W z(5K3Q?uhM+zGKU4B{U|xW6Aw(iJIR)N=)VTeb+#7KAiZ7g1P`lepa`F$vB%Tn41G5)=nD> zMW`o*VU^&jV&UpiF!r%F^&sQ2$HK-xT5$dPgkzgry0wu?RF^`^iavPaU`_C$&;k^} z#?y5sZOhV{fk4zj&2VaP8uO%f>tcM1v0G+0GN{Sm_p4tYFS~fTcjwh1-P?qfoV5dk!(cw9;AXH*}83;fl@D9c)@C48{+$!MT)f^Sp z6#wv`8eRe^z1m>cO24jy&C4~HB2c)MCAwvZuBhY9q3kypwatA@(vW0Z8`$SW`hv&( zFCiD_?cfC&Qt8Vth|ZwOf%|ME{|Z7-e=4=!8e) zJb^%f|4#~g3m)&U|Kpi|#e&cQ*3cPf0aO6XStE6Zh=7fHAoWKlu+Y^VK?Q9v(=DA! z#yd~+$CQWGSiQk0ae!I5akkawxiw!?xX13;u_rSZUuw7=zv}yG=^Ft1__X7#Dl}IV z!H^n3DHNSg@=KW9dg9NU-Wm%=Oj*!xq=r6hdv~k#p5s|__9c(m89#ha$7_+yu9aCg(F&^-C4x(5iGHBd$)a--m=X;_;YN{I!^lxkH#tSb_-gd^i58V%YN|_tpjEJ+n6d%a3Qj zHS8lnijVN@V7}y;=@`~@%@c#W8kL&s+UonvRk|HI2V+=YJwe44Z>_m~ulwoE;RDCm zRO+aSts@n#Ci-!LBWl_LQ21RVkeMexG3ptOie5>KdhEh)IER0J#8A}ui7A{*`iE9kBd z>btl2xDy#mE;rcrpznpx%ibVvy*&TYfZBb-oP$|Uu!Y@I`~vpb`^Fk$1*drD+~Tv~ zHqWQuec1El?UuVP)n0q1%Cd8H*FG418D0hlu!ETjJm35B$++t$v*zzl8F#DQF5Tw$ zulM}awE>Fm=}S57KiXk^uoTRD*68tS=a29{&nMiv+w$P|*(>(PPuv{Vf0=Iw#6AcB zM_N`MWtoRO()xhjPr9BP-wE>_xs{`ry`J!JYtWD}*3~*`5*uhDQJ5i9Ca(<99a&je zR7@2ChwN#RJlm!5(UjTmm;YF~cqIi(*r}M9jqBcNcl_4u>n8^9+h1e^iV|UGDvR_N za0*zE5lYdR3Sa<^*6PSx#05|aPb^D9=Xax4!3=ymm7wxGxEt2@b8U4tb0zTaVS)1M zU-7?b+@?oEF6}Hf5>;C$Tm;oY51VNs+G&dQGEN?Clef^b?XKvt*Q##+X~EOmd+vxF zC8&xFapTR?o1)M6d!9wAbERMRu{PDZ8kIyvF_!ED_>u5oleH1^%B7xk77wF6l=lB= zIUoASzxC3qfIKJ$?SmGeK?_)3h&cj81ac0U=7r#GVQ`T8MqFH=1=JTHL4=V4j0RQ^ zeX587^~La(nkcLQ(AOk=gjMbohk6S=+O8|ydw0yZW91fItiSD1-wPiXzlKLZ#^dOi z!@a8JClrd)$Ym9@o5;r*sDq(++vK+1H*4=NSV8lW?!(TXe_HbRhf!DV_CNh@)7uTh z*5Rw{U@Y8XE|}`B=M%POC!~Dy}@3IAvRrA!`GBuJr4+K6t?CGK&#kINCbmJN2{c z0tV-@Rxk7JI>Mq-cWpA*5ePtCDX2EaAS6G`aA_z7x6?%RF-e~8Tz_Bugy)m)Olyys z1o~Tm5V8`oK*K(4`A|AEWniO$KeRlw+^1cC^E8xZA?QGICQ4>OFz-+Sxu}&of`TS^ zib5g%vLqPLG{*=aU20woLSAc*Yl~AEOOUejv-38@|C&*2ei(6Ocib41oewZe!#t+0 z+IXzXfu#2|=Xz@bd*CCCGg&nFlKtkAo=Y&H6+hOqKVjNZuMYUV zDqx_#6pj_e71{&K&%n=?xibX6Ue;9J|G5`a0bCji2o+#k78Fn|khu^X0fJ&w%CY$} z&;sP2A@%|-po|F7F?cD`AW(Beg>ZH?#2^eO;TMY_1T5?}%%b8X+uHNpTCMT#xifmy zvGjT8Yi+pQ>FD#Zw?D0Ti@=i(kt~0-pxgX5S*>C{qG=E%zYM{t$R)_FaLc?Fi~BBk zwEYoq{_gP4x~IBwkyxiwpMn5>5(DFrAiCZ8(DCf~2ht`VDL?D`2J3G3JN9|Y+xuG{ z*jd@(Y&f|@NfQkmc-fCj68-b5m*SGLA~P5FUi{&}M~wZwns)zT=aW}!Z9SW{;zY)> zTOE!++w&afkqEIiwbVV--RW^=Z|uZHo*gFG)f#4=-Os#CZ_`rwCh^^ki+3}L$v2K0 zV3s`Ew)||jdRwCgqX_Ea{EMthxH|e@Bdx~A!yjMo`w4Gm#bth8{`ye-geeX+k#?ve z1t5h27KHq~yx=e;La0phq!rx+TW?W><%gxhxD`slW92GkxSl##;)&wF>G8CAy3M~t zxP%zufA5sNKMcLRr^GlUw?|lI4>m75%&Pok$C^ug+U|%Nak1jMCqu76q)oFLEB&Nh z#l(Lz`-dwY>Sn#WPrPq|oxBFP7FRpgnY)7nYyRef|~ve{#T! z9{!UTRDflKa3^R?1q>Jwr9&(`1Udv?plAVPj@Sxu0W#9zf*FDuIAuH(xPwxnGa@xC z)Qo%}R)c{ewA2*qs7(M*9%4~pylsuyuFX~y>bfO-@V@woCo&g87p{M|?_t01f1L90 z^Xi|0E@;G^Ww*!H|3(=hWUc@b_hrVJlnzhHui1U)u#6JzOS-zPw`3rUwdByR#Vshy*fjs zK`PCYRLW2p3dxWLgb2}~G89sX%%VhQk~w6^P!b~2q%td0GM6FAlzEI4=liX_>O4K3 z=N-Q5|G&QPd7pQ8-PgWW`|PvNKKHqQ>%P~zr{k8354D(+Y&<#9Xu=+Yu?Yr&y9`Dj zFb>YOjJRmO@{U*Hx$f&Q6?{GU4R6Qu&_{y5*lTL_rSFN+i5M%16zNWa_pt*eQ=^r7 zBPa#CG2k=2v2BHL!=d0F4fe**!H)P*Fgw9x3*krpH5T2DG(`pZiKuVvutPWSqT`O* z!(V#0b-_21e&UlOPb%hL!~CCQJPBnG;&n&ElakqX(bZmio=(10lk@fA(TB~|n@JxM z^FPMHZbA65-Y+S?=AL@I_2SJMqoWl1Oq9iHy$v?3knYOiv@Xa2!jG9ByPZt<{~z1@ zc-nvT2M932S7P1)W)U%600@9!5wS%9x4f#eh)_X`p#V`F*q1{DDSR~WlLGSC`g5dd zUla&P^YId4pQLTD*cO4Wa~nA>QtG!_ZRlpLk-H6o;k3=LT9Dr<=7Q6ftAqC43dndk zqo8u#1MGnZbRCJwuwDk~P6cLc=^`2vK7K*OPP0FGp`5HxTn_;nry3owQ1VRj<4nz` zq{`PZrO)SGc{1%n`P4Jdmlxya8cj6V1c)myCKN)(9wa^39ljeIuzG5BJGXdA2(=;< z$YR%)lNJ2#QXm+s=mj{9$X=OMeg0c@Ar)Zexf|~j48`%F zTKY7+IJd)!_3B%HDyYcUgQj&L zxwRR>LLw9#w(E{Q*FEk_!mBZ^0a$EF}`vfSePVIxO&1 zh{4d75hHCGQlwBH-VdK>{ZGez$-lq>&%+=fMFCq5(4{dN0&*yTI$*j0t79&_N(d(- zQcC~?Ohn*6f;!+k=7IqLgr;!zK_ej30hJ+P4T3rTVoTJThE+U+#zH$TR(4&j>b^2(OBL!h2{JUrJ&S%+Aw})-@wjXM&V#L^n zb%Tfqa0W>H6}_b^(0GV-W_`LIsG#>F)(T4 znj71TH%?nWqWf?|IXx6+Q6`G8ix?>qd3hp>lhUcCF;TElxAwCizR*8vU}sdN#oP{| zrC@oSUS5;=_07&F&sLXIEWP<``JFeL9@iZBg0(HS=*Q{;hDK_evr#i;_3Qxi5(A>EAG{I9*MqWWiP7cy|P4Y4OD#Av`>@hyVem3z*9c+eFX^u#2!v)I>)iWFALjYUezLdC{HB@7_aqxy_oGc~p-@ft(fSp8uX#4Y^a6QEW^U z^%`lx$f?|0foH2Jtl2PWt&Ug|uPTs6cq%aw;Cs#WoK<`3hvb;#>C78f`yb4*TCqbX zc&XB$X>vURXxfE2DpDc>3VW@{=)?{oetN7&5J-T~GB_q+Jj3#bx_v2d=#-^N&$h3m<_-d1WzL6r+%4$^>S0h zmf%Np7a4eonxmV>H2F@;8u!^{H13SUhLiFk$#)5F)ICzsX%SM5RQs=O zI%KP^-yWl&gBH`X+DD&v-dYj_^G>2KqlNhag!^L;4LZo9gF80g8WA}_7z!5x&`zUu zQp8>?mn5mRPs49q8+16Y{hB0$DX~qwP{1u%wj;JUkUha=o+9K=6$U?CrA+wYfP;Gi z{s=7*jjo{)&eGa$_32%ISuMbTz##A&5IFEQ+#rHd103MRlL7&nj%frW0|E$$?nZVG z65KeYz@p`W1!f%~q#R)t$R|QFsU|E{PZUrUx0%qadv{8Hu9E>s?TsX#==M5h_i zW`wa3(G2Q5`e(l6VQAhN$8{Mt^V2&-oa(XaPQXz~-jC28<8_I;5z&&8I#Jp&Ii4Ei z*Co~2)hCzb&+I?V(Xe|{(pZUzHAoXZ8BGJBwt-MbUq(}pXy_9y9Ce5~G>9UR!zAox z?Ye93ju+W4u;){i_@;99wfml_7acd9v|n+u+v*FwHeVgQ@6qgw4^tkh5(RzIRG(;I zfRB5*_wn7ZQ&`j&4c%Yp8h@sHOm@cw>22p6Z96B=e#K3%LmyM$9A0=(l{CU9>XN3K zL>&!8%JI6?Pki` zAx&d>;{hn0v{}m^9y;te9Wb4W{L{>~3-UX!C~(?T?6&8g-|@#`7hbF?skv4&qPs69 z0ZbH{L`|KlNRb9n0W~6G{2SUQGn1h7?`~R0E0IVRaH$f1vzbp`#Ercfo z;ekO+7XSqfiCZAh5-U{A0S@(@l(9^}cZUUtcn>HAU4a;ctzcM&U=;Q)5MJJq+otO& zY}#LhO6(?+Wo?!>>4zFDw~-xrTGB?w%z`v;NlYw=i8*(~zj{QyGP$yH^_>g7`L`g(xC~^@9r1&f9V~m3@wRN~)#tHyzb3w>%ZCEVi$j(B zX6)@_>eP(rnhA_dg@(FRFEoMhLmD}~(-f))nnIN*%MyX9yy2`N)AD0-YxaKlJo(Bs zyN!p;CMTPRW^`C^#chuy``b{fL8e6Cj2JX0x}$A;G5?p(xpcAro_zb2N3G_jSWHfC zF%ie(?IR0Z@?msjj{$PbTbz6 z4^JE*pvSc-ha|vYhHS`l0Y6ehh(Z7s5hq}fvA^t}6bQfU5n*+}76?+kP>%)rWuf(e zdZ|*tk{MXQ&Lflp7oCpm43q(px1}B7n>f-=OojpCR*?!03HoCTowt8Yeizj{ycKC? zP0Scs5i?5yL>MjYzu@bU55+?h)2wGC7?0j<5_sHZ?hT)mnyjxe1DBc;11n-`Sx;?< ziH%$vxPU9Sl-&wER^+q)y5E7j@y&=%OA7Gj zP#ix`Ngjn4UUuJCKK;U%j4wJw&73qdC5EQN0J2(=c;|K8qpN-Q9I}|cUUS&8#;z+= z`=L<4k(M(Gdu%EhdgSr=v(F=MyjfrNdEcw=IaPGCt4Q+k?B^SMZzRu6j2s$1wDrK& zGA7zY6&lJIkf&TS!J|T}mL%VWZ~aGlHFk&YPdrsLWZ&tov1zU6Bp8fYqdp{B$z{5H zCu9;07q^73A6az{LQSl@xjbtXWXf34Lg-Zf;eCMcgAk|PTqJ5Hf^QBvn?emCt?&S0 zfEC1r{a`Inm+D@(DCUeJrVjqHD8PdOq!hCpVIf25&y+&_t`SuVOd}w{pHjUm0a>5= zh*9LiR2T%Xm4T%kR(1$GLF#>$b#%q7%P$YSG$Xn;#G)Z}Am%R0U9nEQGwN7+yE!|I z{I}|RCtC!caa{d0x~L|v){N+MAQl}+OX(4C7;EK^kliQquUs6kBd^=COxwu3u8Rr= zY$}^~2CLnEx=t;LQBMs!?EC#ZR=3Ed+Z}n5Z&}|yrhR&I@b#y|A0_EOBqOm)v?XT1-S?vJ?^0ge@=H72V{N9* z!c3b57~Jwb{CZP4%8OyzwEs61pA<;jMR22_Dbk=F(ZGYN`$4S`^@(*Zmip;pP zxGhAqeCEZo4jcBFPK?*}=Q5)gl=)?R+4l<9zsi1fdF_SdsfkO5L{028)?KfUji8k& z(KR7DW<Tao1~moPUYCu?HbnwUaorG+1SeelJdN%>a>?K^3|IcZZ9l^Uu`eHJu>~G+pa>l-Pe7OyjV)z&~BQ}xWJYmkGf@R(hSDQqgfZu^jw$O zc~N1X_|Nf`JH~A0rf74hJV+_9uz1k?A1A9z12glxtT@ziT1NYbvJn}0W6XfXkmMFb z*Mbw2wJb6u`>owvMPc6g8uMXaOtX=rQR#S62E_@i0hm9AYxD&CoBi8n$9cF+Q#GoSOVa0+gMT3*D zg1*vwOZA@Dk=^@x(*;GPlk;x| z9ltm2G%yGi*^#utd)pC<#r-3GsMg*r5nO5E#f0Y>dCM7Wl7SYTfb-wt0Ez;6HnBx|6a zJT1Ti2La+0(He4wTvx=9AGcg|(fQ$?K0PrN^C2Cb@xTx<|O#VWzIT$ebbdkDG%QqdyT15a=NA__uIFus;?QJzodWo za`gSDgRejAdH!bOgUW?BDkh&T8GiIizugyl#a|nCP;&fRNT+d~iB(t99t0VKHG&PO zcZXiS-1QX8ja@9w#|=U4Nh@FVLAhZ^e_s8G z-0DY5if&Iham6#apl|%C9;;7vkG(oL>Eq7l+b3+sm7PgDP#es1T^xM}%Bp4Huz?GjxGS1ABKMC+>(;S&rcJ*K(*Q zE)PxvKP%jzaE4k5R8Y|i1+EQcQ1%umpz1)CsspJ}^q&d=DIypa*s*T0!1P)Jr2s{M zZ6a)(av3+sSEBhE@DX6f0LKtO1%EoK#G_nZdh6(G0}rB%1}3q6<9Br=7EYv7S7MzU zocMad?F_4VtLaQ-KVPZqDz$#dk-XSv>)S2mSC(DuOxoZ|kaGj-LM#siCw|YbE*X6+ z*M8CQw&8c&cK%rV2sY1=sRsr$a>PQTJ89RAw6iD3uCeSw+Bgujo9X7))IHF^E3{?c z><$wn?4~Ytj#xi<#ojU7PRz}Ck@y_u_lG?%9#6S&!|O=N#C!m-uY36npIZ zy5n_O@&j)Ex5mW|#I_wVE7^YQ*@B|$o+-sclWX^XOdhwZEivgr+Deh`Kstc%6<}HQ zQ*b!7G8Y=><;Dkh{g0h?SaYo1JZ!+`cU@i)n^K(lQ0;4l#=8lVaUrw-I;QVfj|;+6)~Y{oo^IrBeD z3rHa#9RLL&d<*D>IxN($m*G2r{9`SEeIYms;WdWq1c4I>qC;^=cr!=CaWCIXSH>EcT4`;e}EemmNCC6@1wy?M3q{$;lvSUw-Mo_VJG^80~Dzs5a^s(%Wr7vh|2&LU}TWUF=^Dcn!+C+cE3Qu(u(Nf&zWDG3A*ET zW_NHVox2fhPBLv=h`kHx0q!^UL0siNQMoPh1893Kn*mDLMDEbmZ|9 z9Tz1u3ye|kkBMcftWB`ka*VhY+TEh0%t*00a>U?$Kms7L65xpll5NZCyP_@@N46*( z5F)l6FWX_9Z2JJQH6o&s#fh5#ZXz=TgTZCiRjA!gsMcD5q<$0vLJJI84cY~UOAX_C z!7~W_#~DIW$^YX35WpW>CNOh|+bpFJz(PQW%P&AFT7;4U0w_)h-{J_db(m~7bUsbc znRvoFrr160-O@+cz4z00zq9OGN&iEeH9cp`cf=ZPyx3~Cd}s9IOEI5xZBSy>fp;k* zclRe<1``KHgGdjMJLxfibem=}4kkz0;j=WfKz4pLo zY#!d7xN#&M;GQ8y{@9g$FzxJVZob{1GjxNt#}<8`MAPwUZRecq9&^cU@9i;} zkE4qvIX`O7e_!vp+Evuek{D)8O?$hn+-+b6S;b5dy*{z=Mf-Pz|o6Yj0xTn{>eu2+E#zb`3WOF|+}J5Xv0J!87wiSiq5fsWE_TN@b(DhBLM4|)N_~(Yy|i(EIN(;Q&<;K^ zO$NuP3`RSo<&6g}RdI_}>N{W2WlkelQ(X}ck7*70m~PZ@jC3At6u7e%G(n9=eP$7% zPahXA0SmcqEx^s3e-x+x+%r%FY=Mj~2nfIs!dFHR5ke+VaBlkx?(#4bXswWpuQb&DBr(0`_-zUAeHYh30a`sNcQJZyrb{dX8XoifBB^P_` zyy=(zH2m7Po$p}Dd^!I4aB$K*tIz?&9uf`^4Iw>;5>)G{hk~-;4uPqhezTvkP;3 z5gRr+V195R?aPzO-tBsJW9R{_g%eFe;APmLIc%MV$9j#S8@0T*X#2(Mj6$-^9-|PH z^4{M(EX8#Cp%$|bTZAJ*<7n&oC9A1Ew?fHJgI$Go%ErZZd44VqY|F z7$>$x;rf0e)1E?uPD0K00yUKC!s;025F!U)6~i`wh48n80JDhL?hw=*z9yDG@DwA6 z4jaXQ00QUG8QDgthKg!VLPU187%px*Ue<1we6M9n9$VGMrZtbc;hY%P)F({N)>~`} za}*6&M~GWYm$Qvgb4zVC16z>@R9j><%Zv0GPWt{r-o$mRoCl`9N7K)r>=Lu9*_hQ$ z-IuAju2k!XpLmCUAQBVM&7{C_`wh>dNX7ja`%IEngILg#O}7sPCoZ<1?Jej7QFkW} z9>fvy&miVSdV$<3hU|6KUxWA=v+2P;1$J$t|J+540iACJDRI{oF#rK-|Bw-d)~A1-$Fqz^_Rk5BJQ z?69tvq<$*svnJ7W>^1}6MALCOoulsu)7}jewfyica2eh*j5rS@T~V^{<=O|u9w{g7 zV~)3*pJo-FZXI#FZB%xLrForJp(^sZ9-A*ZCtP*iTik#DtwE``J<{%ar{5oujdGx6 zV^5ZiJ6RTxf8YPaU9a?;gHnt7CST~e<7B7x8J3Ip8_(FH5wJvQ;B;Ap&)EzVwM5-T zw8w2vtM8&61~!OL5uLhCx!v;LAOOn&lJocYlUhM+I|!>Dwh)j)0DEOt0(6i>;bIiC zMFCqQ%oV5yI6~BZtSmOAJ0rmsnPtPph(bXaBx;GH%7+o+Vdg73?9d;5uEz!xX?zm@ z2$D9ExQ+Nd;fyD7ybzxEDdlys`@Vw~(_+;JFF=-}ygh=kq1lm`5UV;YUMnEkcW!Fe zI{L)?jF(GqUGBfPa@pP5%pX%UM?<>&$pAk}Za$lxKE#!gpQt}Vm|nz|-W`!~eaNBPUPtba$SMy$^K{zf%BUOQ?VBx+-X}i) zeDqya?w9YUs%y^Hd_P-*1$Aw~&zcKAYc5M_uSkAe`dM?X_FLY!FUQ`$Pk8=vc}c~@ z!rOz6ob9v;Yr2iIaV|Zh>UrFBCuqOqBP=hRma>7f#qNi_;z+rRae7 z28pc^h6i6iN-j*5?=n}x2?atosQK>D9>3psW_ru${Eq7ioDzzL9J(8r_he4-`!!Fh z_kNHZuZ5?WhDIZ@DD8)2XVv%RsBTwqdC2bbPFrXXg_DwG@127Pvf9ww-gQ}rIX@0p zT^+FZP|F!xbr7*O;-*vF_kAzV%*_sw_wXbR{-iHJI*PdA$Wi~1q#^Yi!QID?xPoxQ z`a$0j^fKVRFV1)pry$v3=~E8E(E73ebLoVfv(9T$+l1|IK4zD3K(hIS^mg;kImeZZ zKk@U-kG&q7eMp}XWB^BQex&ah`N2PLO5SI_{c!AcResfvE0UkZk}v0~ULJm4y7~I) z>6ytxgz9CI1jP??5sLPwr3fcy0k?aEPpTn9|57gUK=0k^4NhUs-zb zUHbd#yzj49JuLOi%5S$eK__&v;*bz=w;|#-J%xrS)rg-P1>32=SV-$a0|K0z>_5^e z@Gqg-5CU`|znfw~zTEcAm$(Fzp+Ucr2SoAak}L0aKD*Rw=WfHGg-Xto zWILef4GKUF5t+aZ=ZPX$qBdj2HdEy6=QeU)sx$zJe({=P5)DI9{2{Z=;(XgR=Nz_O z>X%eBH1(SI(Q7_Oi-sM#?3Q%Faa*qK%Jeq#kx_WO?Sc!=+wP7%QN8#5=YyZ-nojf} z-H(qu_$u;FzFiDHH$i`N$$-S_n2L>-QJ$oSFX=Us^aExCIM|LM137XZL;i#qsDWi% zJeu?$Mb(7M2;w?NdwkNctq-@{gXCB5eOD22<+}gj^Uj;HIxakF9iC=28YoJc>ugvI0E=8r7|uolKwV+#Rj0W61ELIGlgxCm;o z9vYya=$Q;sickYq(Uhhap+yC1ARy+J`q|O#1<}+<2}C}Khp72PafhXi`z0HP77tAM zk@Pix8@|i0%`d4S{N$@$FK8KoJjv&Tm(P~px;rJm*#GF|LHjPaZa?cB z2M84O-g3ol*GB#m!gS9=%I_`TcV3i-ZaYGdEFq1aY4N`46C~(|s%% zG>#0$zZ`jtBOYTZ4dIS#fN?__JeCX|M}~mv&*Kcng9C^=fD_+odG*Qa=Mi^{Tn?rf z&ReWFEKqFMPt*dPXwa1vt^qE0jLJV3GWRP5d#I-2Vfzi zh@jRKh!8`iO(UtqIx@1bkyz@n1+DUpavutS0Bj-jzwi@Vpa(0ONT#=nyz8GO$(9`M zyJHd=G@T4@Kt31;kpZ8Nf365G%C=sLil!6LP?fd-&_|g&)W*!H3YN;Fiac+ddhOrxd(j{pit*g412rq3Tqw?eY^g%g%J$aAiQ^?SLG_ z8a-Wd>vi1YPl>O;9r^qt`#UwOkcNpo8XO_pXC*W+4}>$IGm_ez>aRyWB5?+#10F3d zDxH1qZfO3U@i}+KWR;A{x;HxK;kc6(q352=xm>yY_UkQWA5&jd=YIWpUQ&DV=gaL+ zug|-f<$WODc15(#l*s~L%nbjSFmEvOnnW0RPb6Od4ox6l9C=P4l*W@Ga}~yX$@=s% zw!GN+P?Fy4h(-f_#O<7f#vOzjToZ6~2jE&`a3&C&|CvV&X2ajcMJ$1n*fE!A-X71$~E$HK>bw|CM_ME3-@l$hy#BRR!Xx`f`PtJ7Sv`)<{MBHJZxH+kjyP;j>$Nl2=g^J0g-q?(4zWHMD?H7^PpUt`aWJUqXQveqgQwtwY zEkG@6#Cev_zEVE-YI$VQqo`};3$K?izFxkRqvba$Vs2H$-T^(1z4K(vohR!`pR6x^ zw(;JJxCgIxRlMEz{O$f1A5vd`&ia5kwx-}m?fIWy^1oK1zu5BPQ$gt|y|%7zF+W0K zOeh%=LI#Esk11qW81b4$yzA!&Iyp=uKK~|7B|aQ^PbJ=AgdXdOotGYQo{q!FaL{Dp zHH8d)pZ4nYn(|BbN%86vC&@axi&}LN>RJia;1q)igx|X1Dj4z)6X6GdGby>H3IV5uDCfg_4&-ppVvQ@9QrCruXz*ytSIbU zs^gY*TC?YqF>}a>NHTV%%CtD+=p@_q>HQMVj!wTCcIIZprBX=p<_FK?%b&%Uzu58k z-TqhaQ{R2c`1CpV^Vd^fzvh4adhT0wLCtqiVNG>m&G(D7pxTQ+s?UG_lKbUD#`_Ox zZ(b!nd%Cr(Y+Y&TlHwcTg%>B}<$E7a@3(uO{l=}W7O&Ktwm@M##2DBIO(kA42)bXO z_43HsbRG!ch)oEiW)si|&@4(L!$|<92wX8qGb7~x_{ zI3Fo9b}k9T41*7vLOj{$hI06tN-;Hj2JxFo$sdFZMDS7#eJ35J) zqhV7EfinC-sLYSq4<<1L0EPjk$G@ctU=RSVscWo0KthTF=ssu!7Ba`}22fUD02DCC z8G`^q&C&U^zLB3UlEG1HC9;w87OhbkmeIvS4!>Vl0Y#A2FEN4yMw9VNNC-WOLr@a% zlEbdms@?(OcD?CFoCexpA#)$)YgimRtNn-$M^*ymcC_jUbb)R^VT-ZiP74*?Hmi<2 zWH>Xs<+5{~;)`7mJn+qZIjy)V_Niq5SKJxa*T+3?N*5Mi7?*X-b$3Fmm9eU`7L$;9 zWaKR3Goy|m6~kvz$;8ob4)LE$#R-z;5BM+{gVb?^lMqh-*&NN{F2-fJn3G9}C6&y1 zWK0AZ1KiCgJ#RdJHOd$4MUv;ZQA(1ebIKwTFzlw_nW zOT_KxHgZDgIn-q>bWA{*%9@m_+T-7|2P7{f<3RICfMowSi0%@lL4G1@%VpV*$s*g$3opPjkR8H%d zvz_9vxFp{lmiZ{CpmNU5_p2+u?tCLTTrD|KXUu;){Ppe57f;tcC|OukJnh`M(HU6- z_aAoHo@~8#yV0TzO~Y3Sr!6NFqRH4O5)es7MNol(K-Lldtv`vu$oXW{d`!RGaRC{F zueOMc2f&w+Nvq_hZc?AO!(?fK>57#08;?5f%5hIA9G!h*TH(FO8?QDztwN=4JQWk& zoF8A0eX7l@hVr8SKc;+ziON6_j&lLT*Xu=?60l$|J8mUs3WV?(yfuH^t$h;1B%&8!9L2ops`5xNV+B@zMxb*lhwo`kggE&qN+LA^R) zx)17)*+}@8ZJB~CfB>pbFy9%Z6q`b*xB`nErH@f}3uPE*$aPz;?4F<-lGSGU71#Zb zLN9&a^$C0RrBT=8bmxEH{4S^U%J4?L5o^(1q}M^9jwF3}A5g>q>3b-xNl7R|`#CgK zLGm*CL}MEYO?A;W8m;Akf_@@1&NtU#ylj^#ay`Qp`a~-Zj8XB5Qx8bc4oxwbbJR5Y zgyrhfwwnvO@4no7|Mfvf?+nkmKjv)tq|47{-FUU|-usw}PjSzycE0_d{OLzpHB}r~ z_F}brful31xKi^yqx##iZ(q}@z8(4UEv>5R$d{@kRn?i*-;RI#dh9C(HCf+lAzgVt z>pT%$@gllv#;xFkH ztW{Kcn+3qH6x#GIoFD@rq=y(3)4;qopqDxL{C6VlBWo$tE+_K?gs zlWt4mzWhx1a@0I}0=A$<=3Rt3uyD|C6!m^tfpJ#HfF2Q{L^~r0@e&6FbR$9sc_hFh zvK{%bXeeypLc7DX7%Xb#O@kh7gJipe%JrDp$R$#t|6=9At5ke8sEv--4Bn+ZIaz-pL3marYFOzUXGge2&Xi_`R5t2S)4DuUbwtyCGfLd9)`$^c4@adca$f)Ka?t5n=$ znhc3m^;oOsxnA7|U1Q=jM{Ly!*r5}&OK;LX!|ACe5m{|koweI`t^bjS<1bZj{UAyD zex%3d9hS=~0xzwVAAwasccFom0EMa>p;kLq!GHjMWu(($(ql6r*V;p@ zyL6@;Gh1@OZda*Ce)at4Rhuf`$35GtGk>DE2l!?y)P|D9gvK^P83ZuhF)N78g#Qf? zXb1;3#o{VVw?kq&dE~K)y@)cPl@(LY4 zgJru;la-6H+9HH1NN~zC6C0BUg7IU;j*$q8e zmMISaEms~`M~w%f$o5iY&;Sr#TB78J>!X$WBjj>{qU(Ic-jNEu<|#ObH*!QY@=W>e zAT%yPFB>Y)oJdcUMd*G9D$=qxX!;2WM=mPlIYhR7kgWA6aT{Oi6So*f?LCBJq0cOQ z<<0Af`prEk(U;IB8(ks1xq5W|2;34|qL&$L(LgaGxR6%XF+|pW3UHy&ce#r94z0Wp_P!X0`$0u(1!BQtRM&gCdU8TzlIQiPL!^QIrlN7 zSEM5Y6kq|XfWX4G0&MA{Eu(gKp&kqs1k^cmq43T^V@;83p4yEHp6_G%*)6u@Ip9k7^qM znz=W%7HG5+Xxa!+Fi_84q~BF!(33uiac`js8Z{!=6{-py>ODj)Jz+T(q(U$6Kq^RoB&6%s5h@dcM$?96?Lf$+z)gHXBJ1HIE5vo6DU6q>6=FP4 zm)L{aY-XS#B2$*eg(HNyZY)MV7m7b%_P|QV04>bW0}I7E(9X$OsDom>4au21=^{i> zB5Hu^aMOFKt(n7Td-Ad-74TeKSAt&2;Mpfpi(Pm7J@BM1Yi;5lbfi8C^!I_ z)JH`ofdZl?kbl}1mHGr|M5tzoiCU=IN~qd~cBEA?5hz1+)n!D;#ba~6RP3?W$_Nm^ zfEuFph+uF47Vs882Ht8^`A`>#kj!7py4+{)voTX$2liSl39Y zFCCkL&@%o{34Mf7rL&Ic-Hdt(4biO_TLdVX%MpTXbR{&$32DGXolWH zq=_!T@P?zfGzi^);Ai28R-9&ar!n|F(F<@TO5%f1p%;#ou0l-|fJGK0QvA@E4@w!+ zQCE(7(1!uaEWAY4qh-5*hKk!d2#s0_Rg7d%N|%NM0Rmt+4}o7$Lk*#NTL=)~7R6Lb z!7KC}Q56W0XRRqC)|HVpkdbf3#StNt$cScPH`3=wM@F7iBv9eX5nx*tZ>PjbJ}`xQ za<{D;;7lEHKL>shTnT>!g#))Ag^Ksc=2H`hnxORqC9#@}tUAs3Mxvjbri{EchYLNL z_k^x+iqID@pUea(l7O=Ls3P0MN(dv0;*^H`3Dj*sLJ*DpgCaw;Fe5vm=075I{sq}{ zNA4QD3)B=YJRGq(zzB6~r37a;3n@gIR!RaKRnW8)NqHdDaNvA`N>)N8xCN1~%?Ke_ ztpo~KaKU539iecgFX2i`RzgfZcza{q4P|oM3z|?uuX)Txw67m}++#+f#O$15*kF7*{rY7skx2VoYYY#TX~qQHli&n1Wz;U}H`})K#`Yi6=%< zQwjq`83Bkx2cQ8&(85qq7HS+KppQVB6c?0W#ZeMa(xEVcNk9o{vvq{=?+Miv^f_6A zI2p*$7~0=1KmZ}k!g|7($|`pf%O9+p8WLm=4UAB(gd-6aR`n#*s2?|__DLOK48luw zuTtgv8=)Nx`v_=cc25jorgDUL3AJTVEf$8Wps9>Nla9qYNQ$KOf&{9N|GM9cPBM(! z_$}B%_`h5QFce@OkeWkGN~I_OyC9QN)18w~dWI2`cBx=9A&1N}_SW5#GqirYKWB8Y zYjD{wcVM5}5F;ph$*#cwaNz!}!v&{w=;DGy1nS`kP%03FA6<$I4jmLDD42sqN*%$< zqGO7h-xIVOG)tY_Lh&&`aRaoaz`xLjjOf2iTp`0S+hDvkB+M012ryfS3M%I^ zgAg(~WHSbn%sTtg9p4>;U527E2U%{O-J^$zkh6M3jX#+?zU8n>nuZRA2#@E^kU9p zY} z-v6AX>{Fzl^~>AYztSW9jqoWPQBZJ?#2~<)p&kyT~2 zPX`=`2$N?{GO5k*XGBF;Dq0XKDRE+kPx;L(Y&dnWFo@%Om$6hf*nr6u z8~>A|^fKH{dIvWC<^F&C7uVw?dtmk|WAY!b#2M)`NMCJu24g%41fJbfdX4mrcokzQ zb1?Yd`uYARKmA|-y;3=3QYSsK^Em$h`ol-FXJCk^14zB5V5SrW#qae66R>~h$V7+< zOha*MIA*UxwG^mt7{+ZWPOZxK-V|>c0h(VRjN1O9^MB4_x;a?pExA z-(AnmTD9T)yWEa=T45wlmis&z6>1B{8Dd7$n zOXrGzK9D{e8~me->CKtIH~666eIgV1dhy0fzq>PMsp951DLsN|{NLY|0}Qagh!ca! zKR+_@kP6Q)clbyD{<MblETG@vcAq;p8uuv45H9{_;WW-#@(`C#A1S-^)(2;~zho zKf`l?aeztAzvRZhdD*|b&;R@aJPD-wPyhbOOMiMjf0E|_&jFqTJO_9V@EqVdz;l4- z0M7xQ13U+K4)7e{Ilyy(=K#+Eo&!7wcn_video->initPsx(); - _runMenu = false; } if (_displayLoadingScreen) { g->displayLoadingScreen(); diff --git a/mdec.cpp b/mdec.cpp index 936bc7b..4efd5e1 100644 --- a/mdec.cpp +++ b/mdec.cpp @@ -170,7 +170,7 @@ static void decodeBlock(BitStream *bs, int x8, int y8, uint8_t *dst, int dstPitc for (int y = 0; y < 8; y++) { for (int x = 0; x < 8; x++) { const int val = (int)round(idctData[y * 8 + x]); // (-128,127) range - dst[x] = (val < -128) ? 0 : ((val > 127) ? 255 : (128 + val)); + dst[x] = (val <= -128) ? 0 : ((val >= 127) ? 255 : (128 + val)); } dst += dstPitch; } diff --git a/menu.cpp b/menu.cpp index 075f758..d9b84b3 100644 --- a/menu.cpp +++ b/menu.cpp @@ -250,10 +250,13 @@ void Menu::loadData() { if (_res->_isPsx) { for (int i = 0; i < 3; ++i) { - DatSpritesGroup *sprites = (DatSpritesGroup *)(ptr + ptrOffset); - ptrOffset += sizeof(DatSpritesGroup) + ((le32toh(sprites->size) + 3) & ~3); + _psxSprites[i] = (DatSpritesGroup *)(ptr + ptrOffset); + ptrOffset += sizeof(DatSpritesGroup) + ((le32toh(_psxSprites[i]->size) + 3) & ~3); + } + for (int i = 0; i < 3; ++i) { + _psxPalettes[i] = ptr + ptrOffset; + ptrOffset += 0x300; } - ptrOffset += 0x300 * 3; } _iconsSprites = (DatSpritesGroup *)(ptr + ptrOffset); @@ -338,6 +341,7 @@ SssObject *Menu::playSound(int num) { void Menu::drawBitmap(const uint8_t *data, uint32_t size, bool setPalette) { if (_res->_isPsx) { + memset(_video->_frontLayer, 0, Video::W * Video::H); _video->decodeBackgroundPsx(data, size, Video::W, Video::H); } else { decodeLZW(data, _video->_frontLayer); @@ -353,7 +357,10 @@ void Menu::drawSprite(const DatSpritesGroup *spriteGroup, const uint8_t *ptr, ui const uint16_t size = READ_LE_UINT16(ptr + 2); if (num == i) { if (_res->_isPsx) { - _video->decodeBackgroundOverlayPsx(ptr); + const int count = READ_LE_UINT32(ptr + 4); + if (count != 0 && (count & 0x100) == 0) { + _video->decodeBackgroundOverlayPsx(ptr); + } } else { if (x < 0) { x = ptr[0]; @@ -375,7 +382,10 @@ void Menu::drawSpriteAnim(DatSpritesGroup *spriteGroup, const uint8_t *ptr, uint } ptr += spriteGroup[num].currentFrameOffset; if (_res->_isPsx) { - _video->decodeBackgroundOverlayPsx(ptr); + const int count = READ_LE_UINT32(ptr + 4); + if (count != 0 && (count & 0x100) == 0) { + _video->decodeBackgroundOverlayPsx(ptr); + } } else { _video->decodeSPR(ptr + 8, _video->_frontLayer, ptr[0], ptr[1], 0, READ_LE_UINT16(ptr + 4), READ_LE_UINT16(ptr + 6)); } @@ -448,11 +458,15 @@ void Menu::drawTitleScreen(int option) { } int Menu::handleTitleScreen() { - const int firstOption = kTitleScreen_AssignPlayer; - const int lastOption = _res->_isPsx ? kTitleScreenPSX_Save : kTitleScreen_Quit; + const int firstOption = _res->_isPsx ? kTitleScreen_Play : kTitleScreen_AssignPlayer; + const int lastOption = _res->_isPsx ? kTitleScreen_Options : kTitleScreen_Quit; int currentOption = kTitleScreen_Play; - while (!g_system->inp.quit) { + while (1) { g_system->processEvents(); + if (g_system->inp.quit) { + currentOption = kTitleScreen_Quit; + break; + } if (g_system->inp.keyReleased(SYS_INP_UP)) { if (currentOption > firstOption) { playSound(kSound_0x70); @@ -540,8 +554,10 @@ void Menu::setCurrentPlayer(int num) { _checkpointNum = _res->_datHdr.levelCheckpointsCount[_levelNum] - 1; } } - const DatBitmapsGroup *bitmap = &_checkpointsBitmaps[_levelNum][_checkpointNum]; - memcpy(_paletteBuffer + 205 * 3, _checkpointsBitmapsData[_levelNum] + bitmap->palette, 50 * 3); + if (!_res->_isPsx) { + const DatBitmapsGroup *bitmap = &_checkpointsBitmaps[_levelNum][_checkpointNum]; + memcpy(_paletteBuffer + 205 * 3, _checkpointsBitmapsData[_levelNum] + bitmap->palette, 50 * 3); + } g_system->setPalette(_paletteBuffer, 256, 6); } @@ -576,16 +592,18 @@ void Menu::drawPlayerProgress(int state, int cursor) { ++player; } player = (cursor == 0 || cursor == 5) ? _config->currentPlayer : (cursor - 1); - uint8_t *p = _video->_frontLayer; - const int offset = (player * 17) + 92; - for (int i = 0; i < 16; ++i) { // player - memcpy(p + i * Video::W + 6935, p + i * Video::W + (offset + 1) * 256 + 8, 72); - } - for (int i = 0; i < 16; ++i) { // level - memcpy(p + i * Video::W + 11287, p + i * Video::W + (offset + 1) * 256 + 83, 76); - } - for (int i = 0; i < 16; ++i) { // checkpoint - memcpy(p + i * Video::W + 15639, p + i * Video::W + (offset + 1) * 256 + 172, 76); + if (!_res->_isPsx) { + uint8_t *p = _video->_frontLayer; + const int offset = (player * 17) + 92; + for (int i = 0; i < 16; ++i) { // player + memcpy(p + i * Video::W + 6935, p + i * Video::W + (offset + 1) * 256 + 8, 72); + } + for (int i = 0; i < 16; ++i) { // level + memcpy(p + i * Video::W + 11287, p + i * Video::W + (offset + 1) * 256 + 83, 76); + } + for (int i = 0; i < 16; ++i) { // checkpoint + memcpy(p + i * Video::W + 15639, p + i * Video::W + (offset + 1) * 256 + 172, 76); + } } if (!isEmptySetupCfg(_config, player)) { DatBitmapsGroup *bitmap = &_checkpointsBitmaps[_levelNum][_checkpointNum]; @@ -608,13 +626,31 @@ void Menu::drawPlayerProgress(int state, int cursor) { } void Menu::handleAssignPlayer() { - memcpy(_paletteBuffer, _playerBitmapData + _playerBitmapSize, 256 * 3); + if (_res->_isPsx) { + memset(_paletteBuffer, 0, 256 * 3); + static const int16_t indexes[] = { 0, 3, 6, 18, 36, 53, 67, 81, 99, 111, 125, 139, 156, 169, 181, 186, 191, -1 }; + static const uint8_t colors[] = { + 0x01,0x00,0x00, 0x02,0x02,0x03, 0x04,0x04,0x07, 0x07,0x08,0x0d, 0x0a,0x0c,0x10, 0x0d,0x0f,0x14, + 0x0f,0x11,0x18, 0x12,0x14,0x1c, 0x14,0x18,0x20, 0x17,0x1a,0x24, 0x19,0x1d,0x27, 0x1c,0x20,0x2b, + 0x1e,0x23,0x2f, 0x21,0x27,0x33, 0x24,0x2a,0x38, 0x26,0x2c,0x3b, 0x28,0x30,0x3f + }; + int offset = 0; + for (int i = 0; indexes[i] != -1; ++i) { + memcpy(&_paletteBuffer[indexes[i] * 3], &colors[offset], 3); + offset += 3; + } + } else { + memcpy(_paletteBuffer, _playerBitmapData + _playerBitmapSize, 256 * 3); + } int state = 1; int cursor = 0; setCurrentPlayer(_config->currentPlayer); drawPlayerProgress(state, cursor); - while (!g_system->inp.quit) { + while (1) { g_system->processEvents(); + if (g_system->inp.quit) { + break; + } if (g_system->inp.keyReleased(SYS_INP_SHOOT) || g_system->inp.keyReleased(SYS_INP_JUMP)) { if (state != 0 && cursor == 5) { playSound(kSound_0x80); @@ -704,7 +740,7 @@ void Menu::updateBitmapsCircularList(const DatBitmapsGroup *bitmapsGroup, const } } for (int i = 0; i < 3; ++i) { - if (_bitmapCircularListIndex[i] != -1) { + if (_bitmapCircularListIndex[i] != -1 && !_res->_isPsx) { const DatBitmapsGroup *bitmap = &bitmapsGroup[_bitmapCircularListIndex[i]]; memcpy(_paletteBuffer + (105 + 50 * i) * 3, bitmapData + bitmap->palette, 50 * 3); } @@ -713,12 +749,14 @@ void Menu::updateBitmapsCircularList(const DatBitmapsGroup *bitmapsGroup, const } void Menu::drawBitmapsCircularList(const DatBitmapsGroup *bitmapsGroup, const uint8_t *bitmapData, int num, int count, bool updatePalette) { - memcpy(_paletteBuffer, _optionsBitmapData[_optionNum] + _optionsBitmapSize[_optionNum], 256 * 3); - if (updatePalette) { - g_system->setPalette(_paletteBuffer, 256, 6); + if (!_res->_isPsx) { + memcpy(_paletteBuffer, _optionsBitmapData[_optionNum] + _optionsBitmapSize[_optionNum], 256 * 3); + if (updatePalette) { + g_system->setPalette(_paletteBuffer, 256, 6); + } } updateBitmapsCircularList(bitmapsGroup, bitmapData, num, count); - static const int xPos[] = { -60, 68, 196 }; + static const int16_t xPos[] = { -60, 68, 196 }; for (int i = 0; i < 3; ++i) { if (_bitmapCircularListIndex[i] != -1) { const DatBitmapsGroup *bitmap = &bitmapsGroup[_bitmapCircularListIndex[i]]; @@ -1432,21 +1470,27 @@ void Menu::changeToOption(int num) { // nothing to do } else if (_optionNum == kMenu_Load + 1) { _loadLevelButtonState = 0; - memcpy(_paletteBuffer, _optionsBitmapData[5] + _optionsBitmapSize[5], 192 * 3); - memcpy(_paletteBuffer + 192 * 3, _levelsBitmapsData + _levelsBitmaps[_levelNum].palette, 64 * 3); - g_system->setPalette(_paletteBuffer, 256, 6); + if (!_res->_isPsx) { + memcpy(_paletteBuffer, _optionsBitmapData[5] + _optionsBitmapSize[5], 192 * 3); + memcpy(_paletteBuffer + 192 * 3, _levelsBitmapsData + _levelsBitmaps[_levelNum].palette, 64 * 3); + g_system->setPalette(_paletteBuffer, 256, 6); + } drawLevelScreen(); } else if (_optionNum == kMenu_Load + 2) { _loadCheckpointButtonState = 0; _checkpointNum = 0; setLevelCheckpoint(_config->currentPlayer); - memcpy(_paletteBuffer, _optionsBitmapData[6] + _optionsBitmapSize[6], 256 * 3); - g_system->setPalette(_paletteBuffer, 256, 6); + if (!_res->_isPsx) { + memcpy(_paletteBuffer, _optionsBitmapData[6] + _optionsBitmapSize[6], 256 * 3); + g_system->setPalette(_paletteBuffer, 256, 6); + } drawCheckpointScreen(); } else if (_optionNum == kMenu_Settings + 1) { _settingNum = kSettingNum_Difficulty; - memcpy(_paletteBuffer, _optionsBitmapData[_optionNum] + _optionsBitmapSize[_optionNum], 256 * 3); - g_system->setPalette(_paletteBuffer, 256, 6); + if (!_res->_isPsx) { + memcpy(_paletteBuffer, _optionsBitmapData[_optionNum] + _optionsBitmapSize[_optionNum], 256 * 3); + g_system->setPalette(_paletteBuffer, 256, 6); + } handleSettingsScreen(5); } else if (_optionNum == kMenu_Cutscenes + 1) { _loadCutsceneButtonState = 0; @@ -1454,8 +1498,10 @@ void Menu::changeToOption(int num) { drawCutsceneScreen(); } else if (_optionsBitmapSize[_optionNum] != 0) { drawBitmap(_optionsBitmapData[_optionNum], _optionsBitmapSize[_optionNum]); - memcpy(_paletteBuffer, _optionsBitmapData[_optionNum] + _optionsBitmapSize[_optionNum], 256 * 3); - g_system->setPalette(_paletteBuffer, 256, 6); + if (!_res->_isPsx) { + memcpy(_paletteBuffer, _optionsBitmapData[_optionNum] + _optionsBitmapSize[_optionNum], 256 * 3); + g_system->setPalette(_paletteBuffer, 256, 6); + } refreshScreen(); } } @@ -1489,8 +1535,10 @@ void Menu::handleLoadLevel(int num) { if (_levelNum >= _lastLevelNum) { _levelNum = 0; } - memcpy(_paletteBuffer + 192 * 3, _levelsBitmapsData + _levelsBitmaps[_levelNum].palette, 64 * 3); - g_system->setPalette(_paletteBuffer, 256, 6); + if (!_res->_isPsx) { + memcpy(_paletteBuffer + 192 * 3, _levelsBitmapsData + _levelsBitmaps[_levelNum].palette, 64 * 3); + g_system->setPalette(_paletteBuffer, 256, 6); + } _condMask = 0x20; } } else if (num == 20) { @@ -1501,8 +1549,10 @@ void Menu::handleLoadLevel(int num) { if (_levelNum < 0) { _levelNum = _lastLevelNum - 1; } - memcpy(_paletteBuffer + 192 * 3, _levelsBitmapsData + _levelsBitmaps[_levelNum].palette, 64 * 3); - g_system->setPalette(_paletteBuffer, 256, 6); + if (!_res->_isPsx) { + memcpy(_paletteBuffer + 192 * 3, _levelsBitmapsData + _levelsBitmaps[_levelNum].palette, 64 * 3); + g_system->setPalette(_paletteBuffer, 256, 6); + } _condMask = 0x40; } } @@ -1674,8 +1724,11 @@ bool Menu::handleOptions() { _optionNum = kMenu_Settings; changeToOption(0); _condMask = 0; - while (!g_system->inp.quit) { + while (1) { g_system->processEvents(); + if (g_system->inp.quit) { + break; + } if (g_system->inp.keyPressed(SYS_INP_ESC)) { _optionNum = -1; break; @@ -1705,8 +1758,10 @@ bool Menu::handleOptions() { _iconsSprites[0x27].num = 0; _iconsSprites[0x26].num = 0; _iconsSprites[0x28].num = 0; - memcpy(_paletteBuffer, _optionsBitmapData[_optionNum] + _optionsBitmapSize[_optionNum], 256 * 3); - g_system->setPalette(_paletteBuffer, 256, 6); + if (!_res->_isPsx) { + memcpy(_paletteBuffer, _optionsBitmapData[_optionNum] + _optionsBitmapSize[_optionNum], 256 * 3); + g_system->setPalette(_paletteBuffer, 256, 6); + } _settingNum = kSettingNum_Difficulty; } handleSettingsScreen(num); @@ -1715,8 +1770,10 @@ bool Menu::handleOptions() { if (prevOptionNum != _optionNum) { _iconsSprites[0x2C].num = 0; _iconsSprites[0x2E].num = 0; - memcpy(_paletteBuffer, _optionsBitmapData[_optionNum] + _optionsBitmapSize[_optionNum], 256 * 3); - g_system->setPalette(_paletteBuffer, 256, 6); + if (!_res->_isPsx) { + memcpy(_paletteBuffer, _optionsBitmapData[_optionNum] + _optionsBitmapSize[_optionNum], 256 * 3); + g_system->setPalette(_paletteBuffer, 256, 6); + } _controlsNum = 2; } handleControlsScreen(num); @@ -1727,8 +1784,10 @@ bool Menu::handleOptions() { _iconsSprites[0x18].num = 0; _iconsSprites[0x17].num = 0; _iconsSprites[0x1A].num = 0; - memcpy(_paletteBuffer, _optionsBitmapData[_optionNum] + _optionsBitmapSize[_optionNum], 256 * 3); - g_system->setPalette(_paletteBuffer, 256, 6); + if (!_res->_isPsx) { + memcpy(_paletteBuffer, _optionsBitmapData[_optionNum] + _optionsBitmapSize[_optionNum], 256 * 3); + g_system->setPalette(_paletteBuffer, 256, 6); + } _joystickControlsNum = 1; } handleJoystickControlsScreen(num); @@ -1739,24 +1798,30 @@ bool Menu::handleOptions() { _iconsSprites[0x20].num = 0; _iconsSprites[0x1F].num = 0; _iconsSprites[0x22].num = 0; - memcpy(_paletteBuffer, _optionsBitmapData[_optionNum] + _optionsBitmapSize[_optionNum], 256 * 3); - g_system->setPalette(_paletteBuffer, 256, 6); + if (!_res->_isPsx) { + memcpy(_paletteBuffer, _optionsBitmapData[_optionNum] + _optionsBitmapSize[_optionNum], 256 * 3); + g_system->setPalette(_paletteBuffer, 256, 6); + } _keyboardControlsNum = 1; } handleKeyboardControlsScreen(num); break; case 4: if (prevOptionNum != _optionNum) { - memcpy(_paletteBuffer, _optionsBitmapData[_optionNum] + _optionsBitmapSize[_optionNum], 256 * 3); - g_system->setPalette(_paletteBuffer, 256, 6); + if (!_res->_isPsx) { + memcpy(_paletteBuffer, _optionsBitmapData[_optionNum] + _optionsBitmapSize[_optionNum], 256 * 3); + g_system->setPalette(_paletteBuffer, 256, 6); + } _difficultyNum = _config->players[_config->currentPlayer].difficulty; } handleDifficultyScreen(num); break; case 5: if (prevOptionNum != _optionNum) { - memcpy(_paletteBuffer, _optionsBitmapData[_optionNum] + _optionsBitmapSize[_optionNum], 256 * 3); - g_system->setPalette(_paletteBuffer, 256, 6); + if (!_res->_isPsx) { + memcpy(_paletteBuffer, _optionsBitmapData[_optionNum] + _optionsBitmapSize[_optionNum], 256 * 3); + g_system->setPalette(_paletteBuffer, 256, 6); + } _soundVolume = _g->_snd_masterVolume; _soundNum = kSoundNum_Confirm; _soundCounter = 0; diff --git a/menu.h b/menu.h index 1d8c180..3f53989 100644 --- a/menu.h +++ b/menu.h @@ -70,10 +70,11 @@ struct Menu { const uint8_t *_optionsButtonSpritesData; DatSpritesGroup *_currentOptionButtonSprite; int _currentOptionButtonSound; - const uint8_t *_digitsData; const uint8_t *_optionData; const uint8_t *_soundData; + DatSpritesGroup *_psxSprites[3]; + const uint8_t *_psxPalettes[3]; uint8_t _paletteBuffer[256 * 3]; uint8_t _loadLevelButtonState; diff --git a/mixer.cpp b/mixer.cpp index 17002e8..524b316 100644 --- a/mixer.cpp +++ b/mixer.cpp @@ -15,7 +15,7 @@ Mixer::~Mixer() { } void Mixer::queue(const int16_t *ptr, const int16_t *end, int panType, int panL, int panR, bool stereo) { - if (_mixingQueueSize >= kPcmChannels) { + if (_mixingQueueSize >= kMixingQueueSize) { warning("MixingQueue overflow %d", _mixingQueueSize); return; } diff --git a/mixer.h b/mixer.h index b6dc124..cf83ce5 100644 --- a/mixer.h +++ b/mixer.h @@ -15,11 +15,11 @@ struct MixerChannel { struct Mixer { - static const int kPcmChannels = 32; + static const int kMixingQueueSize = 32; void (*_lock)(int); - MixerChannel _mixingQueue[kPcmChannels]; + MixerChannel _mixingQueue[kMixingQueueSize]; int _mixingQueueSize; Mixer(); diff --git a/monsters.cpp b/monsters.cpp index 2dcb5cd..d289816 100644 --- a/monsters.cpp +++ b/monsters.cpp @@ -988,7 +988,6 @@ void Game::executeMstCode() { } ++_executeMstLogicCounter; if (_mstLevelGatesMask != 0) { - _mstHelper1Count = 0; executeMstCodeHelper1(); _mstLevelGatesMask = 0; } @@ -1183,10 +1182,11 @@ int Game::mstWalkPathUpdateWalkNode(MstWalkPath *walkPath, MstWalkNode *walkNode } void Game::executeMstCodeHelper1() { + int count = 0; for (int i = 0; i < _res->_mstHdr.walkPathDataCount; ++i) { MstWalkPath *walkPath = &_res->_mstWalkPathData[i]; if (walkPath->mask & _mstLevelGatesMask) { - ++_mstHelper1Count; + ++count; for (uint32_t j = 0; j < walkPath->count; ++j) { for (int k = 0; k < 2; ++k) { walkPath->data[j].coords[0][k] = -1; @@ -1206,7 +1206,7 @@ void Game::executeMstCodeHelper1() { mstWalkPathUpdateIndex(walkPath, 1); } } - if (_mstHelper1Count != 0) { + if (count != 0) { for (int i = 0; i < kMaxMonsterObjects1; ++i) { MonsterObject1 *m = &_monsterObjects1Table[i]; if (!m->m46) { @@ -4434,9 +4434,9 @@ int Game::mstTask_main(Task *t) { } e = CLIP(e, -1, _res->_mstHdr.screensCount - 1); if (p[0] == 224) { - _mstOp67_type = m->unk8; - _mstOp67_flags1 = m->unk9; - _mstOp67_unk = m->unkC; + _mstOp67_type = m->type; + _mstOp67_flags1 = m->flags1; + // _mstOp67_flags2 = m->flags2; _mstOp67_x1 = a; _mstOp67_x2 = b; _mstOp67_y1 = c; @@ -4444,9 +4444,9 @@ int Game::mstTask_main(Task *t) { _mstOp67_screenNum = e; break; } else if (p[0] == 225) { - _mstOp68_type = m->unk8; - _mstOp68_arg9 = m->unk9; - _mstOp68_flags1 = m->unkC; + _mstOp68_type = m->type; + _mstOp68_flags1 = m->flags1; + _mstOp68_flags2 = m->flags2; _mstOp68_x1 = a; _mstOp68_x2 = b; _mstOp68_y1 = c; @@ -4465,7 +4465,7 @@ int Game::mstTask_main(Task *t) { } } } - mstOp67_addMonster(t, a, b, c, d, e, m->unk8, m->unk9, m->unkC, m->unkB, 0, m->unkE); + mstOp67_addMonster(t, a, b, c, d, e, m->type, m->flags1, m->flags2, m->unkB, 0, m->unkE); } break; case 226: { // 68 - add_monster_group @@ -6864,7 +6864,7 @@ void Game::mstOp68_addMonsterGroup(Task *t, const uint8_t *p, int a, int b, int } int j = 0; for (int i = 0; i < a; ++i) { - mstOp67_addMonster(t, _mstOp67_x1, _mstOp67_x2, _mstOp67_y1, _mstOp67_y2, _mstOp67_screenNum, _mstOp67_type, _mstOp67_flags1, _mstOp68_flags1, data[j].m42Index, data[j].m46Index, d); + mstOp67_addMonster(t, _mstOp67_x1, _mstOp67_x2, _mstOp67_y1, _mstOp67_y2, _mstOp67_screenNum, _mstOp67_type, _mstOp67_flags1, _mstOp68_flags2, data[j].m42Index, data[j].m46Index, d); if (--c == 0) { return; } @@ -6873,7 +6873,7 @@ void Game::mstOp68_addMonsterGroup(Task *t, const uint8_t *p, int a, int b, int } } for (int i = 0; i < b; ++i) { - mstOp67_addMonster(t, _mstOp68_x1, _mstOp68_x2, _mstOp68_y1, _mstOp68_y2, _mstOp68_screenNum, _mstOp68_type, _mstOp68_arg9, _mstOp68_flags1, data[j].m42Index, data[j].m46Index, d); + mstOp67_addMonster(t, _mstOp68_x1, _mstOp68_x2, _mstOp68_y1, _mstOp68_y2, _mstOp68_screenNum, _mstOp68_type, _mstOp68_flags1, _mstOp68_flags2, data[j].m42Index, data[j].m46Index, d); if (--c == 0) { return; } diff --git a/resource.cpp b/resource.cpp index 6ff9f1b..eeaf290 100644 --- a/resource.cpp +++ b/resource.cpp @@ -1753,11 +1753,11 @@ void Resource::loadMstData(File *fp) { _mstOp223Data[i].indexVar2 = fp->readUint16(); _mstOp223Data[i].indexVar3 = fp->readUint16(); _mstOp223Data[i].indexVar4 = fp->readUint16(); - _mstOp223Data[i].unk8 = fp->readByte(); - _mstOp223Data[i].unk9 = fp->readByte(); + _mstOp223Data[i].type = fp->readByte(); + _mstOp223Data[i].flags1 = fp->readByte(); _mstOp223Data[i].indexVar5 = fp->readByte(); _mstOp223Data[i].unkB = fp->readByte(); - _mstOp223Data[i].unkC = fp->readUint16(); + _mstOp223Data[i].flags2 = fp->readUint16(); _mstOp223Data[i].unkE = fp->readUint16(); _mstOp223Data[i].maskVars = fp->readUint32(); bytesRead += 20; diff --git a/resource.h b/resource.h index 2370e8d..07f46e3 100644 --- a/resource.h +++ b/resource.h @@ -316,11 +316,11 @@ struct MstOp223Data { int16_t indexVar2; // 2 int16_t indexVar3; // 4 int16_t indexVar4; // 6 - uint8_t unk8; // 8 - uint8_t unk9; + uint8_t type; // 8 + uint8_t flags1; int8_t indexVar5; // A int8_t unkB; // B - uint16_t unkC; // C + uint16_t flags2; // C uint16_t unkE; // E uint32_t maskVars; // 0x10 }; // sizeof == 20 diff --git a/scaler.h b/scaler.h index a917e66..4e90983 100644 --- a/scaler.h +++ b/scaler.h @@ -1,7 +1,7 @@ /* - * Heart of Darkness engine rewrite - * Copyright (C) 2009-2011 Gregory Montoir (cyx@users.sourceforge.net) - */ +* Heart of Darkness engine rewrite +* Copyright (C) 2009-2011 Gregory Montoir (cyx@users.sourceforge.net) +*/ #ifndef SCALER_H__ #define SCALER_H__ @@ -14,11 +14,10 @@ typedef void (*ScaleProc)(uint32_t *dst, int dstPitch, const uint8_t *src, int s struct Scaler { const char *name; int factorMin, factorMax; - PaletteProc palette; - ScaleProc scale[3]; + PaletteProc palette; // palette changes + ScaleProc scale[3]; // 2x-4x factors }; -extern const Scaler scaler_nearest; extern const Scaler scaler_xbr; #endif // SCALER_H__ diff --git a/scaler_nearest.cpp b/scaler_nearest.cpp deleted file mode 100644 index 923d71a..0000000 --- a/scaler_nearest.cpp +++ /dev/null @@ -1,26 +0,0 @@ - -#include "scaler.h" - -template -static void scale_nearest(uint32_t *dst, int dstPitch, const uint8_t *src, int srcPitch, int w, int h, const uint32_t *palette) { - while (h--) { - uint32_t *p = dst; - for (int i = 0; i < w; ++i, p += N) { - const uint32_t c = palette[src[i]]; - for (int j = 0; j < N; ++j) { - for (int k = 0; k < N; ++k) { - *(p + j * dstPitch + k) = c; - } - } - } - dst += dstPitch * N; - src += srcPitch; - } -} - -const Scaler scaler_nearest = { - "nearest", - 2, 4, - 0, - { scale_nearest<2>, scale_nearest<3>, scale_nearest<4> } -}; diff --git a/scaler_xbr.cpp b/scaler_xbr.cpp index f87a5c3..cadc197 100644 --- a/scaler_xbr.cpp +++ b/scaler_xbr.cpp @@ -4,23 +4,22 @@ #include "scaler.h" -static uint8_t _yuv[256 * 3]; - -static int diffYuv(int x, int y) { - const int dy = _yuv[x * 3] - _yuv[y * 3]; - const int du = _yuv[x * 3 + 1] - _yuv[y * 3 + 1]; - const int dv = _yuv[x * 3 + 2] - _yuv[y * 3 + 2]; - return ABS(dy) + ABS(du) + ABS(dv); -} +static uint8_t _yuv[256][3]; +static int16_t _diffYuv[256][256]; template static uint32_t interpolate(uint32_t a, uint32_t b) { static const uint32_t kMask = 0xFF00FF; - const uint32_t m1 = (((((b & kMask) - (a & kMask)) * M) >> S) + (a & kMask)) & kMask; - a >>= 8; - b >>= 8; - const uint32_t m2 = (((((b & kMask) - (a & kMask)) * M) >> S) + (a & kMask)) & kMask; + const uint32_t a_rb = a & kMask; + const uint32_t a_ag = (a >> 8) & kMask; + + const uint32_t b_rb = b & kMask; + const uint32_t b_ag = (b >> 8) & kMask; + + const uint32_t m1 = ((((b_rb - a_rb) * M) >> S) + a_rb) & kMask; + const uint32_t m2 = ((((b_ag - a_ag) * M) >> S) + a_ag) & kMask; + return m1 | (m2 << 8); } @@ -32,7 +31,7 @@ static uint32_t interpolate(uint32_t a, uint32_t b) { #define ALPHA_BLEND_192_W(a, b) interpolate<3,2>(a, b) #define ALPHA_BLEND_224_W(a, b) interpolate<7,3>(a, b) -#define df(A, B) diffYuv(A, B) +#define df(A, B) _diffYuv[A][B] #define eq(A, B) (df(A, B) < 155) #define filt2b(PE, PI, PH, PF, PG, PC, PD, PB, PA, G5, C4, G0, D0, C1, B1, F4, I4, H5, I5, A0, A1, N0, N1, N2, N3) do { \ @@ -255,9 +254,24 @@ static void palette_xbr(const uint32_t *palette) { const int r = (palette[i] >> 16) & 255; const int g = (palette[i] >> 8) & 255; const int b = palette[i] & 255; - _yuv[i * 3] = ( 299 * r + 587 * g + 114 * b) / 1000; - _yuv[i * 3 + 1] = (-169 * r - 331 * g + 500 * b) / 1000 + 128; - _yuv[i * 3 + 2] = ( 500 * r - 419 * g - 81 * b) / 1000 + 128; + _yuv[i][0] = ( 299 * r + 587 * g + 114 * b) / 1000; + _yuv[i][1] = (-169 * r - 331 * g + 500 * b) / 1000 + 128; + _yuv[i][2] = ( 500 * r - 419 * g - 81 * b) / 1000 + 128; + } + for (int j = 0; j < 256; ++j) { + for (int i = 0; i < j; ++i) { + if (i != j) { + const int dy = _yuv[i][0] - _yuv[j][0]; + const int du = _yuv[i][1] - _yuv[j][1]; + const int dv = _yuv[i][2] - _yuv[j][2]; + _diffYuv[j][i] = ABS(dy) + ABS(du) + ABS(dv); + } + } + } + for (int j = 0; j < 256; ++j) { + for (int i = j; i < 256; ++i) { + _diffYuv[j][i] = _diffYuv[i][j]; + } } } diff --git a/system_sdl2.cpp b/system_sdl2.cpp index 4838ab2..05fce03 100644 --- a/system_sdl2.cpp +++ b/system_sdl2.cpp @@ -18,14 +18,23 @@ static bool axis[4]= { false, false, false, false }; static int _scalerMultiplier = 3; static const Scaler *_scaler = &scaler_xbr; +static ScaleProc _scalerProc; -static const struct { - const char *name; - const Scaler *scaler; -} _scalers[] = { - { "nearest", &scaler_nearest }, - { "xbr", &scaler_xbr }, - { 0, 0 } +const Scaler scaler_linear = { + "linear", + 2, 4 +}; + +const Scaler scaler_nearest = { + "nearest", + 2, 4 +}; + +static const Scaler *_scalers[] = { + &scaler_linear, + &scaler_nearest, + &scaler_xbr, + 0 }; struct KeyMapping { @@ -301,20 +310,21 @@ void System_SDL2::copyRectWidescreen(int w, int h, const uint8_t *buf, const uin } void System_SDL2::setScaler(const char *name, int multiplier) { - if (multiplier != 0) { + if (multiplier > 0) { _scalerMultiplier = multiplier; } if (name) { - for (int i = 0; _scalers[i].name; ++i) { - if (strcmp(name, _scalers[i].name) == 0) { - _scaler = _scalers[i].scaler; + const Scaler *scaler = 0; + for (int i = 0; _scalers[i]; ++i) { + if (strcmp(name, _scalers[i]->name) == 0) { + scaler = _scalers[i]; break; } } - if (_scalerMultiplier < _scaler->factorMin) { - _scalerMultiplier = _scaler->factorMin; - } else if (_scalerMultiplier > _scaler->factorMax) { - _scalerMultiplier = _scaler->factorMax; + if (!scaler) { + warning("Unknown scaler '%s', using default '%s'", name, _scaler->name); + } else { + _scaler = scaler; } } } @@ -346,7 +356,7 @@ void System_SDL2::setPalette(const uint8_t *pal, int n, int depth) { if (_backgroundTexture) { _pal[0] = 0; } - if (_scalerMultiplier != 1 && _scaler->palette) { + if (_scaler->palette) { _scaler->palette(_pal); } } @@ -437,12 +447,12 @@ void System_SDL2::updateScreen(bool drawWidescreen) { src -= _shakeDx; } } - if (_scalerMultiplier == 1) { + if (!_scalerProc) { for (int i = 0; i < w * h; ++i) { dst[i] = _pal[src[i]]; } } else { - (_scaler->scale[_scalerMultiplier - 2])(dst, dstPitch, src, w, w, h, _pal); + _scalerProc(dst, dstPitch, src, w, w, h, _pal); } SDL_UnlockTexture(_texture); @@ -456,10 +466,12 @@ void System_SDL2::updateScreen(bool drawWidescreen) { r.x = _shakeDx * _scalerMultiplier; r.y = _shakeDy * _scalerMultiplier; SDL_RenderGetLogicalSize(_renderer, &r.w, &r.h); - r.x += (r.w - _texW) / 2; - r.w = _texW; - r.y += (r.h - _texH) / 2; - r.h = _texH; + const int w = _screenW * _scalerMultiplier; + const int h = _screenH * _scalerMultiplier; + r.x += (r.w - w) / 2; + r.w = w; + r.y += (r.h - h) / 2; + r.h = h; if (_backgroundTexture) { SDL_RenderCopy(_renderer, _backgroundTexture, 0, &r); } @@ -839,10 +851,25 @@ void System_SDL2::updateKeys(PlayerInput *inp) { } void System_SDL2::prepareScaledGfx(const char *caption, bool fullscreen, bool widescreen, bool yuv) { - _texW = _screenW * _scalerMultiplier; - _texH = _screenH * _scalerMultiplier; - const int windowW = widescreen ? _texH * 16 / 9 : _texW; - const int windowH = _texH; + const int w = _screenW * _scalerMultiplier; + const int h = _screenH * _scalerMultiplier; + if (_scalerMultiplier > 1) { + if (_scalerMultiplier < _scaler->factorMin) { + _scalerMultiplier = _scaler->factorMin; + } else if (_scalerMultiplier > _scaler->factorMax) { + _scalerMultiplier = _scaler->factorMax; + } + _scalerProc = _scaler->scale[_scalerMultiplier - 2]; + } + if (_scalerProc) { + _texW = w; + _texH = h; + } else { + _texW = _screenW; + _texH = _screenH; + } + const int windowW = widescreen ? h * 16 / 9 : w; + const int windowH = h; const int flags = fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : SDL_WINDOW_RESIZABLE; _window = SDL_CreateWindow(caption, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, windowW, windowH, flags); SDL_Surface *icon = SDL_LoadBMP(kIconBmp); diff --git a/vcpkg.json b/vcpkg.json index ee78ac4..cfe6cb8 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -1,6 +1,6 @@ { "name": "hode", - "version-string": "0.2.9d", + "version-string": "0.2.9f", "dependencies": [ "sdl2", "getopt",