From 0bb493dd5f4f5bdd8cbcebe327dea159bfa0fb92 Mon Sep 17 00:00:00 2001 From: Cyd Date: Fri, 3 Jul 2026 16:43:21 -0500 Subject: [PATCH] Phase 3e: live texturing + serial receive-latency fixes for the RIO Texturing (validated live -- game-live-textured.png, the ravine's brown dirt terrain): the wire model is Division's intensity+ramp scheme. action 26 uploads 8-bit intensity texels ([node][nbytes][w][h] + rows; type 13 = texture); texmap (12) references the texture; material (11) references its texmap and a ramp (14: lo/hi RGB); texel color = lerp(lo, hi, i/255). The backend bakes RGBA per material, uploads to GL, maps with wire UVs (stride-5: floats 3-4; stride-8/9: floats 6-7). Serial (directserial fork options, tracked in vpx-device/serialport/): - rxpollus: -- receive poll tick (stock 1ms); 100us discovers inbound bytes ~10x sooner. Validated: sim advanced, camera moved with real RIO. - rxburst: -- stock DOSBox re-serializes received bytes at emulated wire speed (~1ms/byte at 9600) though they already paid wire time on the real cable; a 15-byte analog reply gained ~14ms, blowing the RIO's few-ms ACK window and dropping the game into its 15-second retry fallback (L4CTRL.CPP limit=15.0 //0.2). rxburst:16 delivers buffered bytes 16x faster. game_rio.conf: realport:COM1 rxpollus:100 rxburst:16. Crash-on-advance fixed: *_TSHD.BGF terrain shadows live only in arena subdirs the search path misses; null shadow renderable was dereferenced once the sim moved. All 11 copied into VIDEO/GEO in the working image; game now runs sustained (560+ frames, textured, no crash). Details in RIO-NOTES.md. Co-Authored-By: Claude Opus 4.8 --- emulator/.gitignore | 41 +- emulator/PHASE3-PROGRESS.md | 24 +- emulator/RIO-NOTES.md | 35 +- emulator/game-live-textured.png | Bin 0 -> 90376 bytes emulator/game_rio.conf | 12 +- emulator/vpx-device/serialport/README.md | 12 + .../vpx-device/serialport/directserial.cpp | 375 ++++++++++++++++++ emulator/vpx-device/serialport/directserial.h | 81 ++++ emulator/vpx-device/vpxlog.cpp | 193 ++++++++- 9 files changed, 741 insertions(+), 32 deletions(-) create mode 100644 emulator/game-live-textured.png create mode 100644 emulator/vpx-device/serialport/README.md create mode 100644 emulator/vpx-device/serialport/directserial.cpp create mode 100644 emulator/vpx-device/serialport/directserial.h diff --git a/emulator/.gitignore b/emulator/.gitignore index e1eddc1..0fb37fe 100644 --- a/emulator/.gitignore +++ b/emulator/.gitignore @@ -1,20 +1,21 @@ -# Local working artifacts -dosbox-x/ -image/ -*.png -# Phase 3 evidence renders (keep: decoded from the captured VPX stream) -!divrgb-decoded.png -!divrgb-frame0.png -!divrgb-live-gl.png -!game-cockpit-decoded.png -!game-mech-decoded.png -!game-live-gl.png -dbx_out.txt -vpx*.txt -sweep_*.txt -dbx_err.txt -src/ -vpxlog.txt -sent_hex.txt -build.log -run_build.sh +# Local working artifacts +dosbox-x/ +image/ +*.png +# Phase 3 evidence renders (keep: decoded from the captured VPX stream) +!divrgb-decoded.png +!divrgb-frame0.png +!divrgb-live-gl.png +!game-cockpit-decoded.png +!game-mech-decoded.png +!game-live-gl.png +!game-live-textured.png +dbx_out.txt +vpx*.txt +sweep_*.txt +dbx_err.txt +src/ +vpxlog.txt +sent_hex.txt +build.log +run_build.sh diff --git a/emulator/PHASE3-PROGRESS.md b/emulator/PHASE3-PROGRESS.md index 8cb40bd..ffdcad5 100644 --- a/emulator/PHASE3-PROGRESS.md +++ b/emulator/PHASE3-PROGRESS.md @@ -156,9 +156,27 @@ traversal). Fixture: a 954-frame capture. The live backend (`vpxlog.cpp`) gained the same traversal: stride-aware geometry, dcs/instance/object link decode, cached world transforms per DCS, y-down projection. -Still to come: texturing (stride-8 UVs + action-26 texel maps), lighting from -the wire normals, per-frame articulation once the RIO drives the sim, LOD -selection by distance. +### 3e. Texturing (DONE) — the texture chain + +![live textured terrain](game-live-textured.png) + +The wire texture model is Division's intensity+ramp scheme (no palette ever +crosses the link): + +- **action 26** uploads texels: header `[node][nbytes][w][h]` then 8-bit + intensity rows (64×64…128×128; node type 13 = texture). +- **texmap (type 12)** flush references its texture node; **material (type + 11)** flush references its texmap *and* a **ramp (type 14)**: two RGBs + (lo/hi), e.g. dark red-brown → light tan for mech hulls. +- Final texel color = `lerp(ramp_lo, ramp_hi, intensity/255)`. + +The live backend bakes each textured material's RGBA once (texture × ramp), +uploads it to GL keyed by material, and maps it with the wire UVs (stride-5 +verts: floats 3–4; stride-8/9: floats 6–7). Result: the arena renders with +the ravine's actual brown dirt textures, live (`game-live-textured.png`). + +Still to come: lighting from the wire normals, per-frame articulation once +the RIO holds sync, LOD selection by distance. ## 3c. The full game runs through the live renderer (sync abort fixed) diff --git a/emulator/RIO-NOTES.md b/emulator/RIO-NOTES.md index f237b74..5f69063 100644 --- a/emulator/RIO-NOTES.md +++ b/emulator/RIO-NOTES.md @@ -30,13 +30,42 @@ With `core=dynamic + cycles=max` the RIO **stays in sync** (the user confirmed - `cycles=fixed 20000` / `150000` and `core=normal` were all too slow — the RIO dropped comms, faster at lower speeds. - The DOSBox-X serial path is already low-latency on **transmit** - (`directserial.cpp` `transmitByte` calls `SERIAL_sendchar` immediately). The - one remaining emulator-side latency is the **1 ms receive poll** - (`directserial.cpp:68`), reducible to ~0.25 ms if a future board needs it. + (`directserial.cpp` `transmitByte` calls `SERIAL_sendchar` immediately). - Prolific PL2303 has no adjustable `LatencyTimer` registry value (unlike FTDI); an FTDI adapter set to 1 ms latency would be the lowest-latency host option if ever needed. +## Receive-latency fork options (2026-07-03) + +Even with the fast CPU, intermittent timeouts remained and the game would +drop into its **15-second analog retry fallback** — the source shows +`limit = 15.0; // 0.2` in L4CTRL.CPP, so the shipped binary re-requests very +slowly once replies stop arriving ("the polling is really slow"). Two +emulator receive latencies were fixed with new `directserial` options: + +- **`rxpollus:`** (50–1000, stock 1000): host-port receive poll tick. + Stock DOSBox-X discovers inbound bytes on a 1 ms tick; 100 µs discovers + them ~10× sooner. First validated result: the sim advanced and the camera + moved in the render window. +- **`rxburst:`** (1–64): stock DOSBox re-serializes each received byte at + emulated wire speed (~1 ms/byte at 9600) even though the bytes already + paid their wire time on the physical cable and sit in the host buffer — a + 15-byte analog reply gained ~14 ms of artificial latency, single-handedly + blowing the RIO's few-ms ACK window. `rxburst:16` delivers buffered bytes + 16× faster. + +`game_rio.conf` uses `realport:COM1 rxpollus:100 rxburst:16`. + +## Crash-on-advance fixed: arena terrain shadows + +With the RIO in sync the sim advances and the game crashed dereferencing the +**mech terrain shadow** renderable: `*_TSHD.BGF` live only in +`VIDEO/GEO/ARENA/` + `…/POLAR/`, which the object search path misses, so +`thr_tshd.bgf` failed to load and left a null renderable that the moving sim +eventually touched. Fix: the 11 `*_TSHD.BGF` files are copied from `ARENA/` +into `VIDEO/GEO/` in the working image (reversible, one file each). After +the fix the game runs sustained (500+ frames, no crash). + ## Next wall: mission content (a game-data issue, not protocol/RIO) With the RIO feeding real input, the simulation advances — and then the game diff --git a/emulator/game-live-textured.png b/emulator/game-live-textured.png new file mode 100644 index 0000000000000000000000000000000000000000..3671abfac7f6d6aff5652f23d6be6295595dc269 GIT binary patch literal 90376 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

6jP2?pt+geOljhm=_01_VZ}g5v0Ikmj4~Z zcoEH2!2p$LQ0X0o%gj_6xumz!%!k0?OBtFpY1K$~6dlZR5aUR$dyicf2+c4c**tx( zty+wlX=v!3VF9TegM=M+Yn{Gdqr{KU<%ukA7Se3aUpcwBl1_{>o_?@_sQh}i*pBwTm?L_ znSDz9^dYQOeL8inm4k2Nrh`NuVXrL?mM|LBL#~X&Sg5SQ{80}`nS47h`BT0=-pQEMA!hf1M!Nj2Rx8FJ7JCeg}?bzjTuTOzD$&T#e=O z?O>@K$NxN}#P80Ij>~+qG=Q@i-|cA9wJpVLH2DKneO(KA##m}wVt0*+s|cZvxBUd) zX&Q6PQXw;Yin^*HpVarvb-br+F@H)?xSJpBe-TalO|aI>qijzny`V_c_3Z0+5iR}g zTiJ%UBE0G+4+`^?9~LIJ^l(}$o@>vAnvNh2F+l@n_f=J@<~o$4Q(}Swmwx#NS6FQc zk-CXPkXEKQF^9!3t?Gk+aXOO^BksXial9lRlRK$RB&L11q>t%Do6MK>P-MkoNeach zWF9Td8$YyW7HYnl;46C>AX_{@=s83XW<`Y|76+aVK|N_XpjGMt;fQR@Z~y1II{cVa zX?P8*QMp}BRawlQ!m97n>FnC9W}igVO$-!1tx}*h(@~l-Bo4Tm#&XLTD5d)yntJF2 zjY;833pTjYDCs%p9Km+>kSP~2$KVco+4kn={i8c)i9TfBQ-;nom+3qHMZq8x6pDXu z;QevycvOwgar3}GyWL}@68%l6U;n>qTotIs{UhOC9>f+UmL=t=QrT9_Jf7G}iTjGW znE$1gno2f`2*z|O;fFL%l z;$#8qV|FyO83=eZs0cW264-vf`7J|P@!RiftV)pctDa+ZrG=DA5iWjpYcWe5&Ven7 zpZE^$a}}Jza6@=$R{eCP`0j#2(WgYgp0|WDKCXVAAjDHP+N0Y=U)(Ik+L*>t5zkNV zX=-8bC01vhMmnQr-bcJyB*m=cV*LbdiPNtEC9bb;+ zuV#&0T+cZ1d5I7<7`t)-2xufEq z%BPxgH8DVd$11Z|GMxXYk$cZg?qCBU-B878?}^Q)?)a=Vvg4eSu-P~NE1b%PdtM4l z9|dLNpF5v93~>GhOpuq|=k@=$5pr^0a?-31ebIAh?c+a`1^uI=ZNVtQzm`>nU!hH? zD{REPyJ5zGmL@~&c7Ck4m!9Mrn;(5r9`kxXl0341$UM9>DKLv;--TW?vPGbqFBMkz zMo_eQPoj)pz+7V{;g{{oESbkw_o|O8G4uHqBk21^Kho_?k%3~H2k7@-H;bw+Hq67T zwBXZP!qaFMOoydv+9x$6fI*pkF3+K$u=URcgOsYb{2F;sUbMt#5aEF1emQo4GG6LY zqMvEjBbQxMXoxn2uJ0+fr~+&7OW#2!Sb{%sze)7F@5^f2tb~ci6lLf`x1Ju~R6EBZ zdFMunX|+BgRXb_(sHHX5aQa9 zD^(Z{bqxzB+w4+F>a!7Ws*X#G-9UC}G%$!T?f#}PGju5vE z%CPu4@@gE4H#2eeDYF%c3>$&U6eA}-7Lig9>t;RyHB-TDA)g%0O%+}Z&9JakyCieJiENxJT_SNoK=^}AZ0X)yfrY=+wXA#6&}2%e(0?_qh=nO;Dt z9ntG-DMZq8m&(`a?c*T%_#S_h!`@Go)(l+B$fJ@guXp!unKb{m4u2X8P#<#tQ6C-v z^^q_>k7^F=+HJZ7>Pzu|q|*gJIu#@F6f%eOR0YRu1UO;!2?duwvU>f>VL{@%{WN!} zh|0VK_umz#6|TH|@fH5|BI+|2qw^L=jI_}DvY4&`9hBANvG&B)6r-eM6ktvnFd5b0znbt0BI zPl9+lXx)gKRVAnY$%Dx0l*qG|Pqg9mv%;21^!qzb}JCw zN*Z`Rd1BAYb;MHFO7&Q;0=6t{&DXevs`0z!N6ylc!iVUNZFb* zI*kuEJ(T0e#EQUOXMwSmRCZrU|E1g6Lno%S3a((+9W2LX5N|$;?XRWl-?7QjoD%-* z7SypRRIXa?XYKY*pXb>At2Fi)hFiD?sM$zcqF z3p@5JxmaL(b-($)Pkh3&t^lU=8=B z(cXrEQR1k8lkb7LnAp2&VxNk|mLgsbOA^?fqQob+@yLqL2Ox`^Rr8cy`fYPgg&FUX zADUCfdCoM!T$e0JNs?+n%f*l9ETo`lQN^=Obn&tjzD!5ZgTEOAUI zHOHQj2T7G;+icaa@IjkTX{Q<(E{9!iDNdP7&z_eiZUspQyY(_;s?%W|({EoHlrcsB z&eK`a$nl2kq=w{`qJ#Rp0sS;uux@ahz@Fl#`{o%=xRfDx@V;VT%1MfR(4|gK^FFCL z-xIUxW*U0GYdfxAMYzW+Y?nRnf{Wc40TU2(D~)}P>8sPeu_t7oXfn^Ir+6;iaO-AE zTk@9i>LP-fdCo8ChADl`2L8;IjrP#mc9~6@#4^~<(R-%J3Z{Zi1z1LI-!hU1II3p% z56yB{fQB#*d=vRAulk+wwq=;V8I8T}AcmhXIGASO`Qwox3JYz;jQ*`!9_B)M-7v09 zz1)>Eib@I-bF3hCBkUAf@tY$y3u@BM&!QVk!n*0_zYIOJS^&i9`I^)jkzsE zMnZb)xUJCf*8}|i$XYsXo#jr&mV0;4M?gMju+qKIc+A&?D$3dNqo`OLS8iXre(J7M zbZt&1>%SlxV|-a%w;Am!Oq*!jPT1P^__BaXC7Ws$h%p`DS<;)>{{xk!g>5dRDR~t^ z(zG-Z>Y32tOD5*%&PEVJ2U?<6P0K~E2od}`8~9=cOZPtCSzrbS=B)9d@3Yx&9fmo^ zNMEkYUBoO$4@ms5KSpDR~9-j29RlTTOV}ddju`ybto!nD8-X zl6R9kWRR*F+9n_8Vp$T}J1HKb$?&0X=TKZ`PI13U{ zVG#Rhg=6g^mB`6K%b9?72h@BUD^fi#_3HlaIGQOT5o51-JhJgs$RX2XGJa^*ENRQ? zK+vTmbn3Z8&%s?hL4&~Kz<-O$!sD(lq8q;7dBgS!`D!l8gR`_dlivC3{hjlYUq#ZN z)YcyOi#hlgSS@}0fIFgR4)iU;=ljLY86QDS(h{``gfHhdB)D!%5<2ZHkZ1pPZ_w6f zB5rQ?>;z{3FjFYH?T7!%b2B;BRo_Sk z%#M{(!UWo^^cZ?ahaIn_m05NY8NKY=Sm79%P51{I*X$W{qN?zq6ij^2C8c#whi7{w z7U%lOLCceXb_FCOUBQD_HHlZhsR&hblB77c6IRQig*|Lb#@m8-zj36i)gyqjL+WXx zR@C<4R`l$98c!sg*5lKo8=Og6C_Em`2-aJH*0;~2Hgb%bDf$++s?z%VLq7MIiDXv` zkN<}S^-ai66q290%9Z{g3$UK{^pTFSIPLNCZ!_|hgKCdH_;xxW@5WH=BTYOeg_F1 z*NdoFQaw#c_Qe^0R7SqMmp8Tt4YDRe*av~$*P$pWeFCD|k6tOTGiy^ZgwEC{fj9O@&Ina+JEUO}OeN ztLDl+^A_6PWT>u5jDDNMaQ7ARoF+i$QdDHf_n&k7>((E#QO#0&2<^Ewi7(fn)K;~* z6M63`iwxY1NufY?qiY{s&H3kwqu)dl;kfpHSbGs2PK#_O;ueh2n0&-LCR(R`y}OSQ zUyl{)evnBZG!+`#tN2GSNPexr{pxZ0-lC{CBC@iD}MFR|dO#I`Lm- z%gygzOjI;j-Yfw<^hwq>?EK1LZm9i9nCS%!%Zpi;RFwO^tD#Uc|Dw9Fd%UO)97Q^llDk6-mDYT; zYd&9_5S$s1?N%5qjmja<f*^)8M`(ST=+yrw1io}oGToOf6yc=nwxYwjtSfC z+CV6PS@C%+kjoFqm2+Evpk;Qx7Qml-eXf~7yjo#>)g4x z^OEY8uncfGJR5KZIGw1Ta|`@XG<)(u$GiEu!*~wW9tcq0e*yLV-(TPx++5jtPg7YN zcQ5@o94o#f{`*xyTE6Fp+~hl9u4u5d=~UTEtW|6{&Zk zZMMJy+a@D%0^2r0EFmcYfCI5Q?z09}L1;nKu$RLQWW=goVUqwCVMN&xtSpwxoKdbR zDP2%mT@4k@O?m7921A)iL~J&CEd#)(-A@7>TkSvFFZHt%?23DwFY7B^F8Q{>t{L8P|%0?R^d= z%klnMj9f8K_f{L&9FHEHI`a?oa+;;}{V|E_iZ4t*@7DWE|C`CM%S6xgdUZ;_ZsOGP zFoI^5?Y#K*@S^GUOVV18M405=?9m&$oP1BmV6L2>e9NKBiR7+1e)C4H&;#OICjVIu z#Qt~DogZdL$45AGf>s8JI5>bK8z~eJ)tLCS8sj3<7!#4 z8maO+fB)FrMu$wA&eLqcI|K$blfjQr{fv8b9V2 z-T`sg^1FoyCAO;fDHn}=r-!&}YA`{fAZa*}j{V))kny=Bd2=atHn+}SX~HzPfbu23 zAAbip{()Wq9R{ss%@>VoOH|pg|H{#0wjKBjGH{?HWBOv=sP;ZC06IAoV=LzD9XZ_J7iait?gZeH zcP^bDPGX6bw;Rr#dT2ybb9 z4u52CI>J4<;gDul@YGyjiig?Lqi2v36546u>pf-mUOo83iAx_LuJJfl-J|~FIouqj zs-QM{ztAhWUiY zQhw;;z0b03`cEeE3OKe6UmE^Jjw&N9^iyYF&IXv=O5d?@*g2L;RG_JU__u={uY5BB z{uaWiBzq`6{lxU@>G9y$0}X2~|L#{9K~gGedoMSj6+Vf@=){*RPH!PaWF%fQ9A;U* zsRwY5KQhk>0V{Kv?wuJGQL%Cd?-n%y>j0PipQkYxO7BrgfRW|jK4Yj(*5MViXCG)g zOcf~nA=YVG8Rz&w?$qWomUnOU49}ct(7!-?NB39Ej(*&fq0#SQMtNzgVM-qYBYG_Y zr^`gbw`I)~!nbebR|CFQT6CaD;)kkfbD-Q~1zU1<(*rH50goWyD&eXl^Z+i*NmW!k zku;REzpfGdLsP(2!6BxRe^Y<{8^Ck2vk^Ekm`o(keNcELp-lDgD^*EVoyDy5s&_()1)e4vd}m6&{}U@mC_(e`n*+&lZdpispK|V$=s_7}kYFaWh#ntF<&L3r#dKD>ehNoM(Rm99a1C#0LHHJBBW)VDo z$-NR)x`H#B)4u@|<783$YsRX#DNsgU{6FzG@0ig`AG;LkV3T|gj)&4#g{mvRUws`= zHiK&VkDA4U3d@jItW=j&R-wCG)v$|s7~bI|NvkPCG-AGz6fh3pW z|E8WV9c=|Yyfrcm+#P!H1N`9^L>a;pOwKG7nU#^|OxE-k(WMoq5m?_3Ebo^8$$y7qyRjEVOOTwN9%oB!Nb52HRT2;c4| zc}Y|%u&i7_kW7aa*xY1vLhIZvt}?f>#c}u}0K1`_Wllwb?J|gQM+~(Wyd=5DM;hP- zJa9*{^9S1cE_#TIWpq@NK4Iz=H(`wabk&ojYi=TyLZ9WjlGz`m^h&0Ku|J4$(sK3< zk9o`;2tQkJn!^2d&8F3@yQjW%*dU=x&hTpyORj}~>B`e<9`^1uD)Nc$xrEj|GPcq8 z4W+ZIk5(#o@F+1c;CNlc;?rB-Hv$@k2ko$`IWWm zjgGv|CCzW+vg=3P$@DS?54{@$)y)P7LR=)K!SygamJ=z<; zKYk|e#u?lE-wFXH!f2MsCT>)L`&O=9RX9${P~nZmkl5>iyTu5C7j&M?vJ}*wJWQfm z16ym63+Hb#C06@dFq9D4Qgcm#zI)|Ch10}clwNgdF(Ep?M!)5d=X%bonz(^pj-0RP zA0T%-I}|pbc!+N2k#;sGCi^`C-J++cXTlAA^jC1k6`trcRf06GND0I=9a3jWO+FGL znGxCa|7w&iDM%q-;63a@pP6{Kmy@*93o~C&sOt%5m$0c~Cpq*S2$iJY;mK&m=^y3= z-A$k~e^ZAdX0&Co!9D)CDfu={?!uAM9&J&|zkH!jXgDQup=4{-e%4rcA&OibZ7{GI+_t#+(MBr8_0g9J! z*&c^>`iobOU+|Bt3+y$te!J+D78oAWYMw9kgPsar{(#8x1Q-9)JoPTL5}IWb95%Y6 z>!8%i68D29sCEEIYyXV^;?!468njxe!}ei6-le7GlNb@j=SMc83gKo0TX^bvQ^}y#oN$gCAVOcXM2ORjyLU)DsUTK_iYb@GWLBp9D`&9L=tg9-?n{zc5gdA7oJfzb zV2Az+)6Y8&Er;bF9S!EHhgM9Zybrs%g+ zBOe>mn6!F0F#D~nB`R+zAhr9YEnBJ{-0oxGB-h@@Y+S9MFQWm=wJo&=qT8A^fIQgv zY>#`-4A*S(2;^Pg?Jn+C4jLPnhTGOLYO0>1=#6bko%TKf8%YeID(%GtZ*EOef7eKh zBbBL3)a%Hj$eYH|-8L|z7FHezzvaVd6F*{uqLlggj5b(3oOPMrxs(HknIi@>pgkdC zGy*0D@1AWJ6Ub(leCzwhx_Nd3id^#KP}ta|zKEf?laY4YCvy!z-eu&1o|pG6V7O{f zG~Ilun-PvFI0}^A5p~eJ;Ji$rx zdy>2I;^`$TFrJy8^&K5=)ASl&rS<(7p@Syd?`&%u*x~FNeQJZr^-pU1U)I>J(Bv;> zxYlQ3P#c!8u6w+S(|;hF%c`3AcJ*l}e@{)eh^;jYL-LoVqqQQ_t^J!@T$yD{c1^Ln z*v>qxJ86Jj_}7uuSXRyb~F&UmkRuJy0!T0+Td$ZHKX1D%HIVrnNPz2N2L7*N%O z_FPo$<1<8ZaTM#SBBi1Ih7^6CXR*3D5xZA0Xzw-112!~Gt}-QZ(&&TGC4!lbq$VfR zcmfjnyUY)*lqG9X?NJPV_37ccSXX)%GeLqFf3IGdt${&3&fzhtUkygLV`NbPQTU{q zC2uIEE?ZllSC5NuUV{<}NYa@pBFVu8$1A0lhM&Cdg>AAhcNVu^j1!6s{{_zr8QqIM+nDuIx7x&~A+ZN7UX`^Y+|( z7#}wW?bh=^RH1E$H_0LDvv7e-1==&@No2*vk}c5SDLVgVo#H0gG#dyf@jTvG4+$NziD#+JDmka%C+dPYFMbm>(OgNhh00ln6RW(@OhE!9~-d0=oUov)@C)-NQH*Hv)s;%}$oD`FMJ~1P*-Ue{~4l;!6#uu~)mA zf8@&5HKax;fq!?xKAcRiYlp)fYhPTrZQ)A^XSj(!_r!nM<>m^;yp2d(333y(8KRLs z-z$bqlKdR@=#VyjaC^e< za~Vkz5FZjhV(ZV%UZ$ZSd5zZGLwqft(2kI@kG^17bHDremlR_0dr()tiY40Ldx=8@ zNdtfkYzT^Vdo`_$P@!Z8xR16S&TsugRfnY^6EXIC?a-MogQ zXVNJgXD%|H6PGQP2|`J>!Y8`olV+gnC=0Dk(@b#n8? z*UxhV4`i26XFrHCFlAX^T+KS(KJHvo7D_1RJT3ad$D-cf&k&!$97<-$h9-IRB;D$UG7@ZV`mR*rn(z*47=(iRk_;CPyt`-{ zz~GO^>(88>t&74bz}wMUNlgJ(-8y74&Mt@d5$i)~gO5^oICjsAOh+A(kw??Vr>-t} z80f&M_*?&x-kClon5@nieUyA_ZK6gMdo`=5Kd1}GKURBC&z>v|uCPdb^d~)@zHgHwDpfk%N z#2UQjsNJ}>WUtLJ5|MYO<9T9C-`uQLRO7n}s?ID*ZUZXMXSOOG-hA))IV}ZNRjIse z(4?om4-JPlH{)J&4i8bOSej>k<^KuM1VBi0VL)-nAg9gT_MbTw=Q8Kx=_jNtJF|v& z<3(rR3pVpFs86+nqW|)E{G&Ok-p&mzjtES&HHlvd>mNJ6c>khA8A)Rwz-(^X6j8#I zQfh9bXM9o7RB;j`eR^&gkV9}W_Sc(O#$X~Yshp|7DH>f`<6P%@l|cY(>=_FT$p8Co z#d%rP>XS9S%TV!sR`fheH0F6?rA*2O10C#??}3?&E!w>b$ zJh@+KS|?>_*mB|8dyadThc$^BE5JxIj6*!q6czArxF{O!WJyqIyG^y7c6a7^7m_0S zcA6xhpc`9V>ADRc2e+mqO5Skq31Wk=jAwVwTkRez>gX(o!i}SEX}#qGRnumr?vO^H z=lMk*?TWjLO!b#Cd%I@a!V+`={c%`JyQZ;A#skCV2tQu+a5`AJr!#Bf4Z zR4aT0HaJa{uYa5uJ8;ZC8t4QnQ=zhKW!QH#D z+1m!%VZ>J;U|V1WtLBtPu@!i$p`u9I?1b_^04xyrbHx#P3~K|5ueOiK7pA(o-P#5gHsEeSjt66(s=BZZXto5^39G)*<-UblrNZtjk&jW>Sm z<(fNo*zxrNR!X6p7$koHwa+QrwwxG8d*Z)C{I<)GIRrLOQY6-QK1`y_l1#z7SOFQ%;Q%XWupv1b8<`o)w0Q2RBFfzgBKS;8={-y-B*PZ{Re2(H}C?HUl-fTV|f=%$PZ-%1G*o7==$j2dl`eWN@Y}*Fi%p_)JOt zt&jCcfJohV#h~o0UUZ1Xn{WP={&MeK22h;^T7UEZT?&10G5A6-@aB?dl$DcY+>NSnLVrH2~qrpYjG#@-)dZRV}s|rVQLA!55gkK%(hXWx&(5I>3eMp7vgu}xz6?*dASDA zx;^rprQm*!9hpB6EQNwvM=&|6;YGytgxvKSy+G)8fLXxH{3{Ii$e+3WMK5IrX2>O4 zI6pSs0%qEpjH&`f4~&65o^Ct+Qi(Nw+-~PNF4VjDO2n9$`SP9)JcPxKGg5=iZU>>{t2w8^2_TtNp*#AtNd^ZR`C|IBM8oQEa2wdJwP;^sQaMLS=yR^|I(fJrWV35j zr8)uQ{F0d>@W)?pvRh^E_CD_Kb2C{g2>i(pM7^>Fo$+s7jbj7|vTt~r!LI)FFpcNo z(A8lKh$5CuWQz3Oo62`Kky7@(`kGBBTH&*cI<#j*8Wy%SJTzJxEzN}MO@y1nh^4Os z+srcwnki!sSBLyXk4xQNJnebU^4fd+kce(Z&E!AEMt5H%-&X17fvd&6@UJX*Fe0mv zZ&4i~spiyzd6&d*`SVeq2dbyJ7$`Xeog~7-Nz9*+E>8$t2bu=R;O3?cB zZ{W?54aMl{x#+1L&JEq_*Src*f59HkGF439{LpTv0X0`_P^UAEz@}d&^P8K}wH4KP zw!KYnObkG8?!14aQ0?DC2{U{G3LvxQ65H+8><6}>pGSNOpAQ?xy*Y`Om-)M^|D$@) z{Pb~BZT=#}s^I9L)`xJ^LyJ%cpW{FiO;o*YB`y_idXlLw$hUOq zGxZj}Y?o>L#r$jtZMaGdd7w5_PP)CtY4E6GzWdM2VUtB52YyVfl_1=*;rf>Kl+W62 z*9=9>kbCYJD;^^y8HW4=aM%0&>$nB}z8L(=!}h5Sb5}({OJf!*?H0wzSIrukUfSI!%Cawb zJtTP138)VuYE!O6fzWqArZ&)|nb_#qZ5-=s)+x*ogEepFEe60{~ z;9^2Q%d9+~s@$6e%}QA%xPR#_t2nj&N?v;dpt+7V^-B&b3e$VQ%fX8)j9ct|L&g=$ z7Y@b~=0ZKP`V*G+uadt=;^Ws6ayu}j)$n(T6h_ik0)}3~Pq{SYOgW1u)1IHOr*kTc z@Bk$NA*T(`V10%%=>0X^bAnUTQTFdhypFRo6e*r9+?l!d6u)i$(9OIz70Wk{?4K-< z*eGMxi>lTuxo~0`aFi*!7JVoJSQZ?2auc*)G{!>IqbT6i5s}znt5!e_*x4OVuhmm% zqw~D4fN&if8Y;}qogpu*of9Q%^7CHo9fI=L^WU|UG9uDpP3yhFJ6p{H6Cm)wis3|P z=ATl+*7Jq?RwLoEC~1d;Mf$ta**yd}Q&MR&QK!3sq6bV#g<^N-YpT6${yD!`s)D~I z&H3UNd~ka{4>dILXZ^{On=@4dKxwsnfeq1#l6HDncYDf|sO5+TN)0ORm6&kt+5HJl zzIJk~(}`bcb3q=18{@!rZWwSH{?S>+O!qh4K3cJ@_4llTIGx%`tYjX#a)lgztP2Jy zBmEg{T%KNtoq(NP9#FxQfIgI7J5t0 zS67S)!4)=hjzw#Fu}md>IhuNoa<-feL~&YrCb2ZNpv^8QFO|d-2NP&g;$ldbr9NoF zBwW%jor7FAFP{*3kUd=wc_6GjSd*P;0PxSME{~ezQ$tl;&oe@P3sDW9R#khhAF>G8 z`M1AOKvCQ>#|kio#D_CD3`CGhk8_z3=agM3Jor^70@`Z`ViWPG8mZbd&8U8-iM9|cmvmk7gsj2;-|Xx2yb)UsY1;>iGlmQq~xLsYCmhAe~we;>ENMvJMA-pcF8c}jCk)rTcbA;iU z&a*CNWXx`tL>_QFF*%Lt_kUml3ll+m`e%N94ma1A1LY=S257wGumb)OLpKs+O)aI#P*0jS!-n$Cp()c8 zMNdGG*2UCB|2ysoV81bb3WI0aZ^$QZ0Yc;2;hEe!;aTJcOq)0>&!C)tD`|PCw7{^q zi*-i9K+xg@006l(r?uJ4drnSW1pvZta%`cg>?1=F9c*kt_Q9W%hQL2r$s2gb7b5qX zW-{Hi1U^@i`DeH<&|^@x4q7a|p})F->L47H{}UYo*6ocRwCL3KR9~Y(dyM^YRue)t zg`Cev82BkR?*TfqAXHJU?^T6@ue}DX9#mgkUubu~IBNalMmN3vye&k$g;*0`W z^^2RMS*))PDTDowG0lCpdoOg2g<4%=nz)H&lvx}K21OQ$4|`~#=soC7nJv5AqpByh z>nwtw-m8t?1KlePpMR<+sb42}*y>|2gr7lnh9>U3@T4<5D!I_Yt&?@g@7 zWf~h$!P=Oi468nfBP0dBqaS#AobEO95u!o7VFxPd2i>Rh=(xQztfuhRc8((zGB%!U z_4^BH4||A{_-`jBx_C>R7QB7@t=uy~lEw(vJ59)bbyZ#V@N&bPk<)J%v3VS;MSn;` zyj=a=obUWnj3>xPt)=ojt>i2@*C(m=_~Ku$G;q=ifA`iVUU*S_if_F)A|g`k4oG_u zfMaiKCm@^7q7=qUMQUJY=$W-Nlq=LycdtkK)z zDoAjQYTDD*r_-O1b-HgEW>PbX);%kN#a=Md!YCqdnRVKWw+{9-bQV$N8*C~HfOz$O2} z4`>y`oL-D_Y=0|60E()AU|~#C_BnTLp?ige{1&z-xx5;o1etGK5H%_YGzSm)3nEgn zC9}EIDP0z+yDleUvp3!~tp^_Lc&~#8bpPGaa{BQ6j`dzzQ8gV)T_3YDGtsAC8*I51 zU$IG91=JpWRHV%(xbJD5Wl=hyYD_#PV#ckBLi-a{(HG|zPmE_v-Wr)s^US;#CjnvC z^?hEM?*yb=>hQ-j7>x?WE;&3lTy`=17F3`#z{1L@1s)H7==q+Fl2?cb@al(%N`Bo> z39_~BW~ja{grBH#8+I>D(L5)#tkcyT@%Ujr6Vz{g?>Pb7d|T%4;2K;X*xFj(*id{8 zcg-ZU-2QacrW}6)Y5B{R@O{WB_iBK$#QmUsi+K_itDhFviWe2&p~I-0iR@wrKWe8H zCd>a6^V%v*yBTYN6Iu=*dByN5yyg>{kx%bJYyIJ3i32O?O49rk#%Pod|H1%}ADb zq1HC3YcLX*h*C%edzspVo_seiJ}8Hpk) ztNWQRGWi`$1$SmHA~M>GSgC*DO--yP#b5nfa=d>L5&4~PPBcnt%VOOof%L@!F97LFB1~|}^wJr6$nz|(V zJ})SP_DapoRDcWCA(S8Q>zPWs#~4!lR{J&(K=@FG`U&p0~9h2ZhStlI3T zlAYM**hY)!{O+tS+pM0(Q*X;W!KqL0>U3P*CiQrk2AUe5s+7)E39VlvuzUz>YP07- zYXA^3mCKzW$n$dRez~MmE>nk}J4A)kd8nG|r%xyw>Ci{8i8HH1N~nLoiinod?0sVs zrZN|%h@#;dx6lWzxmb(_)b4u8HQ^_@R?jX40RsS5uAWMKbLql&FJF$tJKsG<{bz_} z?JgZ>?=`;{;R>k|>0~Ao8%n!R<;hc73(u)V>)WJ%rEnu9zs<#i_QY%??Ph2S{BkDM zr>qFKP#OS#5=VP>e<$6XS)h0*P>+A)^|2Amx)}`;os~LSn|N^PAIp3LtV*`>TT4t# znK&+rQQ&iYXGbY;BUXu?n1!B`BBAkrAY2F+>T&05ZeY~r*IzFd4H|p z>ReMwqYkX-NWQNdvWVc>9zGEuZPxA7n_4#y$vYf#bk&4sy}5VOC=y@ZW=_)R_+qi*9)HELf6?LHqt93U z$|b~DK~EcxtLYGA0E%0cen$W^8lXBC8;dGA{CtT(~U&6x7+6dtXQFdwhU^B_d^ z&ufG~?K->nT`QbX>>6Br>3N@%KPvCYyeTW7AUTT0+QW6unpXd+T$Nu47{GO6q}^nA zKOgh;8CSm$aU&mmGZDq~xUOU;@OGo$gv#k}bl{9fdiLV{Ps#YdUXu-(c?_oI9;&E-{@E2)8u_Q>vH6)3O9EY0G zQ8|F0%hp9|23I{Lp;&3w<3@>vM2`D+qmgxQL`Y1NQ!}!7GfBzl2Mbzd`HvWoLlFGG*X8v|>4KXgv->zMW%z*_?9rl4<`f`cP`FlWlBYG|7*za5X-{)wrIk&z* z-9y=VQc7r_M5r>0h}MMqu|>s3?K5kt*)Q1AD=TWKT!rL2>*DZx^l?D{7yY84kkZ8V zc20ey!|=1uD2C&HbauyA7USQQNy)riXBwUNwaY6YR6WUw*Di*~P^}F^Dehkmu`umj z=V?@$4`Z^ybt~HWXCK5AYT4v6#kCwk(s&!oo9Zcav5V>-$$8-q+i}>Yvkt z)L@I;W;}UAm*n6lILVnSA1ysDBV=zKg=^W&RDI#@1CV)f7m*`CFU1n^dCTw7jEQh; z7~VzhQgE{HcyIULriuRn`$A$X{7Ox(=HaX+&(6kgOxS{ml+2y^ygAiYd%opJ-t{~to=IKLNC5!owBZo3eVaTi@5(ipfIiWOY&9IF9aVeS7iqMkbAVR|FVu=n@>}WOqS+-8PBKTB$ zW$98iwK2}0O|ZEJK&|+({;@Z<)~cpWo966?KEuSCLcn!88fT4>T4hU)O}N6u9}}mp zxG{v|rK9=uRb%|h@jr*em7wX3H!|ZVwe+{sflLb+Y|==7zwwRb`+#VnPGJZa!*kO+ zs7)8B!<(p;K*o_rBlU~y+c}w%!#avN( zTZ>7t`fzGU_*DvWVikUouq`I-n9d2s+ zrcKlFhICurNxBY!24@+M^`;PoV#+MYz)=-sOR2@Vqd3=?mB8ysUVtR-S4fqK%!>5^ z5kyG|EGVk+A)R0a;Ns=ma2bFqJ4Cu?%lcxtneBh7PY}$*f=H^9r-mYj`|3z_ifari z6CV40Ok#MtJgvjBHnyg@_FOsAuI-uI;h;9Lzkp@YYMKqMka9G+ye`LOCvWh3-J^%P zYp+uM!?gv>w=cYAMrk}a3?4%I0G z`$jty*udgp;1nDPxYKlPMOOW&{j4}O7g$l47QB*(x@vQ1Tw@OzTA)-Ju5HYTke}I4 zpvJU2w*UXMeY5g%W7NMhq3~ORaz_Q)i+xjdh8aAH_6pC0d z2>N-6AL)EGsEOrSE+CkUFZPm}QC*9d8xMFqgeYHQ{yc39QklzT;mz+AZezOZixiIH zSCv3{W%svXP!MwACLK#y*q4~8ePZ|kkdl^@;P)I2 zLGq?O2bD$^{Ofs_MSCuj`hdVJC-nQX*`0 zKie7a*4Ppv*u!SMFz7FSJRZ=VLK&cXnIpei^~?8u%`hX8bixydI55IL#BBtPeJ&AF zl4ArH9IUQt)ygT{uwm;4IM0u>AAAcQeDh0aRgWH;&<2{dFZ(bn?lIoLF6kY$Ua`u+ zd*BYQ>Gr?XFsU=i5`e*m{FuHYNRm<%#N^HZW|ZaqF>#w(7anea|8OTtzf3W3F5?3XXDi)~CiZ@%Q9X>! zc+Jb?+{|nKeik~Dm{#=V`%F#U*X$JYt17VW6;sqBNL;`RZC0*X#nY2X?P!f&l^S8} zl~#)S2?x&G>w~Cn5`8Ak`0%t1YUW=Uz2-o8E=0m+s8`Wch{Qf{G)d*)?oiqF#d*)YiOE*(uX^!=#>>LBE!1+1IU?l2{{qI!3O$z0; z>gCt}MU}~MQJiAc*gQn1t?gY37@`_Ljvu=A?;5U9%<6+(yGKo-_#%r7Fc<{bz_J zG*FVwJ`fG7LPOAt0xO68rq4D2*dD@7k*%K@39ANFQZ;wyQ3OJB;J)d5Q(|~S!IzLX zkIiKReEh;-R#wDf*+c*^TsU%L7Leh>Z}-HRsd5qM)+Imk0$LG|{YIp;%v7(LYV+%J z@KfHEs3?E~Wy&j|RVrPq);4a;%|Gg8+M4jbnr3Xkqx;9ocZSFQ9L)q|9kk?aEE~z0 zAGsrL^p9#}<*lQnN8x>3rG)B z9dmT!zC0=tCj-&TkR>uB%xG|2~9}&*^alg{1}j`Zr6N7^Ma; zTWsu0>_Pe68=d+w+{9W;@xj{bK=f+98t6MZGWfZbJ$AfA(Thx@nxy2t8E~K46H=3?UcdK>v+7LFB~H6 zvV1}_4cLKhX&-E@@Wgfdn|@vdZyXngcNWmr`$!XvIpN2|sR*6ZPF?%HV{PK-a~>+p zj)1skra@M(T=j?pl+E^Kmqc~P>p&l>YQA?mU>T%XPP0aWZpCvdK*90rU515W0sL0Vx$!9-6J-24znE&oborejmL+VNgAWVyB_ zpj_Vlb%ji~3u=|zoy*H9IaFoXpj*iTLM*MX7$H0#Q+=!Qq@a}9*rw%EmP{hb9Bu1h zW@BVvr70q$vv=<`6ZY$24c$)_ga65AqOe!Qg~HU!QgZy@FyR-wZHaB+L+X)cf}}Rb zf5C~B~^~Vu~EjYgC>KdUvWxR>$zg~lm+O`@GV%t!z4vU?tsYLi9Vae4=1vrkP@uaC^{npiBZra-C4vIC z$gOhhexan&ub6Xg>2!Rsn>}qHH9sd;?8nXFCYap*2>*H)DDPJfeEs;*HOkBK_{|x%DH!5 z1kzx}s%VzA2kCMr2Y!!TK=i0`zhgEoxg~HrMPO70$2XzOw#H^3iQ+JXU1+Qpt0jA$)#iXaG|E$z zrM}@W_2y(r%Oxl6?zf8A_ywH_x@eo%{qWh=OA@T@O!B5gMG?k?vZrG!%6(;{rV!JXsli-J$l;E^jvBQmf{PAIPFRZgAF)Tn%{R`p_;=JFFJ zFfr5dS+QH2#D(WTI692oR&9!LV_;mAxoD z*8&w>oUbmg0B0!`v229tM*p|}(@&mg^SFp{PM?t;#S*C1WA_r!QkAZ#v|2_-H`|V+ zGa>v1>67jDoRP5dp$9tdL>{jmcKb|Leh@LQCT4;&Th0mb5}7w`82rS}4nz1S;gID~ zK_c93w16W5c!#hvFQRz5mNr7`n;&F~k*@ZNt%ola@ywpllaiU92t2nVlH%ifuR~J8H>3ePJMqVGS0yhoGqZ znq8w^+c=*ayD!>yjc@Rs(UyYDg6TxHV^1BVbdEeOP1WASyieppsvMDF zU?F!I|(54SNWJ)x>%&?vq@{q;-6I-_aEkPyHaff+^o&=IANHQ}#|eu8_U z7egcYHY47og^zU*Ge5SS@YXg<;|gysj?0)iZVq0MBXPWh6ln6RBD)w%&r)O!D~8+aK1mQ2cCGuPWQ=)cJjndc^oD29mDy1hx@kaIIT5nDn4Zs zv&r-f$`7w!Q%%xp2$4adN2P}|)$A>$zYv#AXwY!N#@A74qKP>cro5Vh!%V~Nsx z!$1$rhDptMmoGv9{|SE4g#LR1PW%1ank!M(^6d zl9xsQiwfo|_6q@1;&|4*@9B&aiky(LI6KNmr50V~w!(JwO-4YxS6h^kfen2N&*x7b zkpHJ+FLHV(Li*i1Q((65PO@I=_+O4B{)L^LA3x=c?o8;l&!(~D2hc{fV%=gvN`YcP zg$c)gL$SI=&WGB5%21#zOJKbEQT%<2^Mh`u8$}cHX%0z899I6|K%uL7qW?eTC|2eu zWbF)WK7=MW)-nx1}=IvgbYe0e3@RIE}7*K`RGs?8g2IC6Dn$kVb&z z^ih~P_=%u(Q!g{^RA+1X^3D#63I+`dWTs(5egPysQODJEw^5&AB`qT&paPIfI`We^ zu&jn|HkWNA=>zgs5ykOWRJj5u8Idv0ZLfDW^T#H9d`Y%M{#<~@pB;4fbX^TLc(u5j zw$7N7^is7&O(5|V9D2!FL3TK2|E;$9k56;{g+@DwU??`Lk2Ya_%V#t{Ge?)oQ$hKh zYVG`%CW+KRq5Wrd5neK-?q>r2SB}dJ%8LcqP+YP4AZrWP_wd~{G_E`nw(EEPiiDad zVNu!4BJ|tT-=DBT!eAjL@zKf#rAH>)!s{HmoEzQbZ<)uP%OV*z!aetV<9Lp8i({ivgWPO@|dwhn0O6e;%k z6_Z=GIqTK?@N&Y?nWcvN<_)gGM4og4a*Vl-NkQDanhsmP9x!k4k85%yyIa@n^!uz) z-#%CE=~>U;jEdL*eBX4}T8eC;7pl%`(nulg8t&7-Xc<1lS&$t!-|%M76h~#`rNJ-M zSA2L@VKwjJ37Ff7W5dz=f=AV_i^g5+vZ*)~lkQ`F7yOg{{NK1u%xM}!^prrEUwB=# z_$FhFz%j9FT3OR|(kcv;i;*bM6ZBP#U9s7Ng?TVjRQKT4_$vWJ5OTJ3`+oaJwD+QW z`^l9_YVB2Ch1+cfRPN0fkz;qV{jkiK}+g;F8vxz_h$(I)NCxU z%h$4!By}n%xAtO6dlkJz;8gWagGXmd<|ZS^h)T}q^N@rcVG;7|Z@DEB5QzR#&_zf& zQ7j_Lp1#OV))H)Oq;a3S$Inh{wU}XN|IVLZvawB^PpgNTMFztO%{jy0kMVhupiB~0$Kgb~!Q4yJYu8QrBU-#C zXXVnf#5A3?qBtg-QPb^*xaK4pT7s3ndEHB!8$2i zge8|>CvrB#+_iq2Q(pLE&^-X*hK4aFYIjp08B<>h)`i7Aam2`hyRD3v;ik(U$0Yy> z+K_<;R8uI)By7UvsIH2@N~ie>Q^e=F-4Tc;cn(&NPxUluNJO7BX#BtY)f1Em>xESY z=^CvrgnZ{}xkJ}m1P%uKhNixd1~MoolL*>(&J9p7+7r*eZh_O?KQn}haI4&RQ(HK2 z^41@XjYKsP4sujOkcDJYuAPSnv$9H2^`i5fb$>lH)L&5&eV095_h>$E?g3!}Mb0Yk zW9{!y6$+S4bFebC=(!3asJm)#%~WS2iWTY2D~^P?5Ab+^WI1V$k3KT>8@X6MR}-pshVl84EDAg!(%vKqV*H%GJvmoDnBb=KaIXcXk2sZ zjd`+2A5rJz;Sf5Q6@{|LBkRLy~B2&b7OJAC%yT{@}L#uCSdDTT{?xbwk1xyvz0legX` z3>wu-;1u$!v|EnE$@h_b^$M`zc~%1c4s4WTqip8u%}n4~wK3I?7DLarZdMK=g^lB` z=K{gH*G735BJ5O}^H8Hz)%$8I+CV_cRPKCtBD!Ky7(6ewdRGH5i4yPXmwgG%k*7PlmQjMv+&kz8~ZRm*>T*{!0MOTi(AY8*c#|R zA%bv3z(NUlFC03Kb|go0^0%H~zCP2?i%#x+J{$ATg?-Dj-sn%F*&Rruj>k5i+O_`@ zS5`aPcFB|0;Gi8E7BpUz1(GsaCVq|$j)TWmSj?$i*H4D%T+VcAE+|Lm>_@=H>+5_3 z7L*%t3Nio|R&J}y#L<15>*u$_pY|tG`|9EKr63EDlYHAR%QV*qwskeJh0f|0GaJ1z zdRKew275w_c^6I$;7$16JwWbnnoi0d!=X~uG5RwWb6Sh4*dkVWxhE7)cE5`^ekz2y zaP|Erf()*H0j79NiK@exh`f!-RLpyyj3%oJ?t{{@FEJYd*_>!O@5w5 z+Na%C9TaM@JX5OaYZ>4N@iCQiS--^V11;rWd>;W}si?}C>nCNwyhCjs*>*zQ4>MHM zn0*y+a$8nAp_H&gY+09#?-`*S1(&ns(WQ0?f5lqEs2GObjWbAryAD!S#}E(i83q~j z%LR?r7;@isL*ojz&pK(@pZ6OYc#;wVqT$DV*;>{EzHjIF z7oBpdA>t>QjNe`Di~kE3+up{>Q$sjarz=k4!y`{ya&jn7)8Tm6lAflnz8_)3b<^rq zFqV86T6w@*_Ux}IDQH7XE43z86FQ;_w}(n14;mucT?*!fP2RDr0d%hj65^!bTD9&q zsJixfrY4L$)#1 z)6n6R&v1tjaeT5P%b!MGYQ)Pi;e0IC4_Z@w)7OKuoO0=;=4=G#k;y;*TeJtY9s}YI zLc8UYdfPUgy1;6H1$`G6T;R$?WT;@;y1YY14%RITghEOU76EUQ>D`LbM7~K~E5IV4 zd3SfDO1=wPBKd*3YnMdei;pIJpUB@fk_)H&W6i zA+x5-um1|BcV2fZb|ms#j>VV=Q;y`)88 z(~}rkQZ_lHSUAVhFSB?J!wtKFfVlCogL`I5B(8)n@oHZ-&OOgl6ZOp6 z*x_?e@6s>D6L8J4zP0AOvLtokJFvyX#`YKnTrSiw`Kz(*o(U_!;V=yqO7aigoHskn zGIF~0ze?@wRt?8nORo(Q9Y%KJeV~_O&sb1l}y*(5on2VFjCn02Vx(yy%DlKAfa zadUf=7_AB?ES5greG_YDW8|acHL9xodqy@St1bnwCHkuL7ZOfV@dwiM_g@^vk>tkn zN~rzd?;BC54H)axtIJ&%JdmbGJ&6$e@aXC?EZewhqwWpCRe=h{*R*ff+;Nz|La!^~ zwFOv@JBh&&@Cufb?@H4W!AfehBtGSp1=V^cY)m;%Uy{OViVsYu(pV@-%`Y8@%k`6jFv8!1yZ3!N`a`dZezX>zc>PJ|bF}%1%^o%@R3i&%!>@TNnpR2b4ceBU@ZDh)Yu$3NPq6IVu~l;1f|@^sYzm+E2VkujPsB_;@*U zr((&<;$~*`j)r?s|1_WJrn-RXrljp9ZI0*4G?`iv=e9uR_mA<;pY>K2tC0g8#`8UC z^lKcuv$fOlH4d_`PQ!d-5984z%!~cm7o~&`GhI$EfEjJn11r4XDDPgX&A(JGj9PMs z?~~COy|yq#|Mz8e=I?sglt>knN%HTf?X&EgW}s8hQh1VW@k7B;+F)Von4d3%$ox`_ z$b3Nqu}c~4%BgPT{3;3D-BV@e4dS|Q?5c6f1#Uw(Ln(}wEh`_jU%`4$bl}Eyfz&pU z6{Yfvz5IWE@gFHR@$DQ^e*>hYX-vZ_H?60h=*xX^BQ6F4w)*oW9l4kynOoD5cd!ET*unHv(9 zS-yQOXKsm0*lb|=cUA*5Pe90fm)-sbfE>ID+da?+l&w;F(#RaYHopQx67gTuf5cIC zlMXSjh%(1;{Rc#GSTF$?rSd;V_uwefcMs-R!WZl2>)Dzq{wC<@w)yu`q>h{7=pQV- zNTmvh9OX_Y2>so`Ki>zz%~1PLaS4n1vS~iMpi4|btMN&p0!d$Igg(1|gT{A80@|bo z;Lw_$#(dW2mtQjl%dW4c7*xbcK~eQ`Yr^z~V_N zoPHt4%u8>TUWk@qqn0Oc6BydYTojh)wbZq7*j7B`8M*z_z}?~8s(twlWwkXk+n#EU zOYDE;qZzMut+Y%Vo4BnVmAX{T>GA~zPSE%pNs|9*KX;{k0tkzWRbo%Z#VkPF+hJWpB3%CDX{6jVW)Km z<_K_4^SUly-{Rw6YVJ-qwJypAmS3^C%eaWMB-9gx7ZGnY%l!{WI+a=@`IPMC=&Vp6 z3;J(w>Aw5?FR$~W=&bt%A%11>1tKR7z|7^%@U5^tqlfS6&ZNcVZB(v)qaPCvm;$J} zu0yl-5wN!ySHM!dkag?Q1yfI1J9LR9%9rgv-0lK1NcXW~T1W#7K*FrLOcURyhrNPG zQ()=pvnSIwY?rt2!GN)nFL;UIyX5%Z`JT{}M6pOntrm~1{xV6NlUAsK%~h9Nez+QQ z54BBQGW$aDhv7R{?eDxrD}uA5Tm!-qH)P^BDI}oDMN6rqMY_dIyOHaws+0ww%KnCf zc*H<}@2Eec{Q}(TxcB8TYdI0XkL8OkmZqkxN7I`p8aL;c7Yq32^UgMRgmfWZf2;~o zzM)C>?ZhbW_ajs?2gVkzc06;A+Ae!z;TQUTNd6&Zr-Ep~+L0(AM0Ley?2?-B(@=kI-pBL$9*M*?_?HtB;W> zqJ`-?R!4`>D+XZr{XwM5w;epx7 zci#_2?IUHb%B^=DWW*kL4_mOmTgZ!m&BT79`1NTkHp?pKZ_C$er%ik3++d372Bu+& z`-!H&jG*@>ezV36LFrI8Be!L4+(pnsrPM0&mr;m}SS$XgY{!A4)yr(9iAZL_Ya zp7O`n>atHRFHEygN^mCsSDGQqkZ_`VS-+O?dUv}1+qs6kg2;Un9P{opRQaXC31j76 zd*5_Y62P3OCycg4jnthAKp{75L6jufV45Kli%_gUb+1J3f!(I7FZQfu}`6eru zmd(Hd?av1J0+&Iiro*$;whbn~-Yz|q_U+czN#hZ<|F3_yC%AsH0)24_32KnZk3;Nr z$^7@53}1V=;~0!Xj)*UsV;5?yi>BZ1MAC3h*BDS2{d(H+p6d=<7f4M04OI8L8YopKS^Qy_yG&OI~utJ;)->$?WLM zy_{WJH)rC&_EQ7h;3xZ_5O@B7;U&a_j$&<+vzeyvUvWs}(GG1*4E)3mDL2ZU{TZ@> ze)r7>2Xv-Q6;!_1Ug7cg?QC9!&)G+&chRl8Yx4T?DJQY&uG2r#p((9k+qscJYZ6vO zw?VcWjn3h$8bY3d!@&zJAueyoddy)#JSQPbpMJH~ztswRLY9Z0Z=yv07sG<{z3M?v zEfdkJ1bG9_`xv7K;E79TWLtd@g@bY4qAXPAb%cD2m+c84Uc#cEwKyc7tIz<3LDQ_@ zQtxLmr?Phjkk8rx>sW5oPTUa8TRnKz9_@%wBpmvTOekr(Dl9upVOJ;JqpM}D9o{fr z@9ekXOLG6^NUT-KcI4}6bKoxB@bkmJ<=L!r61UL^fM&NnvK&81!Y1`@qp+8m$n-Y2 zL~qiLr^b9&GF%6(TtdV4qG#n%OEeH>Feu+7I$rz;_TDeU@iy6=2CRY|Tk{e($}v25 zI99$Inxg!-^>v{1c?~VW+dGsnivT%L$+YkLT8Wf(YA7^wM*IH*))K<4%GP3C6CrEN zVwyu$6|#tAM=Z9}=d?81Foh)5ke@d0hV&;yUQG&ZL^+3_jF{_(@a@Fk2X&tx4Ad{{ z5B8S$h5KsFsV`Pts!tjWoFyI5!9~ZjzFqO#R(_SEMt?WGOPu>x{0&voA-k;u%F%+a z?5yjZPBM+(W8tJG`-?3!%9^+o_j`umg8Xax@mj}!ksMopo^t@Ev9k9N_|Rrlg9dr! z9JHQQs}wsT8l(&Lv0F8c{@OZo?H2Ln)$C_v!RyXjDz2UIXYp^tG}Xe!_VF~FJ_f0M z6_NOo@crY?wp%am?pBx4`lW;(bw#SBlC0a>hbb0Gb>LK#<$<&t zROs#dIwWpVd&Y23D`Oe*&#Aj8h1{cbyR+Vo7V6XLr4n6}u=++k8pPj(@s^2d&4ddn z^?M_&+8auj4Y^~iY3@zr8=Q4@mXIL@v%)MN#y?K^z43y~Z*-ca+O7N;j|%R4cn~#L z-%M;OMn|ZuI!}I*3WlZ-s#F4^|6e?D9r%l*gXX|%6kS1IIsbybd=I^@(cr%`&}rzD zwlKWLQ%G>&x(+ywI4-1B#b8e%1k8WyLc@f`Tg5*m|BeC(#Nz#n-N{Io9p@jh7n=zE z4W^_IlQ#>Kh*S1fCM>*zj#7xKQpCRma(@=n;Z4*QE_!$lXNTeN1^QDK`DbGY)1k2t zpfl8vOEIg;lE@P7j-IsLi>seB{h7J1P`{dK8e4fdg4jQuDQzu5uEBMMI$3&kHsCt^ zL)yRpL?pF+*IU;hogR7P%2J9bBHr>vPEAgQa5i*44}YgJTa9so{%zNuX&9dgdldI$ zlWN|lJ2^v+M~phn77q;s5pKdFYdwA4pRX-+_#gy^c-|VVkNGF!fHeT|PU5;oFPQ5& zpy}>DZMi3_c`oDP7{4)MRD9Lvd1I_eQPQWXX|EZ3$zoJuHN&;nD~=c~m8EnX4->hR z!&1457s0)&kX?Eb!@a9Lf9D7K8?JHUE-=2MyV1aTd)jjGR!}gAg=4L5<1b;B4PtsY z=W;eU$`}fxkW-kmp>SROzH;jEJ~|!&56i~FM*Fh|VI9_PCK0Ejo<(>Zx59 zaiCVlj$9`Ns+6p@tb`7)hkEP2F#xPy?q_Sa1WP z%5I}m z`JAyYkb+O;5m<~HvDWV8?DY})yv5$sF8<1YYS-6L+LuI63KaHLRgndx`dq^L>%OK9!i};rtSy29e;q9^y zxm>k+DAEd1Cr(}rr0`k83Sg@mkmqD%_(JvW%{ zGpz=OdoJaTFFEgRF?nWv^hX04o`;&fex->ei|@{lITca6g*To3v00vNJ7u~#S$%YK zngvKhoMVknIQ>RA>%kZD+pg>1B&bGyxeMLe_s=Y}Vsv*oNMkNuUr!5-Dh`i@w`6mH z#_ba?R}*c8xx)@26Sp7~{`&^h|16(ldR8|s&||*!x{th1CzKh)4;cxy$t_#mcfh6n zd230hc^yi)8nT1v+`t>+X9C0*8~W1~`B%hqP~GQg6LU>U7TSn#O-5xh^<4ba#BUNB zow{etintI6I{%BGtM_H}@#L7^L90*C^zom+2HO>Fk5tD{Dtm~go=d{Ru$+M-?QId` z5>@8a_nUj<7Qjm5!Qi1+FGbUfmrh}%TP-+;2l2?%P+l;+2KHVVn-!Y2 z!~3-X)x&%ngRB#bBrL+BDqN!F+`i_hitRY(ZV35}2-^eitkUN!hXlC{E_NgFs~TX5 zIqE$dt+@PE8r$-ahKw$d+ZIuV=r>c;q^?Qb7Nf!>xE!T=EH2vWk2tMGW*lo-rmAy1 ztl5hVNEcDb=@6%$IVHWm&XvcIy-s4y&24klS!`e zHk>vqW&S7pZHdyUvHz>jgn;Re8O2T_>hSflmx0^aU6!M!fv?LPZ`1yL+7j;U>RRjV zd<0T5jaz@ja@v0(FAU6FUpcxuvVhgUYkF9Hws>A$xh*Sd0%15Vf@q4w4729?FHpy* z*vQRd_3B@tIxOeLM7|fFF`kCDf(GW6!HWrkyL9nwYrhHH_!JgLoXy{?~E#d9m&|pk~yG_TB#F zDMigto43d9Nldy=tJr<^Zcp!$%ShI1 zD4{i7Vb!6Ab#$fH3|1gLx20@(p*7}dZ%h)}*$x!H*i=$G8+h(1?`~p1W+)^&>?)j; zE~{os(w5GX_^C5Dg;)suD7aArx>yEfC<@c!I$$_navTrsA-5){Ah4|4Wg_gl4PXpE z`g!rlJR}4OD$b3By~1;Zsl}i3AX0DB?YtgKvR|n-(i-F)G?dA^qd|$$@xK@pkes)$ zuzkHrY^RItG^}a4YG_i)AG4o6^$r)j)!q^N47TfY)bphdd=9WWS*1?SaG4RG&k!yh zc*BX)-7`0hU?jL}IYa;skvupsy}wBxgJtzgiu~$o084wJDpgaVw|4D*r>vsVJe6Ss zIZLQs4_W?xk9SD1z_Q>lxtUOUJ4wtv%}tRimh7`V`4{G(f2ZVjm&EO&Y4GfFQ#aJQ zBXQhjc>j;A39|A+@AizE<-|gBz5&CS*yu`gwuY&xjkd$zKPmJ6**E9m^r${b01+m2DOBFAB{Objh?$P>UZGX9R3}Kp-yy0k_ z4w%Ip6I$b(1NWpY|3Vj2vby2C^J+z~*v7_MwV)5SulDZz9KM15~*+g%& z{MB`6It{5kSofR9TuWLs(^*JSZ`&zY{w-E`Z{L3{#g4M|)*14VoSl_AiJbPq z>p9hucK`6PBE3>_4m@owl$hYYKd{tsgiYqxk>NGdu+DE@zw*8GVS&lOIqPLO*a&^Z zAjz*saP@mp%SM7V;+Lo|^{oFoWw6B@`Z*o*^y)#Cdi8%cW09Nesbw2c?FvpBU7)W< zigeZxS0d%Zy^T7-J2ZNw*(US{u`!@5%oSq>F2-i<3n|)MQ~t6DT?KMA5u*;ylH#Fw@h&pyLr(y~f#%o~5}8L#N_j zyg(g762>#%P5<{J1=X?YcYALRs+Owh{ZB4FZDUk%Fw$ zH^v?!@ub}^wSs}dD8lo}6eIpuqxLy${cn*S%`4KP@Vq2$c`e)si_YC=J!7{~?OoX6^OO9| zib=E@u9(AuYTX+1I-Ct@X)ig{vReeEPMy@P2fEL2*FA4PhHb(0Ks3d!bP#%(3C_b0c*Q68w6|zo+j&CdCzd0hSTU)8BW$Mp9orBcwa`)4N4MLP-JicQ1 z;ZRSv29Z?&QF|ImmzG@vLY(#28xtDp`BUh`HVQg2EAf|C%slIH>x=qGmGMO65NlTm z#eN%2^{!Uf#h?szFEQE9QEku|{+W$GwNST+Wo4>UZAlWU6T|Vh#}3i)7q3yjC5t74 z2n^@jGG*K?6=5M>`RkhvAl85xx>WR{2LLlx_~whpYsU-rUT%l3i%kx8^(|tDgfXst z63>{&$r)~LAD%|om%UbR5NixegPq^NjQ(*&w4#7?QF;T?LjNGxC-gC~wB^64KV-(n zM$4l~CR+NW6r;Xp9n|BfHEx^gM7N3fWYdFupZSoIIgs|puodT&O5Vg;VI33usbyXj zGj=H3nkCU*vLsW*3*pHdOD;(_&K0?6z-&8DCd2)R4ATmIi-31Qd=a~tzVl_oK<81L zI7$2RsxwP}ETCA|WJtXQd$tc-NU&$u;>ck>W7Zpc`0-?`{iqY$k9l`$F+E0nUN1&G zl6Kd6E-wyhqRs7{O{hP;KCS;ZB_j%EB3%YdJYot``pS!ro zChzq?f)Z#B8x3lrTqUjlM&?{1E|NDZ(%w4G&5;J7R&v_mAC%A#*&bY z%7xT(2{~3()rFl3MMWsW@HyPEuTv+F!TwMRnyh!*@wDtP9lzC+oV_K5K3aMeaZsvgFiIQ#t7{?koHjbFX{>zrVM>f zbRts*3EZJk!6iwDZ>g#o>OE+>$yqvXorYNj4Z4^!aKy~>UuK|Ilzj(5umb9m$g_k} zs&^}Vt%N?&T3tqZJTplD+}8MyStKOQLO-p>(<|F>0Mn<<_%CRWL_UE|cDHE3Z4OrY zbefb70E!n4RhqaC(LR+lSuN5mx&4O{R!#6_&PBTu`~NZ<-UWjFwVzuX_1g2ifi8o3 zsCsXkesomLO?`DXO~uwF*vy4}20*<0RHUBmf9QP}8*4wyITFaNJcXczP52fy4W<5T zp2@3P9iw%2Y|3HVcW>NfXm^bndf)H#zu_L#ip+bG=Hp^$-rlJVUmIa9UcW23C% z5C!p10A?-%VnFf zutNoV{b`?cijGc&=0?qsUigBm>qRHa)!L z2o`-g%kJoDN=`TB5kHhs%%4|38EUX6M9Aywi1Y-n^&ZmnV&CxINeNItQcK$42!Z`C z{vZesYKAwxZDJ2obWKv4JwNWGPo97{8(!ayttR=jM%s{e4enT)XcLb#CO1q4?GV~Z zj$;_x{vm(GXPff-8SF0a@J}FGM0yEFl5974vyu*P(-WtA`!UYzqVrEpqnpA@<^|&o z&COyyf#&7J{};EzTF>_t`2cTt@;aevKkBsuHDU|de4}6#sqFNFGKIBo) zJx{mGLM6m@JfDNth=cFaJw9GkUN5nn9OL;q-?U}D#2>Fdp4_f3rvou$$}Fcm!HiK< zc2vORl&?jY=nU+LI6yK80GBjWVQH3E!V-FnJN-e&_2g6`S>UBpZt58U57BpiR`O81h$6sNPtsFL(JndRo+ykJ$6A z*oByR4k3B{0}$kxVrl&p5pzW9G31%zi$WOzs@~mIb416b7vAec;P6j263<-};$F->y1z$vQ!1VmB<3%=UNBPTYDb|t};clmeqGKF3N)(#} zRjPde2+!UmM$w#`jqRwhpp)mW0$frx@T!c`NR>TeelZMpzsaeJ-k2U)RHLJ(^F1o* zx{`pTVh{*P7<)xu=2Yn7FSZu7yV_t+Euy0<2Oe@dsyPExTOu1*%*BM#%ez|REsJW( zk~Bc!?$Iv}5xM5l@1zANYLJAX6~E$7QeHxXpk;*f0zE`?yqcxf(W-yar|$+YN^7L7 z&p8%{%-R2`TnSyxZlkANOY`LM0%a>!(YL}{s)~zs&d|5cJHnUXtQ<}|i>^-FH``CZ z&*Mv7!z5_Y#g*~BY&-u&LlRjEVfHPGh%FSgI|@Z)1z#B~98$45MQ8Ctx*EA#n``BL z1Bf@o#-8O_Z2yGc7iD$ZI~e*t=P8#w*(&T`H67P(jQhmE$6qq5tdd5NR1(r7G{cY4 z^SjXHy_K4gnd8Xvde@>Gp6k}D!)31-;M}zjko8rF1sgI_gaO%pMGI8D+yPrW_XG{- zAbg|Oy`A=8eEZjPkM@h7&_K46F6)V>A!EI6Sx?D8hq&7p;)T}76zaNsn@jBNz`>W; zxTY`bsCdS!=&lwad{N)vF5^_tIY`#k&<;j{O(Qxq>~Iz-aC6bq{)!gaK_?t;j=~tu z9kF&>_4G=#6Kz@saVMn7XX@MqOS>;pBaDW}Q5xZrK)bJkW=rY))2?|x)w0<9aqJtO z{Y%d?Bv>hp{DF=4C4#`>R^yI-Mw4`C@Y>P>*XiAExekR1+K%B2vk4w+trAODCP2=n z$F%Ijus!S^7X^X<{7~*d0+%La;e34mkm&tQ)9b<}WdFk{?G0HL<3cCV0=M;RI*VJm zJNYR$IPH4THbM>e<3Lv97XUewb?Rp_VD{x=Mo&6!OO18Mb5}OYyoa+*c{6|AKG-h2 zN&Q+nr*@VTK%CxYu;6qSoDZ=^w>Mf}avMX?GpdJM`PPr1ffjsh)@B5`1eEWo&K{9q z>NH6LZT~oH4ilfO{ikaW6OB!AnseG9ya8wJPh6z-`rmSl7#_lnMP=O6R>)iqfzt_8 zy1LCF!Mp(d-Hol3>YP0v+cDhPm2N_F{k$;ZTA79by9AN1zT+N`p2*~1^11kWXSMYw z!Gtxap1R$|C9huW2H%FGo3|CsvBW$Iz-I$bG;yjJ)%(NqeHu>(mdDrso%hT%{70Rv z=Q{rc)#HcCfmwU%=q^UBpE{jXvdeJsUK!$k~-fAq0p*~!>c zSCI2M}Ha!!`$7QjDezmv{TG7@vH4lh)d{ z(L|n`ZbfgKmG72?FUJ4^cC~2#J!=s*K~v*eF6Ozc;|BQG9A_NPG&0V3@PMg0K657j z=}*e(d3k(scQlD=;%5E!o*4<$wy1#sdU0cawG=Rth-8nm(tR;vz9_*p-vOj^iXO;G zmk*+VITFmxSCw;MV))Vf$J%KKs%0_xG#0joHitwc`TFywQ^50R4`wg&jZdvbkFkY=8#juatkB2Q_^40ih%E zoE6>N;9-Hg+tW@^t=VG(m(#(t!448ts)EL4yyRGAVYJfclpQlc#xPTdzmfXm`q&Hk z_lp+D!o37@0jg#pX{M$~A-2h1i0VY_!!_D}VujT=aaBOMb^G|0rX)~!@ynfVF&?p*zrJn3dkrBQOrf?a_=QLgPcq%3iCj3{?I++vkgJ$+l&Bu4|M*8;GrM$ zpw=_Rgt5!?J>+6lL5W{c#{j20tNNgXyn*9)dQTN(6Idm2Dmr+PA<5m1KaALCr!caZ ztXVXT;Z)PK)Q#Q87r~eS{d+E=s+n|QgC+6dA@*+kG_Cq4eQnu2@1XDQ+62LRq?eWR?0 zE(s4VMgA>5{ijkvVIBkF_+9oFQuDY7PDW3rD6f)tbyT653o?69@sR14Mi=hA-M3!T zDQ)crCeeRD;P2>2&Tu$eNLR;D5Np_(Su!Ci4=KcoM}qj^2ZatEN8&+qU!_03w(u zwpebixM0}KE~#VC;CgOcH_`Ow!t_WQC}Olg5#vz)uj!t{k?8BFr(N(7*Hf9;vQ7^QT9V4z7zj4Q@2@55!EY?@UM2X(pGfSMbul{5@?cB$Efl`B^Ebce`|m2` zAaz!GhA-ejsBb~z@Lu`S#0R5fo5}1uhh3|V;IGQQ0@3}!JZ;y-h>bh0V@wJ8Yp>)x zZ1DY_yno|b=^P#OrC`O}$nn?BT|JXM$DWGK-G~8pXM8Vk!6y-TPM25bSfEzn0HD?V zFQoVXYdY(Sq~B~4-5PisVDu|RjgG2=wCqNb$HNh3MXE>b?GGKV)8rd(s~7);vmCwz zjgi*1s|TBvGf&?fqa%b0ZzR37A7r13wpB~;%A&(JZleo$)DWc(ZswIs$ty640&3@>t>6ugzaxZnbc<;##0ryj5~4ZxR%HDUBvulOdE*;qtzkQ^$n02)F(CyMz4+ z^Mz7M)ZUdMvfljZft26SdM@^ULE;K&F}xN|*qzDZ$7{*=b- z1hsQvWf8MOMKb^=WZ0)U|C{?TD#f#}*BG=Ue{(XI=2ftOnZ~Ju&3z`@MfsaQNoHYA zb9#XfFzNgWlb}l_+NI+!BF6no=BP~tY5w+F#aD=gp%E`BHJ-$x0^tYvIBD2UTZNAa z#!cb5`PoZ4BAhw3{~f@bo58RKdFROn#{0V?o3pcs|LPD`BLNQWl-{d+F00Ko?Lk1g z_}fxn=Q8u_(ywd|Z?#o}d9#_~?M+3+bx$?I*5AJ_o+nAZQV;P|jZq}Z5^(@GmKtvS z)6vF6+cCU4TMQOnECMwSP~(2H_r_p#$oKz}!mhb~uPAI`wuK~C>-d@9*=ShwZg>yy z3{qw|Df?=+{`nJBI|coF=VK?KtTyh7>`c-|n?^^UlnN}eh<$!O(C&G2v<=BBG)JV- z4U&IDHoOi}H)X9k`{iWX1NT~Ny9;S3oDWSe;O6ydQb_RKYCbrWoXzO&L4cmENcZ7h zW()qZBO&~*Z#%+UHP({_3qIIen<@a#w))TjQ2JUihJ_ z{)zPe`xdp94AEJ8D zFwW3d%`PU94h1{o>bFl!P^{Y`-At{>C*8tijpOF}pWzRKm$q484e=XtSw=XyJ+1;JrY;EN_~U;!Cc)-5UM_d<C1eJJMgcR;AGx(-bK%haQO zd*~SUVNmCr+m^I{Nd_rtsy>yke)j`4edN^cQW2Pcukv4+{!3?mm_!gNC$)g+ij(Xu zcYVf{wd_${A{wF~P3eAnP|nzGXUwaLCBXsruJ2!4YIch-IOmmrHViex)Rma6SvI+p zycwI3-+X0O>=spOvCObzeTeS%WcV_dL(O!J_jiV6-Pm#-@`z#Pa6K3K7lYKMsQV!_ zOj~4mZk=b)iKe@q$Mj1|M2|2r5X6Sg48|pV{v2;Re0bPNOkGbh+m5FYyWV>DjWt<< z&W~~nM4xcu;$Fp{-!Cq+vrcM2ti0(RTmOLpjw4CQFBP}s8pE+`ielaQ_*(hNsI_6f zvTGMMi4ZT;56WkkEpbHApdBTXxel z{u&jIaXt2Plei8N{mVH)Unw}1+zla;4DtE9RByTS<*Xx<`Zd_vvOo$MMbRO@0q3V* zuo^@#SUe=tsk*q3X5!Kxfnz6p=6lo_g7lDgZ5wLMJcv6HT1i4wQYGrydo7j^^@SSH zzp%4@*uj<_`qtkQ{A(07^Kt`Vw_-Vzk-B;>7X=l(BSW{G(?vap3$W`AA4Ds6zH2HAy?6X@NGN$C9~ z-h8>b2v1gCv~@qyTI$wmeNIw>E!kd~WyWWGglVJp+A0p0SC?6Bdw9~H5}<*T$lu8< zAH-L3jOsv78`dVSZ=3p5eZa_!#uJi^FJH9I9v_cXBwJ>6Kg=MjqM%~0{60j%VE-qM|(~`dy*l%RIlW2=aZLlbn64xL!+L%_O|!J zBmckIj{xcF!%S1!mAj{3Z4UT<2y@Nk8m@Wof;} z)b69~I!~qpuclVqQ`VTB4ORdt(xcj$IBZ9f^%zA)0P23JqJo_}igUocIl9*ayfGGr zS3y?I->B{6KElg8;}tb-um<)rzU9`&)B7x|TF6ezzq2qPki82Xq^k7(;2j6^hrq8E z;fzLeE{XHVH-j&{X9=-+tCTKxa(&^GY?kwO@XveUiNNOOr!4DI2-gs^Aeoq*8!j&i zksK{Ag0o(QnUC4Z&nsqGFUQj>^pV0Oa#cT%{AP#lf-mAZ)hO{@Jn3@MV!b@+blYk5 z1>x@Rmf9r>rb#UsT?7W>?eo+^5C3$fd^_nX582>pKkK;^)sRj@trd;5a3w*ivMc-#bJcA zjVCS}+(hdz?WgLssIF?>sv&Ur{`vl0yHc6*L0u$aNGOMpo$R`AzLY&osi4B&-UZb6 z#G#SHPoy2M9x&v^(mIm@?9*$=gwOjZ;Lu|;$~6#!+52;b8LONAJ-;8lfv3On@;w(; z`Br{h^*G0i^1L+I@%HpHP(XL;)qMseKj9RV?|sxn*6?UEcYtGRP|<$4UE7}2mbJ%&roc=FUVlIa8?RPh!RlbMcr?iCn7f6q%`@SD zc{hw(>4)wx!wZ&Z>t7*;80Y0O#=i~a0h*UDzgiHoW$WF#c5Mu z?XSYlUqh8E&c;ckjT#dB)W!Fxbf$~Nda1hUfJv>G1YN*9EhCpoJh`grcLBdc=D^(OBlHU+m4j^Wzp4HpR; zl-m}d84}F_Tkl7sE$kFV)HiMGO2Q(^>Bwp4B7`F5pp?kimf5rg^}1wE)_QHCjpjST zgebBzKH20SdVPeVO^T~6rGUL{kE55kSI}nYz=}XSP`OwhE7mnw)V=0S|A}$AXnf)S z^z~#(6zpY2i4Sb4S)Cgf*|FrH@AGCH$qZP?#`M(11DTwB%)=G83&)HfjkQ%%Z>K3A zi4YwJs+K-3WZSj>a3&@}pa7Qjua29fv$eS=ew+A`?&z@1^kHqddR(_0k$gzB8p)EF zc|!94tMbL>mR)mb4=;Q$V2|Ux)nA^RCW{s8S?U&T$!J9B&GLuzsn?&B=}IJ~j$eu0b}JI_^ktdBkG zeXKUNG{hd@bms!j$Br`nCF$7D^RC)!_92sAImG8TR@F*Vz-!je(QJOM7LZvf$}J(j zqd(d{An)`Ck50e2gW7{S5hi9KWUdO4Jm?+&&)IL6dxFiz-L#x1ksZRqO3Dy*$TJ8q z;h#=`{zO=NL=AK%$z{{WbTUaqTk_yKjxxAp-o*+t!_Y}f0)60T2*l}(W~`pM|LLh( zjmUTkS8JB%>zGXNXxi&fuS3nFF@7PYct6~YytPj2tUYfBF-jNhJ^enix#B;@)QQKHKOB`L8cGA{*- z25*DGJL%S{WModI-M3`ZGj*RcBM1FtUH1HftN25QR5e@4f6~$}Nm#{j=UueFdGII9 zU5BX5tY~{Ia{RSY{q{Z1!@obLUrgWq8h5c{GPC*J%E}m0r8zotnL;7V1tX_5Tkm|> z5-ym$^N?jiNS`Gt7a}FZj6p7)Uj5msy_S)Is*{{zW~t;s`&D#&zWYMg8Ps`|ZT0k_ z))UpOZXGu@isUe$lTCwSg<6TASR)WvsVRh+4s51^2HzML8JcJkVz!AEqN>yD7T&Oc zDFJqnFZCxCo)iI|n+;97@9RET7oK;6uSbi<(eCG8*FXpFDlqJUvc5fzk$l*`-L=lW z%CNU&`-v6k@_t~2eoq1Mbp1{$|HOE0v83^=aMiKf-BnspBXQ5Qj1)3@=+wVSGzS1R#ywG4PW ze&a*iRNy_AWnvl%t9inOCrG=jv-+7_FNvcp5+06I&3sX z1<;Z^XuI^Vh~?z2QgD1G$Iko8wXRm&)sfl4Oh_hz@|6K6xCOz}>(hoN^mQiJpmwLI zflww%<_E-ocwXa}N07w5oJn3Sk)KSqOZl<(I|-s5748|pvgq8k^8%C|{k&M*?l}eO zPOXfJkIQ0J{?uj`g}tRPcyXSzFkuT}jY91?>$z8L)n$?#r*W*k7AoIeRDvBwC_4C@xeybS${mAH|N< zkuWdU{Jx~fh4u|HP#TF({uRcJ5`19UOHi+fZ(Crl&E~{6L_7(dWl7XdIDJ-kE-WnP z@RpTV{6(XfWn@RJbQ_r6M)ojXi3tw4MzgRIEB%}m#&_72xwP*TLZqCQUQ0A)t_ACz z8A&v94X8e=R2{GRPm}YjeA}sVLEc=Q!Y3M^+};PgaU>CQ{^e-$6WAEUqLdkZTbq;VvK02$A7*w!My3y@++zIV%30Gp~^m%T0KK zL8+x@LNMvMVr0w@4JgqHB0PyJ((l9rlrt_X{AEl&id+%!D2B9D$tfNVcHHpxx51lt z9Fh5Ss%E$E(#)s_mP!VyLR17!B+jDk>SpbE-q~T0skwm(fl`4={3plEy&-c@MpSI| zEIX;Y9C{^1_c{(?phzcrxgf@$zaa~IlFY}3=2}>Ua-eM8VjjNv_VA?*7*SaT z?-Qt((CLOkWf9N?N`=`fQs2Gio>f9{cOMEdo#%)s!kjQgK zt}?D}0}_h72HcBfjF9T<5k&$`2g} zNqE-of8H?R>qc5ppezj{6<7%mC4L2@?HQQ2r%KyXxt-IObW&6~8qZbd=IMPTK?vp= z2v)b=V`83(n}b>kJ$#`1plhjz=V@&HPCc|{Htn8(;`T>m;Ilvpn?|QP$s4`29Lby* zNAYh%(Y!!C7^RQTDX> z&vUW__6?-mrxB4zNr^X_n5Xl0wq&;vnISM9wWLs3{%hWg8c=(;Fwjp|l2Y6e7CtQ^ ziL{%*w)BK$W|CzBu%n^1NRRKi(0+dO_ui!^9ZXV5WU+!#z=Lo`-b6-M9s+mcN(4X; zKe#Df-|h-fPa*=DiEFxs+}_TWMcM3@T=Aiwyw3MVjm(phN%Hsoxtj|%n^PW-Tj;T^ zL{bz}V&B9UKSlB2qPd7FkQ9~M@T`qW_l6aPo7={ieDsjoHxSD0>4eW6<#weJ4O_f=H6bfPEj`X^_{)dH97s`-C?+r&E_=3Ob7Lr?Ki@Xe{9#|x4wSl^fo8TeOq z$1Rzq!!3W%Vb?+8WPijx^=UPpnI?`y-^P~Z!!lBZGE(eL{i6y8xCOIqTSmG6#9936 z+dqP~_tkQWZ$ZSb-GF#H8qh|DL+M@aDENjwrG3K#Q%sj!n%A;f9JIVpIbqhiii22@ zMmAHQ%Iwl={d6(g>974-GYgF<194KgoRGH*RVA0~y}W@25+`4^?u1ou2BY){PDGpv4W3m%bL!r)(6)QvR!J=D#ga- zy)-$if%U)}g9WYfHDt_Ifj^vaUQem}1(*fk|EFTm4RMPW{3o7s$P#8@ST@PDUqTYJ*-Xr#-YCL+81UAp};In7a=h zQAMg;Pg1wH48m&Ja?RPI$&ohenL{;t4|VpU3xei!Z=ThA?LAqh&kdfuV%(@@uGVRs z6=^|Xe8jHFJ^IFj{&TF^ra+)&5n&fGx*gtbJsJX*)V+pp<2>6DMXjx?5{Mh}NK`Dg z@nNgv)PFGqkzM*$Bw)*MmJ(aECL$kOtGlVizA`G)N4ELB>TW5G<-D?&;`G%MpZhuf zEwP9W-8+>m6Dbp_Tmu(xx|U>_)BPkvTP3KHzKq|_!4zyy;rjPiXEK4_T3*&6E++fe z-J&%_Jj5j`{2)k^+CH!wx7AD^!j&UR430Jtd=_Q_fcklRS~q|Fj`BfUwovedL)?94 zK3vYvpf8__NX*}Am)A3=S9m>RC7j~Bd2Z~!CrAIJqM5C_G@0UCb1u6by!YV__ImeE z6%T;0Uin7F@bS1G&TR@xC$;>dx}H5JU5JLuj?c&6Kq=BKi^2Pi$Hw?brn{Cgi~o#$$awd=+P98 zYA9JLk!#AoHx{rZlOVL4%G;fN`f@~?sEU;DlnpFPgX<5vpyhpq{iNhXiE!Wq@|)C; zYzTKa_n*j8^#H`ncC)uKajc7DPyJ0G?Y6==ib;Cgrd)aW#J0L9V%fbmVsO4)n-bRuj)pDHk$@7Iii-buJrvT|RNP`x7f>n{m8Tc38zFvba$}hc= z_Mbjf@|vZ@#*6AIswYDjP$WOZ{N;67+}j#5>_W>(9SI^xQwkSLn)%agTVIWOj&9jq z0O!jxI+NQI-L#gIlaJN47pq*dW=qZX-!VJQogb}3hYHAHdQ(A%9qDE?l*g+2e;z0~j`OT0B{ZD!PtN|9 z{>D+;@*kt*xjYCpB?^Cz77E_u2B86wS8sBDYcRm(iZ3eg=Ul)c|J>F5ALOREH-6Ej z<-*H*F`~OFBF|Yu--E=+SemsuPmM@|M9+4G-6Ef7Q<=24xG3|QB`8dQ0dQZ+|A&Tu z`V(@tN}_ML_vnz~{1ccn;_phCxM^v9D-vwsw$@bhWbtUl(dP4i zdyiOh>VzYJOj?|}^t9ur9AUsz8fxaVH(HbatEHPRNZp!P)_-IT{V%nG9VhY9%6#;! zukZT3iGQv{RdGVz>=h)d99u+Y4-HV3^Omk~!Z(-mwc z^UQdZn>Nc5Wo;uvi-)8h=Ztj<@pIg|baGA2X2Lfzo^P=#4AJ0=mvYt(9P*itxE8nE zFs6h^9Y_JpvoOyp^Hp+ehnormh=6CA~hQZcX~ zy}kTj4{0)&JA-yWe<>BN({VkNE;dtvuO%f`CHQ@*cY$rE(s0yYPKnjZO8oyzgdQMBT#xIw&t0O5$q!MBhJtYz;5> z_!!>ppJf0Y(Z|Pjs*Es z?u!RzSEVS4OYM-_kivGl%Ug5NbQE{_!Zj}Uk`x&f$VRSBn%gHrl7sBkp0qo5 z8bTM&aT3{Z7vfg_EqvFSa+H`*dxjuwpdd2uAJo1 z9bZO6c?T4=UCRYBUu7+CV$QFq1ybqC0{Q5H;96Dl_%{@DzpW$D&wLsIcQ6hGtYR>sqx52FV&& zI&fFuk89bo4nJ>eCBQ-_Kiem+Z{<9XkPuR3q?&LsLnn0{S8mMA)#91&lfxM5D}LwE z469eTb|xiU|0tLt?$j#DCO3bRotDdAJqDiC4VZs|M0MwHnNY$=hRFybSYJwjoHnJ} zbAz2yg9gGFFB9(eXgTZGg!Zw z%Rfsgp1M3h_LZ?B=@TINYxa+Gog6iD>je1^enhekAQmbM0;2=qmk~N`( zm@RwV-{tn+YC38VPni00m8a6@^A)qsm&0_K6_!asyD!TZ_p>~CZ$t)vtvWX*!kN5! zWJJ?B#Cdok2G&;npt~Z)bqbE-fH%)V2LzLq+1t!J-ziNY!RX6{8RYV?A!c?$wLN+c zYOg_ycgG;T8g&zXQ#_THkbjI|L20{jc4<+(UHYDwr-VL0SzL2bREeXgW2>8iezCXv z9$(D?4934<#=$*4e$YYOjj$fsk^@E*+9pYV@}^5{HXINpyyhZ*uG&!I?TRHOiN6;l ze3mO%i9cugPg{dwHJ_qCPzIXZZjAZ3_yw?Z0X{Gj*w$C}*7d{Lps#QZ=*I?;Nt$I} z-EzyPxBV?P%gpDe+^j0jM3$ZsNrz zw}toJeTo)A*paM3Ky5Xt;C5b521#u!?aBCpC1^8BrVli|SDK6(1kYvEPoqB06Qyi5 zxWsZH-z*t)ifBMnua=)_ICKO%&}JH!Lp2=N$l6WG%Z)W0)&34W0LS=09#&swY;Jv= zA=wNcDY+|Paybaydb3M1Al*i?Ui!75p3Y?om7?O$ll;6A z_GH38%Ir$9<2{YbkndnTP`Cp4A?@lj`c9?d1W7WNUslS7egT+g&%Dg_*BeZ_g-a}8 zft7h14W7*(K=Tk6v^zm$S^R!j_p^)?C+?K^UZPt-SukwgSUam#>4c{XYsP2AgZsUk z2<$}gUOb4z#yN5293_=6X0s6>pMc}49&d=-1jn{ks`&W-@kGt&*R&<~d1S5u77UwU zTN%OgdVNqS?-Fo4J1*YAudlmyeE(Me5+DVmD%CQK3Y#d#O&&%_B-(Fw+Cbl`*}m4S-8IJ`ijj{Ot4yfB2PHXtisTgen&r1`_Q!^ zqg!|dkg0gNqZ+dAVldoL@n}Cmaj->b8ujGv>En~YX#jh8y$@pr=LSd$& zS1rr9$B?u7iyK4*>th$f8(N!+XRqENPO3a77>=2_3d?QmOn#c9z?pF zwd|gqcLh-`V9d3v5O!V;9pRLKfKTJNLNc$ist3~(bkc~;l?0u#1J57x0k%5O!Rf(} z`2^#y=#tiXCBAL>UPd9FuQ@D? zgi^TNbuk`%fS97Cl3?h9o019q+}8WlBXY0vQ;tKlxLf`&5W01B7B_o8o!Vu{Z-U+g z!V0~!p0Zw$wVK>i?pyo8(Plw7ENgOm6~BKi0vP?u-cDwT6PwN`fRUFEPAj%KISHe` zH_EUdbN`_%F+_aqCyF|8&O?ZhO*ZTUx(MHpkHzvD#TVDN?L)Z6zi>mecRr{<#Ry1- zg#&hkV1ct|38dCJ+*u1KUjyP)u1Vqoh4BgrwT2*34^ofVRgnbMAK)z|!hrTTy>=f| zY*muK`VTi_E!HyWjMGoK0sDw7I}*l9_`PMwiItSd5We|0o{iSNE;!~v*n`F4NY-bu zxpZ`_Gv#{14$1-2JF=MEs-0IZ3BO|5qH0SWVUByroxV!dJ6*8Tw*&mg(bsbb;E7v8 z(WNQ^e}W1VG(TXF9|u@DAvYmqS5b4dyfbO9>Pn>TUlO34_wKqvf_}kiXu~R!CPJy9 z(`+e9m$wFm(4Jnv=7UoPU`7TnDh$9ZVh&$x+;r+}T(=C(1`t~CztFnXay$N%!^RJV zbcuJR-jfo;A}g1Z=dvIt7Ih{(>8ZYLlO+ueKR!tIbj2vAz67REG zcW_S)(N5kpesg}5(aD=@le*#L^oI>gK z>NDt2zjot`jq-wBOQJGOtInifImKZF?(5W54EUB2$-nr$Yt>Jo*Ad{N$ko||_hOCv zMl{2r2>thDRT_dMGscjw6twJu2QF@5P-&B3uIhu!Mdd$2dGU{wV=v)>OUj?^1)$V8 z(etVCDZ%7R`OrjxoB!>7RrHDwB~b<7DpM|S?He%2JEESAwBrx6n2qiR_VD?sF2#nM zKHx9Y4;&8x;`ob$FWsgX@h-PBZ0(39dqpZh@sg9r&`m6+;R z)i1l0)8Y$C{;pr&Yh$|nRPI4uM*~c~)EnjGNNB_o(iEk>V@sCMb_c@3`OVzcd()n= zfwFKW+mP;ST}&KZ^ZJVev!kT4rrtn_nk=pUBy=yje%BXAI)CaJIh%PV&>?^e(nJ>y zvCi9B8J2xtZ+_*GM>^jH;_27p33~yX_YFA8O8>#1_F}XDT!SBNGhnn6b6?;{c7K(W zFtyYBm<%03A?v51GRgCnk~vqB`yq&#oRz$t({cGRNeD7+*d9=RhbK5T-~Xfk#^mT@ zg$`rCn9te{*|EdWO%J4;@~+-KquC4A>UbrA7z`4pa_MVx=>&_~SV}FuvU&~F_)yA9 zcEM(aZGRTp5GU%jn;lnKzA{=X@yQ>{5GXasvIdv4H{OsQea$Cj;BG>C|#xS5AUjoM5T``AJLDW`kAC@$T~f zBvL^$fJ{Y#0|n<5ez}6&`6Rg;7takOxCh)K(&*>o0&~@Y?LqGiyp;D3J5D>1%9Qhd zK^YfCEQzotd!Y728uv}mRI9RtW)+{!u>Y|7)f{OVP*37w(~9#G#-d1GFQ?wXU&I`%Qp^4sA|et2qs2uk+p1e1mbhM+Vm0?k4ykC# zUCV5?yF4INu57Ud1hagH+poE|Mb_^|4l+TSW$9z|7=0>`gv)kY8vb5#Axqt1U*7fia+LYgITa$*hsEUT!hw%y-;;0j52&-R7qdSv-vHs-}wP>mlXtF zTWF59h@6N{<<>Rj7#QjLF#)aocNiq5Vh);#x$PiZ{{zHdHwzU+jk$H~X-eNc6aY~y zAVB?ue@$dS>b*a?(5G3!c_E}6WqfqQg&v<=Na-?s{??6)z`lso>K5)c*Vn&v0{!TJ zK_xa=&OqE#pR~Y%Q=NVoKZ|FEW*xxYdi)(b^MIc5e4(1P*G5f?zMG~@cTT76Z5;i2 z=f;f->k=*c`7}Bd=UPphyo3JYnqNdk#gOnKe$N-MgHi;yPYUNGp?Dq&Z0?)hMSk~7 zPWeGm$(zeGvu~Qa!4KCu<~Z#Fxz62fF1J2hzYnc<*cy?4^K*NraLB)#7yJPKs$V|z zIFn0(JCoZ9B=G4$Mg1nbF!N0UzEgo)QNwXr17&!t`b)*H*jkAn?jesB{u{-nIlE)v z__HEuUON@qnYK{HrD!{#T_5nqqguVsIz(D!sln5oC=O66h>N)B6&zaAZwcP(!q zLHMI~hQW*TUWZ%eJol5_AbZWikTH7t1vhP(zU6mI2~r}eC|c$I;H(xccXw1uUuSyLvYhHiaE(A#U*^>yE z%`$Ua%~!*sz$3{$_1_W&f8TyVY&^Ip#EAIwp~Jn-z{}oU{ndb4e-r0eH;^Fu@wq^m@rY;( zf5uLJoXplKUYsQQ3Kk@DZAEu~1RI}BKJyQf3!U4l9^45wbC9KX5uUY4i5sXCw!}~Z zGg6X2=iu=KqSf=beuSSW0CBwM0JpN+q|RU_k!`K|Tg>-Mo91608k5#IzEr2en{`fK zvZN(}MH3FVou`7G|r1LbOs50%q-h*cSIrM7HKLtNl_?2KLBf!ixX=&|;f}S|_oMZm} zr${n)8@Rfpvm!@4WHTL7X5_Vt7PW9PY7An3^FEa4^u~Fr#I?wV$C~6M3gy&D5Tb&* zT;z`MpHbzZ!tU4C;l?jGt35kw0fVaEaN4$$mK-M)51X|0mY?< zEcLT#xkzOUavA6*apQk1#rUVNa{!Fdh1@>X;nGn3lj}FrinzXJ^(4P-n!%^p=&;T1mgmjNz@kpK>yR~9X2A9=qm*~7k2fWwUYjC<-sgd zhK28nv;k{mVAK>A-GN@Z-I;$)>T}EQ)hK6kFG_7vVgsGkXP30 zT{p!u>CmZP@44^y30qp-RiR(F^&PO)U+p5B&7LU#sxb^R>r(jzUZ#(sbTM{4aMPOa zD2(K2E%7&Z2h1e4&D=LF!pFwDU(m|9LZWbf=GR<*guqW;^^(e-*6~BebTZ>~b{PDi zyq*ZSxtom={ORs-zQfKjjfboso%6=$8mI5ug+$zH6=FLL5hC+q^hreB4#w51)aEie zqx-ZAsRBYBHz+-e3iuiSW`uwQq6^FLZ#|0LvpL(xb|t9CP_zVnP1~wuPK~gr6&>(S zFQG=NlPUalEI`%jJ#fb$n5pC1e&m)&mk&p~b80THKPpo#yPuUM_>}}X3e}E)>J?<< zl&kh!mtN$9vRB=-ZOtYHC{C?-b<+YW&o0)sT*Y}i&Hd5nSpv+-B74-Fi=3_W>d2SN zBM5+)J3oBM@IS$lNwiPM{uP_q9KKgSWjftym(ai>Y7g504pBFLX%T*tJA%e5q?~61GrVvL47y*Jd|A#MK$S380S#x4f!8N*0idgRQ z72ST`yP6#SubF9_E(ys$=O(Qx3%cNFFpMKXv+(-%tiO_fzkmjPHWZra z8Y!+m)Q`@NM;|zzkpSg>rQ+^-z6*X#LAk!%x{E54wu9~F_mKh$UX_l`g&m^Khnk+K zLb~9At-?PRwVSJ?Go0lEp6SRA3L%T($%keloFr3l_to41$A_Yk&@}(5VCYNZcKAYW zyj=Bc7l-!ljUzL2&gsxw>}@8z?|3};udUbqk)E9(bmf)U*TwIqBaBI_$mx2!ZhZ>+ z?R>-NZVMW|X%@@7l2h?}Jd_GLI%4FLFBEFFQv|w;YKJX80m@S<87yu`3z#&OFphWY z4V+?I<+gi43Zr*bD7*b|TdU6&2HF;*|3()L8vpR-b$2ty#k6`A+h@^N8U7 z)jpJtRI6Fs^B1KKhJU*(L+c{QtGbmBD{N%CT1_iM6R9Fi@jD9f3hce!)B4N8A8JDS zHHgjqhG-(+LW#7h8OQJ5Qj1`lkX!4O2eII&+6+yasYbbSex|2%$O>#9MFGRiXQhE{`7z4$<}M`^mGFl0X>`S2b|tH z{E)LgqfH~l!ri!)8DkqY6X~>m3ZHg`MYp#*5w|;uFBanuclK?d@l>u^`OIDxFPh4&Z$OWF)-l;9^ z-k1j_WgLHCu+D(L6+=iX+7d zBs(nl-mqt!3}w7ajKZ$>TQysaJLAOJ4j*NB*cRj!8%pHJ0wO%?(tm&;50z2w#)9i^ zl-X2F*j&ko`@~kze|?+=a_wcm%Tb^Bc_`lJw${ODm#MC}xs_9dvtbDlSY1fQ-4(E} zN-__RyW@MfLldNVHqoB1udLSe)U<|%3#h4Of1cC8#+lOBs& z=E-wLp7?cA?v|{9Gb-9PwnX(bfbP_=Hc>V~6Xe+qMs8tATLD#m;N?GEBSZX~ubSDm z=*hc*sPk@)hx2$n0}hTRtpv>`097IEuWz>C@Myrl9mXun(8WSGpSo7U+@Bv;u_VY2Q%>XxkY4&5W z;r&T7Sz;>YBRip3rqBiCOO>p8S@Zmp%~_H+o4lOV^^RToD!Mc7{gQLbSL;Lg*u%M^ ztYT^>F}d*Dk0N*6D@kMT&57DfJ|!kRwl9q;#lI4_*m2#c?3Et<)G9TbEOm1SP67^p z{9vm#(m%Bskpqow7QTfPOigB-g@5RxQ!}|59ZdoSxk+?5`)=uq_^e$x<-ugNjj0hFCyXq<8Gzz{n zLTuav$uxyxH)82%6+vc={^VuHIkQq(2c?Z9NaBcQQ$I8%{~a?a9g{AW_)0EYHnO%9 z1-DH~Sc=xx(RxQe38_;I~68zPtTuBM{rksW; z9Zz6Jpi+4XgXEu-)ezEQy4A#7rHerV`!ClhkBSfo5egV@`yBe-0=@rrEC}uS^|m25 zdVH1{yl8F^_k>WmNZ)-Os7`uW^5_@~KtPt*8nCFtxoH8UD1mFE0igW@IYjlnJkE6| zjr-_co~kt%k(1_3_|qHPi2J5Z#x9AZFs?FAXLHVc&ads|@T1hm!pALk6ZQ3NW@VY} zX09A@McH;`b4RmXr@Df;2$?p|Vne5+0UxCFrrD>cH?A{^<&3+-JOzPC@uc@CeMwf0ZC=U6Hnwet9LI=F3-Ma-i zkm&=6U80}dD2Rjtd4Vwfcl%|%t+@Zw+_k?YnQh^cI#!M{PE(FoPBlH&INown zQ-bW~*c_#?j*erfjj5T27Zg$iOf#m`>|!_bcBazId#0c$h>nuwH7|?_BHk7JP(%R{ zzC-61ng8Itf53kBe%HI!-s{Q`nu|NEsY(qQ5?MVIZj_J|S3=RC*!eaCyF ze?-3F7C6#wg`OQU$hUk`QUWmmquSCvuAqE-HR1c`sD$!-mhs zE}Y0X<0+78&R4TB?EToF(Ia!`^YT2ttFUxB(kvFU_{7oXW74g)$ZNNLO>HHS~O)Lu-QS+x0spJUiRpnWbVRb z|G5Smt#V-ONLFTFAs?o#M4W**eHk&?pA_q9oso6jbc(SxRP7LY9&)o;ge{;)IX=#g zT%}|3kiq#Vov71|F&A@xE6b_Ui7}cEm)Z=OT&_qc-86atutW>t3~%J+D_Y2@9DwF%W7_Kfa)^ZaHd}?M><=Sm_S}A;$|ycj zaM3n2@t&(R%|jOnZUsCDqPd}R|Kh;6TFWgd3D7*t-zRH@jqZw@$wS5=<*Yn6Ja-8q zQ;GSXU)mAe_$puls2gFJ#tF-*^`^{ab6J}$di)N29t~uHlc*Ivr^8wpobzR_JzQR! zvL(-vmeK=hN-+~TJA(x4>0*X5g|u2zP+y>y76YRMaXeDdf#PcVSnZ6H@$hspQA_}& z4x>UGcZob}6s2W`D<->Bj9E{09a0>ip=RCNdf`tJc(KBAKtC$6hA;-9_Co(=w(o=- zraFF0O%B?Dfo;e4AaFneV3?JZO=|w`BO+_GB~RXI&C><1{3L71vzWAKOjGA+>!NS3 z-9Bd}ut23R+sI!i*Pp*T2bs+ug%HpN0c{8Kv}OGD7KAbTaf=C zaAGWRXK}YFZ~O=EwQEQYkWE!G_~zL0&A<&jY_{7)whJggu2TW|1F&)=d?s%N;&V?8 z+=6-sO|O)5FjE1w>{KoURtOepi6s22`u4hvyx6+={kG2aieYOi_qTv862(tmaqbpO zI)QCZXjn!p8Q?z}6B?eQK6RLr21r1g?FhRZP4_`w+7-@ZM+z#4gD$cK4(6qlH4{0f z9@_&wOYR@o!1-*xvFob*L1Nylm-F0te7~ntx~a+bZqhZ5HE^TfsXiE$kU~%zQquNi z`d%PbDE}<>ii07PX#gzi+Ut?4GPaOXc1X2EPAH^HL!dtgO~-e9uo%y;T7V4lf6%f< zNIZADrK>G8)G~h^gP+Lc8#?_E>r7wE!Sbq$5v(TRB${i_X14I+Iq0QBcPo=rM8KR4 z?@;kdWmx~n>NO;`Hl|ef#s?(k8qenphna|`)f)bdv5v2GYnE;0FKGWU6o^cH3#RT6 zWIMS?hH7~8``JTxttyXj^5WJk0JJ5NrD_!@X>9nk{hC|p*$C<{6W!|(rrnKRiPMiT zrMlGTnVuVf?6q5#uuYC$_KTioiRgT~OY-nN_QDiG*5<&qw1E%tV%X+Dj?gAr>@v08 zjRI?W>^fOR4Tr9tYpB6kpTFWXShm*!l}+R9gB^(R>m> zCkzfQttF*6Fd}f%?0_%FI)#6o!^MEHtEOB^H^Mmy=u929w-k?$N}yyx9dDI6Pq9um z7@2c3Y!BUkqed7|+pVeu>({dITF&74Y61m6l|1YRQ@qk~FbMMv#OJ%Jd9M4NU28c9 zJt#2MB`Ua?@Oh{^*VcZ;_Y0}s$7wlfqYQB>FBSHRy`h+aw@>w0BKb}fPQ62AmdnHQ zQCwpf*SC4N&k~sSBJkY|JMXw$bdueqio_&P#u4h;)Tl4Fl4FZC`<8MjV(6grGfFo> z!wA@jiOEXPnx=Is{K=kN;@rodAWx>oL?0{j&6+dDL@+41Lb=4l({`fxUFPvz3m`*= z>~B)NDyH!YXtB)bPyB{!t1RXWhScig$4ity=PeYAXGn`n92y-#J}w8T1=URY%A)zM zyD`M2D2uNGUu{!mz^%#5Wl?qP`ADeBmdre=}DXoIeII=GPa~Mvm}^E0)}=;!l1m z>yE%$un<>Pz2Vv&n%uq8l!<+y?1~~iNUK2+SM>pTKQ2}bU+n-|dh$hHF2S-*p<)-s zEkFu|^EA%k36PnRW2)w@Ly)1e7&3h%m4^?YcJm}R%d;`WWk-PhICq>x;`g)|VW9Jj zf_fNP@pcg}|24oJ9$ap6LTh6VJz$`Txk;{TCcH^LNn!HHpbd7rW&qEdO^b*hO_ zf6j~i(sh}8g1+xgF(AsX>#o5M{_iMgovDp#@%Y zBrvYhc|KCciPE$U0M$&fT)@-mB_$0LxYkkYM9p0o32(sus7?HKmd3s>)UI8z(bMtj zG?1Ma{mJX%jEII8&vz^SBJfTXfXYRFl!6jQyIQA!Xc!m(lm3G6UfPm>ziD3t3s`j7 z91@0(t0%1Qac=-u%gEjmqzvpPgB2)-M^h2!X%Ejkvv`C3aEJcwmU*u~>q^rDC6=jL zh}xBFSf~<(;dPytUBk=^7tEwLsqn6&HA)g-L4Mb_ yxC6@&b`QiZO)aoPxed1CLW)Co7HzrTiB^vv6eq literal 0 HcmV?d00001 diff --git a/emulator/game_rio.conf b/emulator/game_rio.conf index c237480..e02efce 100644 --- a/emulator/game_rio.conf +++ b/emulator/game_rio.conf @@ -15,7 +15,17 @@ cputype=pentium cycles=max # RIO cockpit controls on COM1 (Phase 5 passthrough), plasma COM2 later. [serial] -serial1=directserial realport:COM1 +# Custom fork options for the RIO's few-ms ACK deadline: +# rxpollus:100 -- host-port receive poll tick in microseconds (stock=1000); +# discovers inbound bytes ~10x sooner. +# rxburst:16 -- deliver host-buffered bytes 16x faster than emulated wire +# speed. Stock DOSBox re-serializes each received byte at +# ~1ms (9600 baud) even though the bytes already paid wire +# time on the physical cable -- a 15-byte analog reply got +# +14ms of artificial latency, blowing the RIO's window and +# dropping the game into its 15-second retry fallback +# ("really slow polling"). +serial1=directserial realport:COM1 rxpollus:100 rxburst:16 serial2=disabled [autoexec] mount c "C:\VWE\TeslaRel410\ALPHA_1" diff --git a/emulator/vpx-device/serialport/README.md b/emulator/vpx-device/serialport/README.md new file mode 100644 index 0000000..f6a0981 --- /dev/null +++ b/emulator/vpx-device/serialport/README.md @@ -0,0 +1,12 @@ +# Modified DOSBox-X serial passthrough (RIO latency options) + +Drop these over `src/hardware/serialport/` in the DOSBox-X source tree +(v2026.06.02). They add two `directserial` options for the VWE RIO cockpit +board's few-millisecond ACK deadline (see ../../RIO-NOTES.md): + +- `rxpollus:` (50-1000) - host-port receive poll tick in microseconds + (stock = 1000). Discovers inbound bytes sooner. +- `rxburst:` (1-64) - deliver host-buffered received bytes n times faster + than emulated wire speed (they already paid wire time on the real cable). + +Usage: `serial1=directserial realport:COM1 rxpollus:100 rxburst:16` diff --git a/emulator/vpx-device/serialport/directserial.cpp b/emulator/vpx-device/serialport/directserial.cpp new file mode 100644 index 0000000..65b750e --- /dev/null +++ b/emulator/vpx-device/serialport/directserial.cpp @@ -0,0 +1,375 @@ +/* + * Copyright (C) 2002-2021 The DOSBox Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include "dosbox.h" + +#if C_DIRECTSERIAL + +#include "logging.h" +#include "serialport.h" +#include "directserial.h" +#include "misc_util.h" +#include "pic.h" + +#include "libserial.h" + +/* This is a serial passthrough class. Its amazingly simple to */ +/* write now that the serial ports themselves were abstracted out */ + +CDirectSerial::CDirectSerial (Bitu id, CommandLine* cmd) + :CSerial (id, cmd) { + InstallationSuccessful = false; + comport = nullptr; + + rx_retry = 0; + rx_retry_max = 0; + rx_state = 0; + + std::string tmpstring; + if(!cmd->FindStringBegin("realport:",tmpstring,false)) return; + + LOG_MSG ("Serial%d: Opening %s", (int)(COMNUMBER), tmpstring.c_str()); + if(!SERIAL_open(tmpstring.c_str(), &comport)) { + char errorbuffer[256]; + SERIAL_getErrorString(errorbuffer, sizeof(errorbuffer)); + LOG_MSG("Serial%d: Serial Port \"%s\" could not be opened.", + (int)(COMNUMBER), tmpstring.c_str()); + LOG_MSG("%s",errorbuffer); + return; + } + + // rxdelay: How many milliseconds to wait before causing an + // overflow when the application is unresponsive. + if(getBituSubstring("rxdelay:", &rx_retry_max, cmd)) { + if(!(rx_retry_max<=10000)) { + rx_retry_max=0; + } + } + + // rxpollus: host-port receive polling tick in MICROSECONDS (50..1000, + // default 1000 = the stock 1ms tick). Lower values discover incoming + // bytes sooner -- needed for hard-real-time serial peripherals like the + // VWE RIO cockpit board, which drops comms on ACKs late by a few ms. + Bitu rx_poll_us = 0; + if(getBituSubstring("rxpollus:", &rx_poll_us, cmd)) { + if(rx_poll_us < 50) rx_poll_us = 50; + if(rx_poll_us > 1000) rx_poll_us = 1000; + rx_poll_ms = (float)rx_poll_us / 1000.0f; + LOG_MSG("Serial%d: receive poll tick %uus", (int)(COMNUMBER), + (unsigned)rx_poll_us); + } + + // rxburst: deliver host-buffered received bytes times faster than + // emulated wire speed (they already paid wire time on the real cable). + Bitu rx_burst = 0; + if(getBituSubstring("rxburst:", &rx_burst, cmd)) { + if(rx_burst < 1) rx_burst = 1; + if(rx_burst > 64) rx_burst = 64; + rx_burst_div = (float)rx_burst; + LOG_MSG("Serial%d: receive burst delivery x%u", (int)(COMNUMBER), + (unsigned)rx_burst); + } + + CSerial::Init_Registers(); + InstallationSuccessful = true; + rx_state = D_RX_IDLE; + setEvent(SERIAL_POLLING_EVENT, rx_poll_ms); // receive tick +} + +CDirectSerial::~CDirectSerial () { + if(comport) SERIAL_close(comport); + // We do not use own events so we don't have to clear them. +} + +// CanReceive: true:UART part has room left +// doReceive: true:there was really a byte to receive +// rx_retry is incremented in polling events + +// in POLLING_EVENT: always add new polling event +// D_RX_IDLE + CanReceive + doReceive -> D_RX_WAIT , add RX_EVENT +// D_RX_IDLE + CanReceive + not doReceive -> D_RX_IDLE +// D_RX_IDLE + not CanReceive -> D_RX_BLOCKED, add RX_EVENT + +// D_RX_BLOCKED + CanReceive + doReceive -> D_RX_FASTWAIT, rem RX_EVENT +// rx_retry=0 , add RX_EVENT +// D_RX_BLOCKED + CanReceive + !doReceive -> D_RX_IDLE, rem RX_EVENT +// rx_retry=0 +// D_RX_BLOCKED + !CanReceive + doReceive + retry < max -> D_RX_BLOCKED, rx_retry++ +// D_RX_BLOCKED + !CanReceive + doReceive + retry >=max -> rx_retry=0 + +// to be continued... + +void CDirectSerial::handleUpperEvent(uint16_t type) { +/* +#if SERIAL_DEBUG + const char* s; + const char* s2; + switch(type) { + case SERIAL_POLLING_EVENT: s = "POLLING_EVENT"; break; + case SERIAL_RX_EVENT: s = "RX_EVENT"; break; + case SERIAL_TX_EVENT: s = "TX_EVENT"; break; + case SERIAL_THR_EVENT: s = "THR_EVENT"; break; + } + switch(rx_state) { + case D_RX_IDLE: s2 = "RX_IDLE"; break; + case D_RX_WAIT: s2 = "RX_WAIT"; break; + case D_RX_BLOCKED: s2 = "RX_BLOCKED"; break; + case D_RX_FASTWAIT: s2 = "RX_FASTWAIT"; break; + } + log_ser(dbg_aux,"Directserial: Event enter %s, %s",s,s2); +#endif + */ + + switch(type) { + case SERIAL_POLLING_EVENT: { + setEvent(SERIAL_POLLING_EVENT, rx_poll_ms); + // update Modem input line states + switch(rx_state) { + case D_RX_IDLE: + if(CanReceiveByte()) { + if(doReceive()) { + // a byte was received + rx_state=D_RX_WAIT; + setEvent(SERIAL_RX_EVENT, bytetime*0.9f/rx_burst_div); + } // else still idle + } else { +#if SERIAL_DEBUG + if(!dbgmsg_poll_block) { + log_ser(dbg_aux,"Directserial: block on polling."); + dbgmsg_poll_block=true; + } +#endif + rx_state=D_RX_BLOCKED; + // have both delays (1ms + bytetime) + setEvent(SERIAL_RX_EVENT, bytetime*0.9f/rx_burst_div); + } + break; + case D_RX_BLOCKED: + // one timeout tick + if(!CanReceiveByte()) { + rx_retry++; + if(rx_retry>=rx_retry_max) { + // it has timed out: + rx_retry=0; + removeEvent(SERIAL_RX_EVENT); + if(doReceive()) { + // read away everything + // this will set overrun errors + while(doReceive()); + rx_state=D_RX_WAIT; + setEvent(SERIAL_RX_EVENT, bytetime*0.9f/rx_burst_div); + } else { + // much trouble about nothing + rx_state=D_RX_IDLE; + } + } // else wait further + } else { + // good: we can receive again +#if SERIAL_DEBUG + dbgmsg_poll_block=false; + dbgmsg_rx_block=false; +#endif + removeEvent(SERIAL_RX_EVENT); + rx_retry=0; + if(doReceive()) { + rx_state=D_RX_FASTWAIT; + setEvent(SERIAL_RX_EVENT, bytetime*0.65f/rx_burst_div); + } else { + // much trouble about nothing + rx_state=D_RX_IDLE; + } + } + break; + + case D_RX_WAIT: + case D_RX_FASTWAIT: + break; + } + updateMSR(); + break; + } + case SERIAL_RX_EVENT: { + switch(rx_state) { + case D_RX_IDLE: + LOG_MSG("internal error in directserial"); + break; + + case D_RX_BLOCKED: // try to receive + case D_RX_WAIT: + case D_RX_FASTWAIT: + if(CanReceiveByte()) { + // just works or unblocked + rx_retry=0; // not waiting anymore + if(doReceive()) { + if(rx_state==D_RX_WAIT) setEvent(SERIAL_RX_EVENT, bytetime*0.9f/rx_burst_div); + else { + // maybe unblocked + rx_state=D_RX_FASTWAIT; + setEvent(SERIAL_RX_EVENT, bytetime*0.65f/rx_burst_div); + } + } else { + // didn't receive anything + rx_state=D_RX_IDLE; + } + } else { + // blocking now or still blocked +#if SERIAL_DEBUG + if(rx_state==D_RX_BLOCKED) { + if(!dbgmsg_rx_block) { + log_ser(dbg_aux,"Directserial: rx still blocked (retry=%d)",rx_retry); + dbgmsg_rx_block=true; + } + } + + + + + + + else log_ser(dbg_aux,"Directserial: block on continued rx (retry=%d).",rx_retry); +#endif + setEvent(SERIAL_RX_EVENT, bytetime*0.65f/rx_burst_div); + rx_state=D_RX_BLOCKED; + } + + break; + } + updateMSR(); + break; + } + case SERIAL_TX_EVENT: { + // Maybe echo circuit works a bit better this way + if(rx_state==D_RX_IDLE && CanReceiveByte()) { + if(doReceive()) { + // a byte was received + rx_state=D_RX_WAIT; + setEvent(SERIAL_RX_EVENT, bytetime*0.9f/rx_burst_div); + } + } + ByteTransmitted(); + updateMSR(); + break; + } + case SERIAL_THR_EVENT: { + ByteTransmitting(); + setEvent(SERIAL_TX_EVENT,bytetime*1.1f); + break; + } + } + /* + #if SERIAL_DEBUG + switch(type) { + case SERIAL_POLLING_EVENT: s = "POLLING_EVENT"; break; + case SERIAL_RX_EVENT: s = "RX_EVENT"; break; + case SERIAL_TX_EVENT: s = "TX_EVENT"; break; + case SERIAL_THR_EVENT: s = "THR_EVENT"; break; + } + switch(rx_state) { + case D_RX_IDLE: s2 = "RX_IDLE"; break; + case D_RX_WAIT: s2 = "RX_WAIT"; break; + case D_RX_BLOCKED: s2 = "RX_BLOCKED"; break; + case D_RX_FASTWAIT: s2 = "RX_FASTWAIT"; break; + } + log_ser(dbg_aux,"Directserial: Event exit %s, %s",s,s2); +#endif*/ +} + +bool CDirectSerial::doReceive() { + int value = SERIAL_getextchar(comport); + if(value) { + receiveByteEx((uint8_t)(value&0xff),(uint8_t)((value&0xff00)>>8)); + return true; + } + return false; +} + +// updatePortConfig is called when emulated app changes the serial port +// parameters baudrate, stopbits, number of databits, parity. +void CDirectSerial::updatePortConfig (uint16_t divider, uint8_t lcr) { + uint8_t parity = 0; + + switch ((lcr & 0x38)>>3) { + case 0x1: parity='o'; break; + case 0x3: parity='e'; break; + case 0x5: parity='m'; break; + case 0x7: parity='s'; break; + default: parity='n'; break; + } + + uint8_t bytelength = (lcr & 0x3)+5; + + // baudrate + Bitu baudrate; + if(divider==0) baudrate=115200u; + else baudrate = 115200u / divider; + + // stopbits + uint8_t stopbits; + if (lcr & 0x4) { + if (bytelength == 5) stopbits = SERIAL_15STOP; + else stopbits = SERIAL_2STOP; + } else stopbits = SERIAL_1STOP; + + if(!SERIAL_setCommParameters(comport, (int)baudrate, (char)parity, (char)stopbits, (char)bytelength)) { +#if SERIAL_DEBUG + log_ser(dbg_aux,"Serial port settings not supported by host." ); +#endif + LOG_MSG ("Serial%d: Desired serial mode not supported (%d,%d,%c,%d)", + (int)(COMNUMBER),(int)baudrate,(int)bytelength,parity,(int)stopbits); + } + CDirectSerial::setRTSDTR(getRTS(), getDTR()); +} + +void CDirectSerial::updateMSR () { + int new_status = SERIAL_getmodemstatus(comport); + + setCTS((new_status&SERIAL_CTS)? true:false); + setDSR((new_status&SERIAL_DSR)? true:false); + setRI((new_status&SERIAL_RI)? true:false); + setCD((new_status&SERIAL_CD)? true:false); +} + +void CDirectSerial::transmitByte (uint8_t val, bool first) { + if(!SERIAL_sendchar(comport, (char)val)) + LOG_MSG("Serial%d: COM port error: write failed!", (int)COMNUMBER); + if(first) setEvent(SERIAL_THR_EVENT, bytetime/8); + else setEvent(SERIAL_TX_EVENT, bytetime); +} + + +// setBreak(val) switches break on or off +void CDirectSerial::setBreak (bool value) { + SERIAL_setBREAK(comport,value); +} + +// updateModemControlLines(mcr) sets DTR and RTS. +void CDirectSerial::setRTSDTR(bool rts, bool dtr) { + SERIAL_setRTS(comport,rts); + SERIAL_setDTR(comport,dtr); +} + +void CDirectSerial::setRTS(bool val) { + SERIAL_setRTS(comport,val); +} + +void CDirectSerial::setDTR(bool val) { + SERIAL_setDTR(comport,val); +} + +#endif diff --git a/emulator/vpx-device/serialport/directserial.h b/emulator/vpx-device/serialport/directserial.h new file mode 100644 index 0000000..4c1645b --- /dev/null +++ b/emulator/vpx-device/serialport/directserial.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2002-2021 The DOSBox Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +// include guard +#ifndef DOSBOX_DIRECTSERIAL_WIN32_H +#define DOSBOX_DIRECTSERIAL_WIN32_H + +#include "dosbox.h" + +#if C_DIRECTSERIAL + +#define DIRECTSERIAL_AVAILIBLE +#include "serialport.h" + +#include "libserial.h" + +class CDirectSerial : public CSerial { +public: + CDirectSerial(Bitu id, CommandLine* cmd); + ~CDirectSerial(); + + void updatePortConfig(uint16_t divider, uint8_t lcr) override; + void updateMSR() override; + void transmitByte(uint8_t val, bool first) override; + void setBreak(bool value) override; + + void setRTSDTR(bool rts, bool dtr) override; + void setRTS(bool val) override; + void setDTR(bool val) override; + void handleUpperEvent(uint16_t type) override; + +private: + COMPORT comport; + + Bitu rx_state = 0; +#define D_RX_IDLE 0 +#define D_RX_WAIT 1 +#define D_RX_BLOCKED 2 +#define D_RX_FASTWAIT 3 + + Bitu rx_retry; // counter of retries (every millisecond) + Bitu rx_retry_max; // how many POLL_EVENTS to wait before causing + // an overrun error. + // Host-port receive polling tick in milliseconds (default 1.0). + // Latency-critical hardware (VWE RIO cockpit board: drops comms when an + // ACK is late by a few ms) needs incoming bytes discovered faster than + // the stock 1ms tick; configurable via "rxpollus:". + float rx_poll_ms = 1.0f; + // Burst-delivery divisor ("rxburst:"). Stock DOSBox re-serializes + // received bytes at emulated wire speed (bytetime per byte) even though + // they already paid their wire time on the physical cable and are + // sitting in the host buffer. n>1 delivers buffered bytes n times + // faster, cutting multi-byte reply latency (RIO analog packets). + float rx_burst_div = 1.0f; + bool doReceive(); + +#if SERIAL_DEBUG + bool dbgmsg_poll_block = false; + bool dbgmsg_rx_block = false; +#endif + +}; + +#endif // C_DIRECTSERIAL +#endif // include guard diff --git a/emulator/vpx-device/vpxlog.cpp b/emulator/vpx-device/vpxlog.cpp index ec98559..dfcd404 100644 --- a/emulator/vpx-device/vpxlog.cpp +++ b/emulator/vpx-device/vpxlog.cpp @@ -435,7 +435,16 @@ static float rd_f32(const unsigned char *p) { } struct VCol { float c[3]; }; -struct VPoly { float rgb[3]; std::vector xyz; }; /* x,y,z triples */ +struct VPoly { + float rgb[3]; + std::vector xyz; /* x,y,z triples */ + std::vector uv; /* u,v pairs (empty = untextured) */ + unsigned matkey; /* material name for texture lookup, 0 = none */ + VPoly() : matkey(0) {} +}; +/* Baked RGBA texture (texture intensity x material ramp), keyed by material. + * Shared with the GL thread under rt_lock; ver bumps force re-upload. */ +struct TexImg { int w, h; unsigned ver; std::vector rgba; }; struct VFrame { bool valid; float bg[3]; @@ -456,9 +465,13 @@ struct VFrame { struct M16 { float m[16]; }; /* row-major 4x4, row 3 = translation */ +struct VRamp { float lo[3], hi[3]; }; +struct VTex { int w, h; unsigned ver; std::vector px; }; + static struct VScene { std::map type; /* name -> node type */ std::map > verts; /* geometry -> xyz */ + std::map > uvs; /* geometry -> u,v */ std::map > > polys; std::map mat; /* material -> RGB */ std::map ggmat; /* geogroup -> material */ @@ -467,11 +480,21 @@ static struct VScene { std::map inst_object; /* instance -> object */ std::map dcs_mat; /* dcs -> local matrix */ std::map dcs_parent; /* dcs_link child->parent */ + /* texture chain: material -> texmap -> texture, colorized by the + * material's ramp (type 14: lo/hi RGB; texels are intensities) */ + std::map tex; /* texture node -> texels */ + std::map texmap_tex; /* texmap -> texture */ + std::map mat_texmap; /* material -> texmap */ + std::map mat_ramp; /* material -> ramp */ + std::map ramp; /* ramp -> lo/hi */ VFrame view; /* view/bg/camera state */ /* multi-burst assembly (stride-aware: game verts are 3..9 floats each) */ unsigned geom_node; size_t geom_need, geom_stride; bool geom_active; std::vector geom_acc; unsigned conn_node, conn_npolys, conn_loop; bool conn_active; + /* action-26 texel upload assembly */ + unsigned tex_node; size_t tex_need; bool tex_active; + std::vector tex_acc; } S; static void m16_id(M16 &o) { @@ -516,6 +539,7 @@ static CRITICAL_SECTION rt_lock; static VFrame rt_pending; static bool rt_new = false; static unsigned long rt_frames = 0; +static std::map rt_texs; /* material -> baked RGBA (rt_lock) */ static void rt_draw(HDC dc, const VFrame &f, int cw, int ch) { glViewport(0, 0, cw, ch); @@ -545,14 +569,55 @@ static void rt_draw(HDC dc, const VFrame &f, int cw, int ch) { glDisable(GL_CULL_FACE); glDisable(GL_LIGHTING); glShadeModel(GL_FLAT); + /* upload any new/changed baked material textures */ + static std::map gltex; /* matkey -> GL name */ + static std::map gltex_ver; + EnterCriticalSection(&rt_lock); + std::map texs = rt_texs; /* small; copy out */ + LeaveCriticalSection(&rt_lock); + for (std::map::const_iterator it = texs.begin(); + it != texs.end(); ++it) { + std::map::const_iterator vi = + gltex_ver.find(it->first); + if (vi != gltex_ver.end() && vi->second == it->second.ver) continue; + GLuint id; + std::map::const_iterator gi = gltex.find(it->first); + if (gi == gltex.end()) { glGenTextures(1, &id); gltex[it->first] = id; } + else id = gi->second; + glBindTexture(GL_TEXTURE_2D, id); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, it->second.w, it->second.h, + 0, GL_RGBA, GL_UNSIGNED_BYTE, &it->second.rgba[0]); + gltex_ver[it->first] = it->second.ver; + } + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + unsigned bound = 0; for (size_t i = 0; i < f.polys.size(); i++) { const VPoly &p = f.polys[i]; - glColor3f(p.rgb[0], p.rgb[1], p.rgb[2]); + bool tex = p.matkey && gltex.count(p.matkey) && + p.uv.size() * 3 == p.xyz.size() * 2; + if (tex) { + if (bound != p.matkey) { + glBindTexture(GL_TEXTURE_2D, gltex[p.matkey]); + bound = p.matkey; + } + glEnable(GL_TEXTURE_2D); + glColor3f(1.0f, 1.0f, 1.0f); + } else { + glDisable(GL_TEXTURE_2D); + glColor3f(p.rgb[0], p.rgb[1], p.rgb[2]); + } glBegin(GL_POLYGON); - for (size_t v = 0; v + 2 < p.xyz.size(); v += 3) + for (size_t v = 0; v + 2 < p.xyz.size(); v += 3) { + if (tex) glTexCoord2f(p.uv[v / 3 * 2], p.uv[v / 3 * 2 + 1]); glVertex3f(p.xyz[v], p.xyz[v + 1], p.xyz[v + 2]); + } glEnd(); } + glDisable(GL_TEXTURE_2D); } SwapBuffers(dc); } @@ -610,13 +675,59 @@ static DWORD WINAPI rt_main(LPVOID) { } /* ---- scene-graph message decode ----------------------------------------- */ + +/* Bake material's texture (intensity) through its ramp into RGBA and stash it + * for the GL thread. Returns the material key if textured, else 0. */ +static std::map baked_ver; /* material -> baked tex ver */ +static unsigned bake_material_tex(unsigned matname) { + std::map::const_iterator tmi = S.mat_texmap.find(matname); + if (tmi == S.mat_texmap.end()) return 0; + std::map::const_iterator txi = S.texmap_tex.find(tmi->second); + if (txi == S.texmap_tex.end()) return 0; + std::map::const_iterator ti = S.tex.find(txi->second); + if (ti == S.tex.end() || ti->second.px.empty()) return 0; + const VTex &t = ti->second; + if ((size_t)t.w * t.h > t.px.size()) return 0; + std::map::const_iterator bi = baked_ver.find(matname); + if (bi != baked_ver.end() && bi->second == t.ver) return matname; + float lo[3] = { 0, 0, 0 }, hi[3] = { 1, 1, 1 }; + std::map::const_iterator ri = S.mat_ramp.find(matname); + if (ri != S.mat_ramp.end()) { + std::map::const_iterator rr = S.ramp.find(ri->second); + if (rr != S.ramp.end()) { + memcpy(lo, rr->second.lo, sizeof lo); + memcpy(hi, rr->second.hi, sizeof hi); + } + } + TexImg img; + img.w = t.w; img.h = t.h; img.ver = t.ver; + img.rgba.resize((size_t)t.w * t.h * 4); + for (size_t i = 0; i < (size_t)t.w * t.h; i++) { + float a = t.px[i] / 255.0f; + for (int c = 0; c < 3; c++) { + float v = lo[c] + (hi[c] - lo[c]) * a; + img.rgba[i * 4 + c] = + (unsigned char)(v < 0 ? 0 : v > 1 ? 255 : v * 255.0f + 0.5f); + } + img.rgba[i * 4 + 3] = 255; + } + EnterCriticalSection(&rt_lock); + rt_texs[matname] = img; + LeaveCriticalSection(&rt_lock); + baked_ver[matname] = t.ver; + return matname; +} + static void emit_geogroup(VFrame &f, unsigned gg, const M16 *world) { VCol col = { { 1.0f, 0.0f, 1.0f } }; /* missing = magenta */ + unsigned matname = 0; std::map::const_iterator gmi = S.ggmat.find(gg); if (gmi != S.ggmat.end()) { - std::map::const_iterator mi = S.mat.find(gmi->second); + matname = gmi->second; + std::map::const_iterator mi = S.mat.find(matname); if (mi != S.mat.end()) col = mi->second; } + unsigned texkey = matname ? bake_material_tex(matname) : 0; std::map >::const_iterator ci = S.children.find(gg); if (ci == S.children.end()) return; @@ -628,10 +739,16 @@ static void emit_geogroup(VFrame &f, unsigned gg, const M16 *world) { pi = S.polys.find(geo); if (vi == S.verts.end() || pi == S.polys.end()) continue; const std::vector &vv = vi->second; + const std::vector *uv = NULL; + std::map >::const_iterator ui = + S.uvs.find(geo); + if (ui != S.uvs.end() && ui->second.size() * 3 == vv.size() * 2) + uv = &ui->second; for (size_t q = 0; q < pi->second.size(); q++) { const std::vector &idx = pi->second[q]; VPoly poly; memcpy(poly.rgb, col.c, sizeof poly.rgb); + poly.matkey = (texkey && uv) ? texkey : 0; for (size_t j = 0; j < idx.size(); j++) { size_t o = (size_t)idx[j] * 3; if (o + 2 >= vv.size()) continue; @@ -641,6 +758,10 @@ static void emit_geogroup(VFrame &f, unsigned gg, const M16 *world) { poly.xyz.push_back(out[0]); poly.xyz.push_back(out[1]); poly.xyz.push_back(out[2]); + if (poly.matkey) { + poly.uv.push_back((*uv)[(size_t)idx[j] * 2]); + poly.uv.push_back((*uv)[(size_t)idx[j] * 2 + 1]); + } } if (poly.xyz.size() >= 9) f.polys.push_back(poly); } @@ -702,18 +823,38 @@ static void scene_burst(const unsigned char *p, size_t n) { S.geom_acc.push_back(rd_f32(d + o)); if (S.geom_acc.size() >= S.geom_need * S.geom_stride) { std::vector &vl = S.verts[S.geom_node]; - vl.clear(); + std::vector &tl = S.uvs[S.geom_node]; + vl.clear(); tl.clear(); + /* uv offset within a vertex record by stride: 5 = xyz+uv, + * 8/9 = xyz+normal+uv(+extra) */ + size_t uvo = (S.geom_stride >= 8) ? 6 : + (S.geom_stride == 5) ? 3 : 0; for (size_t i = 0; i + 2 < S.geom_need * S.geom_stride; i += S.geom_stride) { vl.push_back(S.geom_acc[i]); vl.push_back(S.geom_acc[i + 1]); vl.push_back(S.geom_acc[i + 2]); + if (uvo && i + uvo + 1 < S.geom_acc.size()) { + tl.push_back(S.geom_acc[i + uvo]); + tl.push_back(S.geom_acc[i + uvo + 1]); + } else { tl.push_back(0); tl.push_back(0); } } S.geom_acc.clear(); S.geom_active = false; } return; } + if (S.tex_active && action == 26) { + for (size_t o = 0; o < nb; o++) S.tex_acc.push_back(d[o]); + if (S.tex_acc.size() >= S.tex_need) { + VTex &t = S.tex[S.tex_node]; + t.px.assign(S.tex_acc.begin(), S.tex_acc.begin() + S.tex_need); + t.ver++; + S.tex_acc.clear(); + S.tex_active = false; + } + return; + } if (S.conn_active && action == 25) { std::vector > &pl = S.polys[S.conn_node]; size_t nw = nb / 4; @@ -737,6 +878,33 @@ static void scene_burst(const unsigned char *p, size_t n) { if (t == 11 && nb >= 92) { /* material diffuse */ VCol c = { { rd_f32(d + 48), rd_f32(d + 52), rd_f32(d + 56) } }; S.mat[name] = c; + /* texmap / ramp refs: identify fields by node type */ + for (size_t o = 8; o + 3 < nb; o += 4) { + unsigned val = rd_u32(d + o); + if (!val || val == 0xFFFFFFFFu) continue; + std::map::const_iterator ti = + S.type.find(val); + if (ti == S.type.end()) continue; + if (ti->second == 12) S.mat_texmap[name] = val; + else if (ti->second == 14) S.mat_ramp[name] = val; + } + } else if (t == 12 && nb >= 12) { /* texmap -> texture */ + for (size_t o = 8; o + 3 < nb; o += 4) { + unsigned val = rd_u32(d + o); + if (!val || val == 0xFFFFFFFFu) continue; + std::map::const_iterator ti = + S.type.find(val); + if (ti != S.type.end() && ti->second == 13) { + S.texmap_tex[name] = val; + break; + } + } + } else if (t == 14 && nb >= 36) { /* ramp lo/hi RGB */ + VRamp &r = S.ramp[name]; + for (int i = 0; i < 3; i++) { + r.lo[i] = rd_f32(d + 8 + i * 4); + r.hi[i] = rd_f32(d + 20 + i * 4); + } } else if (t == 9 && nb >= 80) { /* geogroup material */ S.ggmat[name] = rd_u32(d + 64); } else if (t == 3 && nb >= 104) { /* view */ @@ -792,6 +960,21 @@ static void scene_burst(const unsigned char *p, size_t n) { S.polys[S.conn_node].clear(); } break; + case 26: /* texel upload header [node][nbytes][w][h]... (8bpp) */ + if (nb >= 16) { + unsigned node = rd_u32(d), nbytes = rd_u32(d + 4); + unsigned tw = rd_u32(d + 8), th = rd_u32(d + 12); + if (nbytes && nbytes <= (1u << 20) && tw && th && + tw <= 1024 && th <= 1024 && (size_t)tw * th <= nbytes) { + S.tex_node = node; + S.tex_need = nbytes; + S.tex_active = true; + S.tex_acc.clear(); + VTex &t = S.tex[node]; + t.w = (int)tw; t.h = (int)th; + } + } + break; case 31: /* per-frame camera [?][view][3x3 rows][eye] */ if (nb >= 56) { for (int i = 0; i < 9; i++)