From 27b1d7899dbec123880384f1bfc6975661ab15b8 Mon Sep 17 00:00:00 2001 From: gho tik Date: Sat, 29 Oct 2016 12:47:56 -0400 Subject: [PATCH] v2_03_94_srcfx2 Former-commit-id: 8b755b36cf591992a77b9b707a8f492910acfd78 --- build/dxwnd.dll | 4 +- build/exports/Aztec Wars.dxw | 35 ++++ build/exports/FIFA 2000.dxw | 35 ++++ build/exports/Gunbound (fullscreen).dxw | 35 ++++ build/exports/Nascar Racing 3.dxw | 27 ++- build/readme-relnotes.txt | 11 +- dll/ddblit.cpp | 14 +- dll/ddraw.cpp | 47 ++++- dll/dxhook.cpp | 2 +- dll/dxmapping.cpp | 1 + dll/dxwnd.cpp | 16 +- dll/dxwnd.vs2008.suo | Bin 83968 -> 66560 bytes dll/hd3d7.cpp | 68 +++++- dll/logall.h | 4 + dll/user32.cpp | 8 +- dll/winmm.cpp | 263 ++++++++++++++++++------ 16 files changed, 473 insertions(+), 97 deletions(-) create mode 100644 build/exports/Aztec Wars.dxw create mode 100644 build/exports/FIFA 2000.dxw create mode 100644 build/exports/Gunbound (fullscreen).dxw diff --git a/build/dxwnd.dll b/build/dxwnd.dll index 0e698fd..e23cee5 100644 --- a/build/dxwnd.dll +++ b/build/dxwnd.dll @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e28f7cec6d5eae185c1bdf2c27ab49e1357051f487c243b60f5d25b11172c21f -size 694784 +oid sha256:aefbbf270e55f04618178767c563b732a273e29eac1e0a68a42bd15c1be21058 +size 697856 diff --git a/build/exports/Aztec Wars.dxw b/build/exports/Aztec Wars.dxw new file mode 100644 index 0000000..0cb2426 --- /dev/null +++ b/build/exports/Aztec Wars.dxw @@ -0,0 +1,35 @@ +[target] +title0=Aztec Wars +path0=F:\Games\Aztec Wars\The Aztec\Aztec.exe +startfolder0= +launchpath0= +module0= +opengllib0= +notes0= +registry0= +ver0=0 +monitorid0=-1 +coord0=0 +flag0=136314914 +flagg0=1207959552 +flagh0=20 +flagi0=138412036 +flagj0=67113088 +flagk0=65536 +flagl0=65536 +flagm0=0 +tflag0=0 +dflag0=0 +posx0=50 +posy0=50 +sizx0=800 +sizy0=600 +maxfps0=0 +initts0=0 +winver0=0 +maxres0=-1 +swapeffect0=0 +maxddinterface0=7 +slowratio0=2 +initresw0=800 +initresh0=600 diff --git a/build/exports/FIFA 2000.dxw b/build/exports/FIFA 2000.dxw new file mode 100644 index 0000000..164eaa0 --- /dev/null +++ b/build/exports/FIFA 2000.dxw @@ -0,0 +1,35 @@ +[target] +title0=FIFA 2000 +path0=F:\Games\FIFA2000\fifa2000.exe +startfolder0= +launchpath0= +module0= +opengllib0= +notes0=Securdrive protected - needs crack +registry0= +ver0=0 +monitorid0=-1 +coord0=0 +flag0=752877602 +flagg0=1216348160 +flagh0=20 +flagi0=205520900 +flagj0=4224 +flagk0=65536 +flagl0=0 +flagm0=0 +tflag0=0 +dflag0=0 +posx0=50 +posy0=50 +sizx0=800 +sizy0=600 +maxfps0=0 +initts0=0 +winver0=0 +maxres0=-1 +swapeffect0=0 +maxddinterface0=7 +slowratio0=2 +initresw0=800 +initresh0=600 diff --git a/build/exports/Gunbound (fullscreen).dxw b/build/exports/Gunbound (fullscreen).dxw new file mode 100644 index 0000000..428eb85 --- /dev/null +++ b/build/exports/Gunbound (fullscreen).dxw @@ -0,0 +1,35 @@ +[target] +title0=Gunbound (fullscreen) +path0=F:\Games\GunboundGitzWC\gitzgame.exe +startfolder0= +launchpath0= +module0= +opengllib0= +notes0= +registry0= +ver0=0 +monitorid0=0 +coord0=1 +flag0=681582626 +flagg0=1073741840 +flagh0=20 +flagi0=136314884 +flagj0=4224 +flagk0=67584 +flagl0=0 +flagm0=0 +tflag0=0 +dflag0=0 +posx0=50 +posy0=50 +sizx0=0 +sizy0=0 +maxfps0=0 +initts0=0 +winver0=0 +maxres0=-1 +swapeffect0=0 +maxddinterface0=7 +slowratio0=2 +initresw0=0 +initresh0=0 diff --git a/build/exports/Nascar Racing 3.dxw b/build/exports/Nascar Racing 3.dxw index 1c2409b..907127f 100644 --- a/build/exports/Nascar Racing 3.dxw +++ b/build/exports/Nascar Racing 3.dxw @@ -1,15 +1,15 @@ [target] -title0=Nascar Racing 3 -path0=D:\Games\Nascar Racing 3\NASCAR Racing 3.exe +title0=NASCAR Racing 3 +path0=G:\Games\Nascar Racing 3\NASCAR Racing 3.exe module0= opengllib0= ver0=0 coord0=0 -flag0=671105056 +flag0=136314914 flagg0=1207959552 flagh0=20 -flagi0=4 -tflag0=6163 +flagi0=134217732 +tflag0=0 initx0=0 inity0=0 minx0=0 @@ -22,3 +22,20 @@ sizx0=800 sizy0=600 maxfps0=0 initts0=0 +startfolder0= +launchpath0= +notes0= +registry0= +monitorid0=-1 +flagj0=4224 +flagk0=65536 +flagl0=0 +flagm0=0 +dflag0=0 +winver0=0 +maxres0=-1 +swapeffect0=0 +maxddinterface0=7 +slowratio0=2 +initresw0=800 +initresh0=600 diff --git a/build/readme-relnotes.txt b/build/readme-relnotes.txt index 87d28d4..5f91a71 100644 --- a/build/readme-relnotes.txt +++ b/build/readme-relnotes.txt @@ -1319,4 +1319,13 @@ fix: avoid issuing a GetPalette method against a deallocated object fix: automatic creation of Clipper object if needed for emulated ddraw blit to primary surface - fixes the well known black blitting problem fix: proper handling of dinput DirectInputDevice::GetDeviceData() DI_BUFFEROVERFLOW error condition fix: user32 GetCursorPos() wrapper -fix: user32 mouse_event() wrapper \ No newline at end of file +fix: user32 mouse_event() wrapper + +v2.03.94.fx1-2: +fix: aligned Lock cheats in Direct/Indirect mode ...... +fix: no window interventions in non windowed mode. Fix "Gunbound" in fullscreen mode +fix: avoid multiple injection for early-hooked programs. Fix "Gunbound" regression. +fix: Restore of all lost surfaces upon D3D BeginScene DDERR_LOSTSURFACE error. Fix "Gunbound" lost textures in fullscreen mode. +add: more logging in mciSendCommand - possibly some regression problem... +fix: corrected clipping bug introduced in v2.03.93. +fix: SetWindowLog hooker preventing to set DxWnd windowproc when not in fullscreen mode. Fixes "Nascar Racing 3" recursion and crash. \ No newline at end of file diff --git a/dll/ddblit.cpp b/dll/ddblit.cpp index ca0d914..647bca1 100644 --- a/dll/ddblit.cpp +++ b/dll/ddblit.cpp @@ -8,8 +8,6 @@ #include "hddraw.h" #include "dxhelper.h" -#define FIXBIGGERRECT 1 - extern LPDIRECTDRAWSURFACE lpDDSEmu_Prim; extern LPDIRECTDRAW lpPrimaryDD; extern Blt_Type pBlt; @@ -167,20 +165,20 @@ static HRESULT sBltToPrimary(int dxversion, Blt_Type pBlt, char *api, LPDIRECTDR } #endif -#if FIXBIGGERRECT // seems necessary to "cure" the "FIFA 2000" soccer game in hw accelerated graphics, when the Unlock() method // receives RECT coordinates with big positive or negative numbers! + // v2.03.94fx1: "FIFA2002" is ok with Unlock rect set to Lock previous value, but other games ("Aztec Wars", ...) + // require some software clipping when moving the cursor outside the window or similar cases if(lpdestrect){ if(lpdestrect->top < 0) lpdestrect->top = 0; - if(lpdestrect->top > (LONG)dxw.GetScreenWidth()) lpdestrect->top = 0; + if(lpdestrect->top > (LONG)dxw.GetScreenHeight()) lpdestrect->top = dxw.GetScreenHeight(); if(lpdestrect->left < 0) lpdestrect->left = 0; - if(lpdestrect->left > (LONG)dxw.GetScreenHeight()) lpdestrect->left = 0; + if(lpdestrect->left > (LONG)dxw.GetScreenWidth()) lpdestrect->left = (LONG)dxw.GetScreenWidth(); if(lpdestrect->bottom > (LONG)dxw.GetScreenHeight()) lpdestrect->bottom = dxw.GetScreenHeight(); if(lpdestrect->right > (LONG)dxw.GetScreenWidth()) lpdestrect->right = dxw.GetScreenWidth(); - if(lpdestrect->bottom < lpdestrect->top) lpdestrect->bottom = (LONG)dxw.GetScreenHeight(); - if(lpdestrect->right < lpdestrect->left) lpdestrect->right = (LONG)dxw.GetScreenWidth(); + if(lpdestrect->bottom < lpdestrect->top) lpdestrect->bottom = lpdestrect->top; + if(lpdestrect->right < lpdestrect->left) lpdestrect->right = lpdestrect->left; } -#endif if(dxw.dwFlags5 & QUARTERBLT){ BOOL QuarterUpdate; diff --git a/dll/ddraw.cpp b/dll/ddraw.cpp index d31d507..77de78c 100644 --- a/dll/ddraw.cpp +++ b/dll/ddraw.cpp @@ -4434,6 +4434,24 @@ DDSURFACEDESC SaveSurfaceDesc; LPDIRECTDRAWSURFACE SaveSurface = NULL; LPRECT SaveLockedlpRect = NULL; RECT SaveLockedRect; +LPDIRECTDRAWSURFACE SaveLockedSurface = NULL; + +static void PushLockedRect(LPDIRECTDRAWSURFACE lpdds, LPRECT lprect) +{ + SaveLockedSurface = lpdds; + SaveLockedlpRect = lprect; + if(SaveLockedlpRect) SaveLockedRect = *lprect; +} + +static LPRECT PopLockedRect(LPDIRECTDRAWSURFACE lpdds, LPRECT lprect) +{ + if(lpdds == SaveLockedSurface){ + SaveLockedSurface = NULL; + return SaveLockedlpRect; + } + else + return lprect; +} static HRESULT WINAPI extLock(int dxversion, Lock_Type pLock, LPDIRECTDRAWSURFACE lpdds, LPRECT lprect, LPDDSURFACEDESC lpDDSurfaceDesc, DWORD flags, HANDLE hEvent) { @@ -4450,19 +4468,18 @@ static HRESULT WINAPI extLock(int dxversion, Lock_Type pLock, LPDIRECTDRAWSURFAC dxversion, lpdds, (IsPrim ? "(PRIM)":""), flags, ExplainLockFlags(flags), lpDDSurfaceDesc, sRect); } - SaveLockedlpRect = lprect; - if(SaveLockedlpRect) SaveLockedRect = *lprect; + PushLockedRect(lpdds, lprect); res=(*pLock)(lpdds, lprect, lpDDSurfaceDesc, flags, hEvent); if(res==DDERR_SURFACEBUSY){ // v70: fix for "Ancient Evil" (*pUnlockMethod(dxversion))(lpdds, NULL); res = (*pLock)(lpdds, lprect, lpDDSurfaceDesc, flags, hEvent); - OutTraceDW("Lock RETRY: ret=%x(%s)\n", res, ExplainDDError(res)); + OutTraceDW("Lock SURFACEBUSY RETRY: ret=%x(%s)\n", res, ExplainDDError(res)); } if(res==DDERR_SURFACELOST){ lpdds->Restore(); res = (*pLock)(lpdds, lprect, lpDDSurfaceDesc, flags, hEvent); - OutTraceDW("Lock RETRY: ret=%x(%s)\n", res, ExplainDDError(res)); + OutTraceDW("Lock SURFACELOST RETRY: ret=%x(%s)\n", res, ExplainDDError(res)); } if(res) OutTraceE("Lock ERROR: ret=%x(%s) at %d\n", res, ExplainDDError(res), __LINE__); OutTraceB("Lock: lPitch=%d lpSurface=%x ZBufferBitDepth=%d %s\n", @@ -4517,8 +4534,7 @@ static HRESULT WINAPI extLockDir(int dxversion, Lock_Type pLock, LPDIRECTDRAWSUR dxversion, lpdds, (IsPrim ? "(PRIM)":""), flags, ExplainLockFlags(flags), lpDDSurfaceDesc, sRect); } - SaveLockedlpRect = lprect; - if(SaveLockedlpRect) SaveLockedRect = *lprect; + PushLockedRect(lpdds, lprect); switch(dxversion){ case 1: pBlt=pBlt1; pGetGDISurface=pGetGDISurface1; break; @@ -4578,6 +4594,16 @@ static HRESULT WINAPI extLockDir(int dxversion, Lock_Type pLock, LPDIRECTDRAWSUR } res=(*pLock)(lpdds, lprect, lpDDSurfaceDesc, flags, hEvent); + if(res==DDERR_SURFACEBUSY){ // v70: fix for "Ancient Evil" + (*pUnlockMethod(dxversion))(lpdds, NULL); + res = (*pLock)(lpdds, lprect, lpDDSurfaceDesc, flags, hEvent); + OutTraceDW("Lock SURFACEBUSY RETRY: ret=%x(%s)\n", res, ExplainDDError(res)); + } + if(res==DDERR_SURFACELOST){ + lpdds->Restore(); + res = (*pLock)(lpdds, lprect, lpDDSurfaceDesc, flags, hEvent); + OutTraceDW("Lock SURFACELOST RETRY: ret=%x(%s)\n", res, ExplainDDError(res)); + } if(res) OutTraceE("Lock ERROR: ret=%x(%s) at %d\n", res, ExplainDDError(res), __LINE__); OutTraceB("Lock: lPitch=%d lpSurface=%x ZBufferBitDepth=%d %s\n", @@ -4589,6 +4615,11 @@ static HRESULT WINAPI extLockDir(int dxversion, Lock_Type pLock, LPDIRECTDRAWSUR if(dxw.dwFlags1 & SUPPRESSDXERRORS) res=DD_OK; + if((dxw.dwFlags6 & FIXPITCH) || (dxw.dwFlags3 & MARKLOCK)){ + SaveSurfaceDesc = *lpDDSurfaceDesc; + SaveSurface = lpdds; + } + return res; } @@ -4647,7 +4678,7 @@ static HRESULT WINAPI extUnlock(int dxversion, Unlock4_Type pUnlock, LPDIRECTDRA // v2.02.92: found in Fifa 2000: lpRect is completely ignored, receiving bogus values like (-1, -1, -1, -1} // or {0, 0, 0, 0}, or {-109119151, -109119151, -109119151, -109119151}. // better use the Lock-ed rect - if(IsPrim) lprect = SaveLockedlpRect; + lprect = PopLockedRect(lpdds, lprect); } if((dxw.dwFlags6 & FIXPITCH) && !(IsPrim||IsBack) && (lpdds == SaveSurface)){ @@ -4739,7 +4770,7 @@ static HRESULT WINAPI extUnlockDir(int dxversion, Unlock4_Type pUnlock, LPDIRECT // v2.02.92: found in Fifa 2000: lpRect is completely ignored, receiving bogus values like (-1, -1, -1, -1} // or {0, 0, 0, 0}, or {-109119151, -109119151, -109119151, -109119151}. // better use the Lock-ed rect - if(IsPrim) lprect = SaveLockedlpRect; + lprect = PopLockedRect(lpdds, lprect); } if(IsTraceDDRAW){ diff --git a/dll/dxhook.cpp b/dll/dxhook.cpp index b402145..1b82994 100644 --- a/dll/dxhook.cpp +++ b/dll/dxhook.cpp @@ -689,7 +689,7 @@ void AdjustWindowFrame(HWND hwnd, DWORD width, DWORD height) (*pSetWindowLongA)(hwnd, GWL_EXSTYLE, exstyle); (*pShowWindow)(hwnd, SW_SHOWNORMAL); OutTraceDW("AdjustWindowFrame hwnd=%x, set style=%s extstyle=0\n", hwnd, (style == 0) ? "0" : "WS_OVERLAPPEDWINDOW"); - AdjustWindowPos(hwnd, width, height); + if (dxw.Windowize) AdjustWindowPos(hwnd, width, height); // fixing windows message handling procedure HookWindowProc(hwnd); diff --git a/dll/dxmapping.cpp b/dll/dxmapping.cpp index 116dc55..2b29b3f 100644 --- a/dll/dxmapping.cpp +++ b/dll/dxmapping.cpp @@ -485,6 +485,7 @@ void dxwCore::CalculateWindowPos(HWND hwnd, DWORD width, DWORD height, LPWINDOWP void dxwCore::AutoScale() { WINDOWPOS wp; + if(!dxw.Windowize) return; CalculateWindowPos(hWnd, dwScreenWidth, dwScreenHeight, &wp); OutTrace("AutoScale: new pos=(%d,%d) size=(%dx%d)\n", wp.x, wp.y, wp.cx, wp.cy); if(!(*pSetWindowPos)(hWnd, 0, wp.x, wp.y, wp.cx, wp.cy, 0)){ diff --git a/dll/dxwnd.cpp b/dll/dxwnd.cpp index 8e0f06b..a685fc6 100644 --- a/dll/dxwnd.cpp +++ b/dll/dxwnd.cpp @@ -27,7 +27,7 @@ along with this program. If not, see . #include "TlHelp32.h" -#define VERSION "2.03.93" +#define VERSION "2.03.94.fx2" #define DDTHREADLOCK 1 //#define LOCKTHREADS @@ -265,13 +265,15 @@ void InjectHook() int i; GetModuleFileName(0, name, MAX_PATH); name[MAX_PATH]=0; // terminator - for(i = 0; name[i]; i ++) name[i] = tolower(name[i]); + for(char *c = name; *c; c++) *c = tolower(*c); for(i = 0; pMapping[i].path[0]; i ++){ - if(pMapping[i].flags3 & HOOKENABLED){ - if(!strncmp(name, pMapping[i].path, strlen(name))){ - HookInit(&pMapping[i],NULL); - // beware: logging is possible only AFTER HookInit execution - OutTrace("InjectHook: task[%d]=\"%s\" hooked\n", i, pMapping[i].path); + if(pMapping[i].flags3 & HOOKENABLED){ + if(!strncmp(name, pMapping[i].path, strlen(name))){ + if ((pMapping[i].flags2 & STARTDEBUG) || (pMapping[i].flags7 & INJECTSUSPENDED)) { + HookInit(&pMapping[i],NULL); + // beware: logging is possible only AFTER HookInit execution + OutTrace("InjectHook: task[%d]=\"%s\" hooked\n", i, pMapping[i].path); + } break; } } diff --git a/dll/dxwnd.vs2008.suo b/dll/dxwnd.vs2008.suo index 3d2b464d46d41e59e9f8e735d479424c5bb906b9..04e538422ffc71161f59bc64c377c542a3fc1aae 100644 GIT binary patch literal 66560 zcmeI51-w<&*0&Gch?If?=g{3c$VeVI?nKF=>LC@ml@BrPH> zEG;VCPpaLYRzkYJw79gSw2ritw1)Hm>4DO+(rVI!qz6mONy|&qrJbZ1(u&f~(yG!b z((2MIX=7Dm~=^@gA()!W{(uUGT(k9Xg(st5j(&o|@(w5Rz($>;8(zepk z(hAb{(j%oEq#dP4NDr5Gk#?1KlXjQ(kRB@SDLqWuOWIpnSz1ZjN7`4~PugEPKw3{q z+ZZ8xv~-a480lc?5b03qFzIk9aU*3%*>5U4MmknH&TOLXB-zQ*Dbi!5Q>D|S)1@<{ zGo`bnv!%yLkC&bxog4vX)HO-k2miHL-*OvMHriYm#f)vW0z_O4b$X z-SzDTFXzampmtZ9a!O5{5EU3UJKRxcFN$7W|4VyM4JU+sZuY2&qT z$I4FE{+p({H}x-0xu%lJX*qMSu8@aRvEjQMa%f*jf2G$qu@5Gw9!%EO_erEbQ~4Q@ zXa&i*UHz@7?WYaWCbPT!^xL#~gnAGweL(n39%=vd1M~s3fBJ#A4M$wopZ+eiebS_VirfEqeWq`t4@lMiY1^Uwv!1y9 zFVuImfB1>A^Z7jFoLL*+FWm05Wz}arK5r03vq$o62WrTFPiRYjM_8VIy^CteAk~1` z>bWPSjnud^Qu}14bb?yt6n!^S zN53~fOH9}Hj7dbNPLprb)&DW})847$u-ys$Kg%H7r=O`jWGN5hRpyhGYsREheQ?!JBAEiAb zQM2e7b}dRjM_6BWx1aLroHMyjMj1xnY0^>J=5&onjD$0^4<_hSAF&zA4`q?AdS6#9 zV31-u=r2o7mkli-E$wWrbWdyG-^l@Wb%JX6DCK2qKEmh!Dfvm}lK$YoTRzmR5h^eC zXO_xqUx};IHmmzPY?~GB<^y#E>&$mb!_hYV)iDMDw zPCZp36I7GNCTjbzM6I5o9w5w3IclKK;wYlA{+^&Cjb>^Ko%FYJ;%^;|1kLna6CDFJ z)-g$a{i~y6i{`QoluSD+?r^@V(mbhcyvb0B; z_qS1xQgtu#w(I`qIJGNgq|7>YEql}dG5-i_+&=xDs+kj&D#sxM+!s#P(af&KfZFQ+ z@@QquOS9YmvCsQD1N8UPmF85n4SKw-BW2k8$rA3MJTVq!CH`*BO-rRuxkVa{TQD{s9#DRt#3UKnCyB-@A2$;i29Z+?d!_g=bV8UuNFY>HeP8m2O-r4 zN|nFJ_GwS@Jmbh0*&oT{)S=4tp3UMLhVz@BC;InNX$$jQm{TjVLwykdBE~IV_~LjjgNxhaHkV7o;f(eJeL_s=;M<8>?rkmqea=q@J%v)`@ib{|4KW%{a;C~WRfQ+y;Kj!X$HWt z()N9P|HK)@?Ni+LGv{Bzwr4l}@><10ucm{xp1yEeV&0vlJ-U4^woPyQPn>@uUCK1O z>Br+wVmh+@q-2h&S9ZFAIV!e2YjDZK<_0@CY>rc;u}{(9Fh?DJM|A<6l*{&-Ky}bz zYaMkMOr~1MYpidWZq?DJOnt|stC9XSO?=-_omU5GduhYOiH`<4Q@mXp-+AlWy78QY zVXQoIFE%ywaikH>4sbSMwDvgX3hE}#@Ngt})IM7K7^Txl+Dz+C7Gm-$&EUOQKj$~H zx1*A02Z!#{G)AgjPDspswr&YM{k|%x9jjaNy2qZbe_z#$u;;dJ?Vr#0S*s1+p;aHI zA(v6NwX|1`JMl4c{3YM9&Iq+O)d04jGdhK&2IlwL9z3tNI#8i zq&iHsHcJ}vlbm&Loo6Sbcl@jV_ixR=yQ+>cnlb|SQBEdlUdD(7nW|oScS8D{+vc2j zcDFz7e|sgyK5F`;gpQ6%zM}}&c2bGTKannJBdKD;cNuaxZbm}CG%Jz%M9sps*1)%> zb*20JYa)b?bd^#A>qz&eXNl|Ijx(EW``$ShU%@Hwzsi<$InF%`DEZI#I>o1-a}_C( zax9hfA;*8TKU!M)MVi$PDKqsaH)t;@1=QQ08LPPNfpGjpOhsvE#~jVWGNdfG{p>rt z?Oux}RNviadM?T58UHw)1d))q;Xl03p?UkM>OPyd28QRqaW}GXzP>Nh3C64*) zl~UyIjz6LQ3FBWpb9s8!sv4w0nn8|`lOcsMkEbpjC9NV2jFFMP>5zymmY*qI7O|D` zS#EX2*2wQEeKcZk%csoNn=yXXbf%ii;&RQgh1ndlx?*$9mYI>a8_n)BV>=#^PhDRd zv1iSmi`eUCZ zc#gl1CiXqF#e=px%g1%Lrb@rQrtLc(_a@I4ao%Q>^LXx?EB;L$$xkdHkBrMG*XTKm zN*r~%h?FsK`}t6+^2nw5h5ui-x2MXkkIsZnQ%}fw(d2%~qkR15=AD$#e|xWTMj?AO zTY<=q=V%glDQ3ij^uyPG4hUwMSt@sIo0^nXq)2O`G>t3f4XQ@#P`6rbrdTg{kGzG3VUt_fr5A7IP{D78}3s!|w|ymx1YMp};h zlh>od8MNkw@4AGT5Q~qTEzKhcKTv;??VnHdFpF^{H_75Qk=|@}r&-Ov*2zSd`9-Kf zY+D^EdygeqklNf*$~GP%g|TgvA=^p0hB)>GjJ_5|4eM^(!;CU(K(&x`ktOa*X;-u1 zW_+BeKNDp+Z%$l$=?K1%EHtBToMit(+xlXcIPOL>mR}*CI&zO0%dV2o9C&ra9y5D9 zV$YksV0Ma@xk*0qmRV2vZ_8&}--+0Ivv(u*p4s~m`#?VOky&r~ADew;HdO3uvmec9 z^*_m1$B_1O#5S32j@U0|zeemg`N$t;)QdmO@~eVScGTel_B~nuo3ntOi~Wy;^7QzcnETc}pNG=pZoRSeMJ4br1l+i62wPnkN z`W%)CY;WumuV5GcS-M-G9S5DdY|-u>A$28 zNLNZ%NmolBls+VVL>ltAR{o>X$D|)fpOCUWPuV_W`6_ATrEf`_X?vLate3tkeNXznlyp9n{Yd(;^b_f)($A!yOTUnQDg8?NwRD4Yqx2go z%YG;Oz4Qm^kJ6u{beo%`o29=Fs%lDO#|3r3|wrMQ$5SRNq@A~Gj_Xq z@1sY~quK3{dJ(g#vPB7yFn?2ZN}NM3En7``fSJ1c#9Y3(Y#Fl>vIk4+NGq7Jjw-A~ zQePIAd3yu1L(JNUwKF@yOen37*$}hJVnfZwnROByZx+tevTQg{yU=ljaQ>CKcR2qF zTdzNyM|@9~&-JAz;Yf0*tH(WBno~YfE@I`)T9}Q{_btsjm<VI0kT*-7{ z%uPuv%u7ef-$;;Tf5%Z@ipxjpnPthZZ`RJNxmbI%E>T=pv;Jna6gR+ZxY;9k~8RfzaV^d_mG^0%%m^=AwU>R9rPLaOpWqX(% zD%Mjza+BEz`8S(=YF1C|OS50iSmrmg-_4qd{b5!@JA!mM<}S%jk)%f~!>oo`U438E zth*Wet%unNGx9RhY^GT|u~}wUm^Bu=((Dei%f#+9d&cY#v1iRbFdHcLp;_6y$?fPO zc949euUR+w{mjmZ;?6aD!t8LxJ!Q7hjOw^qKK1z*Gm^}eFL@rJf-L0+t0W&=$Lu`$ z-*|m3%od2Xl#lc^V^_?Q-&OWFvkvn6JMLPu?qb)Otu!O;m*i6xFPn9gzshm1nY9;t z-E7atzir2#?2dorEeP$im;h{p^K^{-jm}fv{K@Y!#q!BV+L~P={}8j@W`o5JHydKs zQf#Q%B(tN$CY#MQYbrL+>}<2)V&|A$Wi~+UYO}k|==1J1d&R7a*sErF3M99qlUQE) zNHa6qSabRNYW&^V%)U^;4OBZH?cybcc1GEw!~f>^pX&T`gTy(^WSYDByEpegKCYF~ zPb10eh8$UO&HuQxbY1Zprh8$A>0Bplcf$GiiVC7_GoRmGJl`{K4sofspWZdKkUr@K zNgbX1_TMtg9GE(NW*@0#Z_58^tumBi^!{^BvFs!}gOVPXUe9Sx%m~*g6M) zFvt2tzfWqh6w-$rI`-ewTVS_6{s`%ZvkRk@hplPXKI5$lq!~&eq|dCI{8ZkH^51#- zq|2Rf@qV9Fas8>J=+t+5@9mzS|J!-`JKh&trA6ttqSSE{XNPF%Z2SJw-1}_%cXsCH zvMV}gDUn2KSmX#PyM!EYypk?uT&g4utfhR8YdB5`>^k|G(j^i5m;5Tyl@WVLeoyHm z5u?ALC*a%z>m&W@IFhEaxQv6GV?d5KV}5sn*%CA6=u6G+F=IO(mOo8)O~jrydnRJ9 zn!RSWuiF2##9a>5!QskB@(l0?KfL~^vXt09jwv~pQcn7#SsvNs8$t>vyyQ`)GM0Gr zMj5lPXW(o-B_8$+v+>GOguO@%ZHpr~{9(4#IecWel<$}okB}ncr7(^lCP^YGmX0w#=iy%qW*2)OYLge=k-M>rBvfz!^B#5wU#6&ef9)tBdMd4 z-@Ygl#=q3*Gt*5ido%vOq*adbYE!4rEHkx`KIZ}&OS8NEtyC3g$H*8T(@c{E4UtYa zJJD>A*nG27%&1+bnsMyKvb3P{#+G zJ51l-V76R#UyZ-ae|CK0U`m%}S1Q58)?tIthp@#cZH}XVdi=oXt?8v{^MNsPu$THp zjH8l*(xGPbXw=N!($J^CXf30qw0YLoS6WSqoFWZrTrU3rDSZa4xO9;;r*xS##N8sF zy1m?te&J>*ad(>$OG~B4rNxA0^U8r0l!awVxf_z%Swpj2@)?c#%Z587v*e#9MW#9q zHr+ng#9f2;;K`oh`)K~f@fS}Z^5D@}9e<3DW?^|ttgT6|mBDR7VQeR&!rlw(gCjMhFiJO(pll;EnMCcu($^AKMH+hOc4jh(Yx7vz6mitVj#8wT zG^9%{OV$50%I5t~>@{k9cKe?W%0PLlizIdWj4Y`|{oh}Ge|FQS#@<&kx!%<2)3c@) z`d{`Dttq?dzoOWmsXUU@>9gk4vNz+;Q7Zp8T?ul?=9K1=a$gPiv(UoxN%Kn!NNMSX zq=ltLq(!B)YR&=eFD))DAuTB_B`q!G7@K=y%1F!l^TD#^q~)a*r0LQOX+>!zX=N$r z|Eo%?Ntxr;kkY%ZhGZWcx!`9cQe{7d0~CD zU4-^(H_>ikv|Si^rVS&sTU_Q-g`{CzC?=mefbAnCrn{6m;DOR(q_8~F!P27AP*=zs z^&+oyl$5w4(y_MV%vhG$3~}Sl$OCl+M*Uz}#yRQ;Wu>Q@G0Qnanl3$4 z8sg59&$84R7aC7mR5SN&{PC_J|o}v{H(M`a^lVB&{TU zS<12{r7uX?wpXMu^76hkL;8^vM!9?}CC{HpVTGmdN%xn2E)8*C$gd#%+KjwV_edku zGuF3JT1EPulx0cxC+R`bpQT}ao8+^9H=EU#r9QI0P#tpPI5y#lkL6)%tMp-qI(?zz48OtzU@I7-DEbC)zz-KJjdbcbeo|m%sUX=0~ zr6gcst25=3%iOXd^<46IHeWtP9HED2U0G6kdHg%|hhAOop1g2qyJ!3N=K0M%c`|(F zZ!hIn4ih=X25BsZtw8B}CQ5tzb3WPTQe>o*n2J)i9+@D8Wk^{L3GJTtOuI)ekP=7R zxloE+A%#(Iu9Rl)DU;pbnU(7NRdO$pYAneTfYNrDN8mGmz*6^^L*O%a2#omyKJx}F zb(whr{mwWu>hgFq<_r3tXxpRgGk1U?%pY>fGS7g~2QY`gpKVLuc$NLDZ3~LAPlyYQ zKB#~cS)?yqI}+g86wozN9dq0UwK6O6WpOy7zl57>1HSOs$$JIAG zS}fJ^M`(iz+oh!Y_$yrzVSn#3*k8<%9cMpP`b~GYB7Lv)bI?{u8_C8ZiL<{iyDUDQ2vW*PENmcO6u zDQ5lUXURuCGGomD*#0-NE{oK!vC>$`YFbKf)kLS7>}|4v-Ol7ka)%ka>`t=>%;|)!yE=F6QB-cjlQTfQ@X82E-J!3}Mydt0Nc-4$C`B}$pGRqX( zEWeKIFA@9I?6-*hZuUpS{xsty@ubQ65(nKfIZGrNbNF2Hk%P@T$}cCMZ7UzK3TEjM z%P^}Lu}Wr@BUVK|Qr(RF)RkXRwt?AT`86EZ%&e(cbNP&$Eh5&^tX0HXo3)8pTeCwV z)=oZ>X+~bU$tV5pX3Ud1IPPdO=FfxVlb2&6HrQ-P#Dqe}eS^bDL zFl%UbmX>KG|7zLB5o;nJX=X;+-Q;(Z?QS+resjkSG~@XADEaL3qa!xR?3joRHX9PL zp=QG(He5b3(u}sBWp=C?^Ma}J+sjUi*mTFuG-GadhWtLVXPX@+f0pAGo6(MLkY7`F zNyL_#EsNNVW;aFbX0ux&wp>1Po7t)IZ#P?GHcadh`JH6fM(k0u$0GK)*%J|a((I{- zJuM%3){JzYGkZIVdq+O$u8-KeX75GpeX|cD_MzEF5&Kv^@~Ii^=m+`fvOk%vmj9XK z_S1nk_3RXBG5O3Z_m5aOZuxK983vd;iT@>iK)O=8O1fJ5p!6Z>!_pU}e`mY7=JJx(!Lj5k(pROgNne+~ zA$?Q2vuitlC(V7mY;o6#5>cHfX;vy?rOghA*n#qqvS!-cv~>ByWGkC7zs{6jL$;&Y z1@aG)kMuTU+&f%;SJ@*X*2k=G#QK@_kJtdSBO^9YK613#E%FDMjWuIVHctMLvg0E* z!E9p0CYeo+*c7v4BQ{k>n@AnqfA_jIz2`{#e=T%xKF?R*`*036q~9(u>H1O2J)GoHZ&v8jm#RGv2AtaBTb{YX7U;1o12kFEBOOt+eC3~E$s$C#0qW98S9 zooYs2rkPDQBQF!>BQvA8S@Jnnn{CGWPLy9yc77DcGgPpJW~}c7`N+vp+$r*z3!ECo zT_&I7*2|-~E95ixxzdbnyHq~gc9ogjwCm-Um%Y(U)i&(~`E2jHh`k}dn%KY1SmqP^ z-$d+N`@h<7zeL1J+Rw0MeHG=Cm#Sv0ubNpcGs^EH zrH9ms;_Aw$jkhpkd%MZ+C)?eOvg#+FHq+mXcG=thkx|@W#|??%2HGEHOP+_zuOvIl zj69Dv8*fIsQ{^)!on}TkPqaTfiks)S6Qj7}?4N8)x~IseoX;>L-80S3HKVK!laHJq z#a$quzUfLc@^_>B_Od)v$9^j-z1IG6Tl$w3_V2Z=D0aX7)wY!V?eaO+2~2L<6Jq4^ zdB?#%Hv2SUJi|xczcwR{@9gs~1oD?I-DIEV`(UK|n0zEW>&JSEDACcf`^kp59P(*1 z#mp$v1LW6{JVd@QFwN63~p<22fJs;Bgcm7=)H@;i!EH6z_R z^63-nMsfAb>YI@+&mbZVqqs)$j}dDe#dVZFU#wFU*I7P&Viz;^c?bEFL02>Ka=3i@ zh$GBo()yV7H6!jw`Sfc8&1BM!GCSIgJon@a$uVYp9IQW6Wrvy(H(5S?-4rvKv}5&$ zzHX`+d7dPnJWn%|n|8eXDzfv~S-;_Y}U6JZZ+qr}U?(>^d{n_ksK>vOMQWUS!ff zGW*z!^{wX%$){#~{7iqCM}2Na+(!9xWxp|#N&8lRIM@E28Rfh|KIQzqSu*wdBH<~) z%kw8$8?h_oBiETx)2=tW$&9_S-0T4}YW-@nXUr}Vd)92d*>tgY<GF{?%}UBY%l_@Q%wg|z+-kF9 z#2z$zDq>HYy=QiT;@&q4^Q6II%#+yO?;HpF-t32n{U{&#*^F&r9>o}vQ)3vP$4NIi zu7DZaSx`RLISWOsuvw9a6_t+^Gh==Gn{h8OX|TQu@>ySc#F&@Cn3ILLO7f8^X07Ej zS3~NXQC}LEbuc?gtfN_&cd<_9Q|)DsavZIEfa4~ZjTB>^gv>M>D1VmO0<%-a7Mfjd zM*X|O?3#!zGFu$68_bvok+(Tg-Zg-PxlRrF_nAHFxD&;~oZ}@k;$C)Kn76>bH!JQw zm1Vq%&00mQwOJVFiDR5cdPQ-)&B9oHn7(JMMkYHBHpOhVS*F-= zW(y*=(CkdJ&WbzB>^ie9V%MA98?pP$!g~$cDK5O%;C08r-f&zP_h5{B2xA!gsGXE? z4EfcJ5#l$qeCjKSW83n}M~a&fSHi4j6j#fvomn@3!BW?U;L4dT|AJ#NM^ zGVggnUNAdK{)_Tk%C0lx{+5><$2E1uny99<+qc4FJkXI?n5)m{v)%m z%t-HR`PA195##zh@~v5A`QMp^cTW@*`_&b-12!55elUVYpYo3Fq@64_osPx&U3exPR&pFw!{#5thG|+1icPBi5 zQC30pb=ggSsbU9tf2K~KIb~|uo9AC>Kb)gRFkPgNaKI^fR2GJQDZi8K2D78&Kjyd} z&1#GNBp>^8#5S32j@U0|zeemgv)?23hkRs<8GU!+CNJ3{n$VLs#^Iv!`^)YZv0`TX zN36J6iHManD;2TQ@{t40n#(U^R?&>KtI21sQPXUK{7Q}+V8&6yk!C~9&J`PGc7vHv z+7h#6W{mYW%5N`wQ^amIyCq`F&2Ba8uVrqN&zOIE#8${h?lfy9|1Pr^%oxL8G<(~O zJilZ1h1p!OFU@{7J6mj%S?&Xp`>4NI9{EVgh?O#{Y&KTkS21fAvF2vo&6t1oFdG@M zEVJom?4#N8kCZ*m>^}K39Jk7hx&LbUr^-GUv4_kaHlq(*BY&vuBN1CGA9>8|boq~) zJ#R)|_k#ScvM)w#o!Ltfds#m6su{-(ubF*d_Nds0W?!0-?pN|h$bKEM4Q3l7_Kn%M zW_y1B^UmMj5Wl@OyuV}VXEo+2ONsW14Z`^(_7VN&{!)6`1EsJ$(lS!265qpUYh1fS z9+kEG*E`f-Fk^pnO%Hj;tcm>fW}ll?75l=Bdj)YzNWYU)P4;^;^74cH;<7)QvA#|6 zk>AXi2mNlAp#dDXuCyY9xumKYel@e&X1T=r%BMZ{Go#O~{C2X-%&N=3T0ZiySyTCI%y>^5%cV=dG0U%vh4mGZPk$DU)hR#T^M-_LIQ8To zWH#2zk_N}c2-g@`UwtXZ#Yi~rW$fTu0dkS!GUQ)u#`*uS>|(POW(*Z~m~pI29QEfR zvuDj1pPw@e*9VxBy=C^9;(kI;@yNZiB z_xV5f7hnY5Bpoi@EG1(wo*Eq?%M+t8&M!Y9Mam11V)9e30?7!@750{oj5ecx8Dn;w zStqgM&CZM1`DRPa81t5yJ!-}r^)a&-BKD%$zs;z*Z<&1>vCqtYFjH9Ck7j?EG0*(d zteolzTTdM;?>bY@jCx<+tfv{{32iirVjG+g9ud`N#;fmhwlM9dCA}*a>Fy%`OvLV0Nlmcd^sV z&W_kQW*3@q9C4A^9cBx}?lgPStcTcBX78E}6MN6>C$oFRem48vj6UTLGk&6*{dSBr zw|u0a*(LG|nPr%b6su^~+>92{!mO7WJyLJ8QD*e_qs``;)fSs)cClG4u}jSO`2_Mu z4|A*8TC>?=kDC44Y>3!fX5X5zf4{T;tLg68kz^W;#PY~Tipg$mxyfrM+%t!S z=Vus8)AeVPY$aK~W(@koadpi&zgCZ6NhdSvO=q*AW_%p4KNDpqn6;Nbj4vef%}UB= zK1(v^+fqK^o+ReAe23m9Key~%W-R-F{Asc)BevG;(TF{37T$+^ik4xnjJ)Z%p7Q@K zzlZGJ{-y})`@rj?j(q4ezAzgq_NCePX6&mU-lU^ zv3&Lm+7g0~FyCic;=-JNzbFnLDP~5W60XTrH(MlD!*Pe0v2WU$9brbB?PE67tgqNG z`HUyoxhAKuzwU)%zRmFm67F$2RzAlc$O5O)NB%CF*-Zf$wv;3)>qE!%g!}9maXJvTg$KP_0=__zSJ{oZ+4Pc zrdeMz^24>Z_Oju=r|I(hIc}WeMv8GC6B3Sp2Fee|KjA*5Q^n>x?ouyHoxIHKs)$`} zc3s4-H(P2(8(e0_y+q^}#{Eag(-C{d?30LbKM@kHzcJSi*WbeZLNM+VLh@)}APrbv z1_{Z25i4d^#*8tetXaK?)i-NymZ`WFX6+-EX%^lg-dSmJA@;-Z zD1ALVuzdY)-PQ0a;Y53>I$=R z@_8c|aJYPZ$Fu%FFx zt8mEgLDD=Fl%%v7J^uk_t;~)WYi*Wk#xfnuI+|4!>tuGA8QaF)YV^GQ&8Y7K%`1^H9VnC+5h>KZd%B-~}jdM*=dD|@Bm=mnYaBDa{8mA_U#z3`)EZ_ZhJguBg%W46Q{XGrnfNyd>GXWo%; z#+)|DU0JM`JF-}oJcqOB%<5poaTgVFVdloJDFz8TRxq zQimqL&|iMzDOr;7`>VG7{g>3|pE>K3S~&k)RUy>&?C!Or6nks2 zzk753`&m}T&XS}~pMEK|kbX^tFc;45_A}3&Z5p`JH4JMU3BAf?XG}>&+HN>;|(X5nC!BxzP;&CbL`2D6dlawSBUsW%+!v zbh+dDm@%{ME5EaBzlilW8xXN0%?3v7D6^v@Hb_1aekZB7{9$I{se_?nJas_2&v{w$ z_q_ZgWnYNci)QO0#vA*HdpTnKJ`(bp8QaVg2pwg4s({ZN%f0S6o)&-|D*a7ySgn=@j$tbQ)4+(0ksw`Zi~QO0k;l!*^E&whWM48P-6tGZq)>8u8K)}9A1Rw|#HZTB_1P5z;dn`d^r*okI~%}C>B`SoOPF(Zu|9QRoi_pSUY z`i|dhW;t{}O*(4G7L(;O+xEGaO*eygbX+I1Tg12?hV(OI+5ToT%$PKuET6h`x*2|f zBFS^k`^mEY>e5+Wwz1h%u_p45m2DcaW@gPJ*21i%*)%QF%B*$7+Q>)RnUR-X^68%k zMr@e;L&b)hb&)?(KJ{f(6nDCOw($ZpmdTWlTx~}FuaQq5uqa~Jnq3#M>&+HN>;|(X z5nC!BxzUXC^Ea8TjMyr(XU#5A+;e8@%&ri7$!w$9)neb64KAA8M;svzk&nzWyGH(O zvp>x^54XiE*M7-mSthr9B#+sA`FYLCn;j=s!7S5ku2=`Nu4dGkx$-%~G|%jC`KQaL zEY2`v|6VEo0NJa|=<}5Eq>=mk)gy?9GV%TR!r(Szr0<%|11|T`bvE<N9+Q#3nO-s*~JmNL_Ttv8T(|Z{BE+#%ofPM+;MlC^%uKGeoNVV zBX*zJ{So_@*#i-ytiMx3Vm|P_Ea}32pm9llGCN29&t|y~Ovd#V%Ok&sY~G0FGs_>b z0%ipxR>-Vy#EQsA_A{$3znEDm+2k?S5{)Y4w1af1eB@rUI`Z!`d%~=i*pp_jn6(#s z)$DyUw&(-14Q9i|Hk##hPufW=mwVED5zB8@*o;x6l6-onhGz6+MI0AKyAooIc5KrW z+x_KFvww+gO|i@6BVmN8BcBllx!G~}x0r>IgT1u<$Z?L2+xgAEnNog%Z;IxCGu8hN z(eW(zdAHC!XJ@~b7=A5p>n|0O^LeU8T#HKnrsrtg<2OuKmC|(_GFEHnIiEpkJ>`$k zHd?(OZl*lQVJytxU}!JtL@E29&-tXy zr3kY>SVbwl0Wv`f%aF1hLeB_eU!EmJE|9|5mlsMAMrIhJ(v?zVkrb98y;h1a8-taV zE|(%Jq_C3GJEX`xQW*NMl%u}2{+wI(RcQ<9>k)fLK5ggyMA_|&>xVP6FZjLIM%pi1 zYej*~@`*VEd5h(2eSlF`=SVZ97fE61(!O3GOBT13bd=d-Ge)W@W+$5oCEgN@J>9IT z{4*SPo*C(0AitVyI4eh6EO~Wy2Y`>%FW{+G6?0U1ofIQGXa^Uoy)npQo&t zUA$o?llH#;&|80C#`kygh2#%2mgUZ?X0laf`I>&?Psfck<0IErhRcR)E0jx?7{av^ zV)%HT{;<9a%-Auv%I_+>!fdqsQ{*GzE-L(0W{)_Ivf?@lqth#9)TgzMd*6(i9Ctt= zxzq`g7upclPmoe(v_-C;AmNDuSa_nKg5%B*OLyEfvyNiZ&B7D+u<*qFT*r|Oo+O~` zmqm;_c$j5|C+W%K8nZ_nN4d?j1@ypbsYWHY4V54o*uC? z9Cwx(>$_Nf1KCT==tItS-1Sl1t&Y1bisL#8veK-n{8eV}nUMx}whWN{%uH??*Hjqi za>}x82zRtlKHSX$V}0*?ec{d)cu9TFSkE;UKBLv;SC_3BF`hQ3uWxTQP=2^8hNsS1 zu8Opad?dW_g?#bG7vwa@HIje2*#&0x#4a@B$p_X)9B*bpmPK(ln%!YWEKfwxmxQMv z@b7dSPm>daylM8f8SU{i`Sf9*n-!7I+f|U?&G7#)+hWE#YvoP$GvQ7Y_7T@~=%d=m z5(8@|pZc6>MwRD@Rwio4M2x2?*dIJK!Llg740(v`g=YK9FDf5dWL8!FwPrV&QSWcI z&s*487jl>5!c*j|Z;jb{$HBfb@u_4&1jeR%kL!nFSDcMuW;P2W|hT$Gb>Rrxoy;g zlJb!Q&Db`cc)+$bTPVMbbTWr zl-q-5&zi9v&zXH}CY1Jx*{5bli+yGmo{*R$wqGHlRi|ai2XU>VQ zv#ZU9id|#I*<98~xo}1o`Ii}djr@+XkC>6}1CD#qjJ{@_+1C-x|pwA z+aGK;En?Hn!dYg8rJZ4RmgC6J`SPhF7n-rYvmJM(8GYteX8)W&*jMKdww`(ZQWFjK z1!e!0e8#!2Bep?4!Wn4dPL_Uaw#n>NvCU>%%nlOEt%DWXSRPrHT`Uc^OXf4HEtFqA zQpAj9_m|JI#m&aaFY36GX0^mhnUysooecT8WviH-A^#x9)i$Hv*D-4nv8HBi&9W4C zh*>|gJYxOLjxnQO8)|l}8ROAZvpHt1#O9h^YQ`D<%gydI>n3)W*)wMJ5zpFx*LJwr zdyf0qjCsT-W*f~Y%Wup!o3S0g*e|3r#+2PuX;Jw|C9~=BE1R`2Beu1C&bPEN;~Ys# z$90g+?wu7{>>t^~5}^DurT2Qd-(+FvpHk+0Tl|@M^zTw6w*rWP<+0B_vwOC$rsPbn zg$i}7e0p)tXs~8#6=yYI$D7ePG?kCciQ?wUXM{Y}teE^wtV9ydcrdzghbj0PTi7D| zJOjpeBw8Z6R_2VG`ks+b%Xz_!^>L@_o}d5R`TI9` z=f(`3mmI6}k;ykHg|~1#n1iFBqhznW zNICgcxc`kKtn8*gUg^Kg&Xc50pE+Y{QTmB=NgGKWo%}X&CR=TPUr93M(9)3UX`+6Uys;3@;gZ1H_MdWw((K4Ejd>=^~M(8Lg_z~koL zTuIJefhBiVSzW8jQadJ{sj7`zuPaBpbPxB_zN^)V+gA5tKC<&&J1fz?CTec6bv{C$ zEZJsH9h@)OGxXIL<>VuE&FCe-?0?`Cv5l8Clvfd zo1Ul>u|3F)dXg?JCC!jhcPOihQtCz(DQrJ!9VvB;quLNhzr^`~`ev+;nHIK@8S85- zC9a8CAz9iL=`w?3eF*LE0BJ{QVBO_Yj}A2}E!$H{{sxX0PAZm z9VhKA4Kq`gJylAXoF*lXnJ)co0qGgiz|J;1&y4S9N=fT{vof;GuH$2efBgS@B|x3I zz;!|cDBBQW}yR&o+;(2Q+sA)hjv zZpLul|C+A-y;hghtC~w^halzk>?BLljn;g#vF&dFu&3Ngz#)B<+s?3beG7l zE4$Q;_3@jWHDqrxqYQ4AUr9E+*Mhnc-db?0*TanRJ5+uZ+1_Ss$KhuE%&5-;?2m|8xPx$t8S6V%K4a(qrStEUU$}#jeSVS3 z0C~;jLYclUKU4Of-@nwIUus~~UFu60`3+>dno(BW%(|OVrXA!XhemP#{QjkmH&q_p zR4(C-8}z%EishBP%4H?@pWnY!8FbSzV?WvMK6a$6`pIYB%^N?cll0&J{QjlJX_ZHN zmBr027si=u?Ju`ISnQwQztj^5bXt;wWvN4aET=z5$o}*Dm%5VTa-kpp=l3r)-&A=p zr+Pdpms8{;Pnz-ZDg9|GyUvX9@&ox*WIr^M`LF!`CC3oqx6s1dfP@n7%I_%~-zCoP zj*-uTva}ESjY{@;kA7g>Iga1ZmhI&(@(kIgW~`@~SxYneZ0;~;+0GH`V*hYk)^~*b zO0xVm8S$*Izu7=D>S>02l-5MvzZbx|SD10vJ9#F5 zbLAuVm=Slce75&lGroU8KHt3*u{X@#iP&bd-^|DhzdOeI!rk+%hr8!t;qH0zl1mIJ zEE~$Si2S}{Ma}r0YZa^`-HbH2lb&)6*DPSXYX;WHaRuaa=RM1YyX;|H-+=KR8q%eW zRhECP%nmhUeY|&u^&J+mUS@sFNP~CWkY2cJpY`<ZyEWa1=L0KIb9B9shiPf*9+V=Q!BOW~W7r_sv$kgBxt4sST@=?|{z$RT zW_;gGKI=HtjP}^melOdaV&R=c;XOp0|31p!^G=~qrdjfv>AO*8tb4TC7&G!WOg=I$ ziW@I~jMxM-zMn6jc5{@^mR>hgJ#gLCcIm~kHI%Iz5EzHjjN%WSFRC(1|e zG{e7Fenr_;W*jfn(oW_$zL_lB%e*-JT5$_A;<`AlPZZbEem`6GWq|bfi`mT~sSzT+!`mQruVn+ELEFa;wF<2jQH_B(e@u(Tw7T)>u zycy%nI{WY1a$btx&S)ze7v&9)sVt!n=eTh5(MPQ@lbiO2{GPIJo3RYP71B-iJu~*lhw?dp z8lIA&489SoCi|`9D1-0JzBeQ8XZclRH$`!q&3=jE{*cf5{*2W<$zPJBV#D2E_i!Iy1Z?;gTf0&WRpJq9f3Gzo?z7<1qnGwftyHKXQafCSX z65hL2JYps6m$oI{1LTu#Su@smkXd;%nZMprk{-on$S2(@X4I>i_UqaTr8SV>L^izl z3&uOX7-zy;PiRNsy<6kGENrsx!5v)9bX-x~SI8&TYw@|kD9Z&phFXY#RML~&orXRh>>8QZ}VS?rq) zw$y_k>~FFaO8Zql`{Q>r%DI|W#;sp%t*J7eERmWQQSEBl<8D6%JMk* z*b}0-Ir8;C@vb?xjWV6(?@zR)Tu!kc-a7|7)9hSZ@_e3r%H={c@_domrDo*$B>Cj| zvMBCy`OG(XcO7{ljqt9zn<93z{o8Cw_jdWDd#4%c-eq>L8R;&TPrCO-aret7-PL9? zX|H?U^pKfQ+9UFN%06z!yy;2#-DRJN;$Czd@6lsB5Z;T&vhSI(A3l(ed~8-+KJUJ3 zAj^C2Se9%s#>9;6;N5rhYrOZ4IJS4A{JgTf?=E>SW0L>V{{LzJ)VF`yf9m%C$N#-s L-u~I*s098WV^SE9 literal 83968 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 diff --git a/dll/hd3d7.cpp b/dll/hd3d7.cpp index 2bf5107..f529574 100644 --- a/dll/hd3d7.cpp +++ b/dll/hd3d7.cpp @@ -12,6 +12,8 @@ //#undef OutTraceD3D //#define OutTraceD3D OutTrace +extern LPDIRECTDRAW lpPrimaryDD; + // exported API typedef HRESULT (WINAPI *Direct3DCreateDevice_Type)(GUID FAR *, LPDIRECT3D, LPDIRECTDRAWSURFACE, LPDIRECT3D *, LPUNKNOWN); @@ -542,8 +544,8 @@ void HookDirect3DDevice(void **lpd3ddev, int d3dversion) case 7: //SetHook((void *)(**(DWORD **)lpd3ddev + 0), extQueryInterfaceD3D, (void **)&pQueryInterfaceD3D, "QueryInterface(D3D)"); SetHook((void *)(**(DWORD **)lpd3ddev + 8), extReleaseD3D7, (void **)&pReleaseD3D7, "ReleaseD3D(7)"); - //SetHook((void *)(**(DWORD **)lpd3ddev + 20), extBeginScene7, (void **)&pBeginScene7, "BeginScene(7)"); - //SetHook((void *)(**(DWORD **)lpd3ddev + 24), extEndScene7, (void **)&pEndScene7, "EndScene(7)"); + SetHook((void *)(**(DWORD **)lpd3ddev + 20), extBeginScene7, (void **)&pBeginScene7, "BeginScene(7)"); + SetHook((void *)(**(DWORD **)lpd3ddev + 24), extEndScene7, (void **)&pEndScene7, "EndScene(7)"); //SetHook((void *)(**(DWORD **)lpd3ddev + 52), extSetViewport7, (void **)&pSetViewport7, "SetViewport(7)"); //SetHook((void *)(**(DWORD **)lpd3ddev + 60), extGetViewport7, (void **)&pGetViewport7, "GetViewport(7)"); SetHook((void *)(**(DWORD **)lpd3ddev + 80), extSetRenderState7, (void **)&pSetRenderState7, "SetRenderState(7)"); @@ -1188,15 +1190,48 @@ HRESULT WINAPI extSetRenderState7(void *d3dd, D3DRENDERSTATETYPE State, DWORD Va return extSetRenderState(pSetRenderState7, 7, d3dd, State, Value); } +static HRESULT WINAPI dxwRestoreCallback(LPDIRECTDRAWSURFACE lpDDSurface, LPDDSURFACEDESC lpDDSurfaceDesc, LPVOID lpContext) +{ + HRESULT res; + OutTrace("dxwRestoreCallback: ANALYZING lpdds=%x\n", lpDDSurface); + if(lpDDSurface->IsLost()){ + if(res=lpDDSurface->Restore()){ + OutTrace("dxwRestoreCallback: RESTORE FAILED lpdds=%x err=%x(%s)\n", lpDDSurface, res, ExplainDDError(res)); + return DDENUMRET_CANCEL; + } + OutTrace("dxwRestoreCallback: RESTORED lpdds=%x\n", lpDDSurface); + } + return DDENUMRET_OK; +} + HRESULT WINAPI extBeginScene1(void *d3dd) { HRESULT res; OutTraceD3D("BeginScene(1): d3dd=%x\n", d3dd); res=(*pBeginScene1)(d3dd); + if(res == DDERR_SURFACELOST){ + OutTraceDW("BeginScene: recovering from DDERR_SURFACELOST\n"); + lpPrimaryDD->EnumSurfaces(DDENUMSURFACES_DOESEXIST|DDENUMSURFACES_ALL, NULL, NULL, (LPDDENUMSURFACESCALLBACK)dxwRestoreCallback); + res=(*pBeginScene1)(d3dd); + } if(res) OutTraceE("BeginScene(1): res=%x(%s)\n", res, ExplainDDError(res)); return res; } +static HRESULT WINAPI dxwRestoreCallback2(LPDIRECTDRAWSURFACE4 lpDDSurface, LPDDSURFACEDESC2 lpDDSurfaceDesc, LPVOID lpContext) +{ + HRESULT res; + OutTrace("dxwRestoreCallback2: ANALYZING lpdds=%x\n", lpDDSurface); + if(lpDDSurface->IsLost()){ + if(res=lpDDSurface->Restore()){ + OutTrace("dxwRestoreCallback2: RESTORE FAILED lpdds=%x err=%x(%s)\n", lpDDSurface, res, ExplainDDError(res)); + return DDENUMRET_CANCEL; + } + OutTrace("dxwRestoreCallback2: RESTORED lpdds=%x\n", lpDDSurface); + } + return DDENUMRET_OK; +} + HRESULT WINAPI extBeginScene2(void *d3dd) { HRESULT res; @@ -1220,6 +1255,11 @@ HRESULT WINAPI extBeginScene2(void *d3dd) } } res=(*pBeginScene2)(d3dd); + if(res == DDERR_SURFACELOST){ + OutTraceDW("BeginScene: recovering from DDERR_SURFACELOST\n"); + lpPrimaryDD->EnumSurfaces(DDENUMSURFACES_DOESEXIST|DDENUMSURFACES_ALL, NULL, NULL, (LPDDENUMSURFACESCALLBACK)dxwRestoreCallback2); + res=(*pBeginScene2)(d3dd); + } if(res) OutTraceE("BeginScene(2): res=%x(%s)\n", res, ExplainDDError(res)); return res; } @@ -1248,10 +1288,29 @@ HRESULT WINAPI extBeginScene3(void *d3dd) } } res=(*pBeginScene3)(d3dd); + if(res == DDERR_SURFACELOST){ + OutTraceDW("BeginScene: recovering from DDERR_SURFACELOST\n"); + lpPrimaryDD->EnumSurfaces(DDENUMSURFACES_DOESEXIST|DDENUMSURFACES_ALL, NULL, NULL, (LPDDENUMSURFACESCALLBACK)dxwRestoreCallback2); + res=(*pBeginScene3)(d3dd); + } if(res) OutTraceE("BeginScene(3): res=%x(%s)\n", res, ExplainDDError(res)); return res; } +static HRESULT WINAPI dxwRestoreCallback7(LPDIRECTDRAWSURFACE7 lpDDSurface, LPDDSURFACEDESC2 lpDDSurfaceDesc, LPVOID lpContext) +{ + HRESULT res; + OutTrace("dxwRestoreCallback7: ANALYZING lpdds=%x\n", lpDDSurface); + if(lpDDSurface->IsLost()){ + if(res=lpDDSurface->Restore()){ + OutTrace("dxwRestoreCallback7: RESTORE FAILED lpdds=%x err=%x(%s)\n", lpDDSurface, res, ExplainDDError(res)); + return DDENUMRET_CANCEL; + } + OutTrace("dxwRestoreCallback7: RESTORED lpdds=%x\n", lpDDSurface); + } + return DDENUMRET_OK; +} + HRESULT WINAPI extBeginScene7(void *d3dd) { HRESULT res; @@ -1260,6 +1319,11 @@ HRESULT WINAPI extBeginScene7(void *d3dd) // there is no Clear method for Viewport object in D3D7 !!! res=(*pBeginScene7)(d3dd); + if(res == DDERR_SURFACELOST){ + OutTraceDW("BeginScene: recovering from DDERR_SURFACELOST\n"); + lpPrimaryDD->EnumSurfaces(DDENUMSURFACES_DOESEXIST|DDENUMSURFACES_ALL, NULL, NULL, (LPDDENUMSURFACESCALLBACK)dxwRestoreCallback7); + res=(*pBeginScene7)(d3dd); + } if(res) OutTraceE("BeginScene(7): res=%x(%s)\n", res, ExplainDDError(res)); return res; } diff --git a/dll/logall.h b/dll/logall.h index 1ea6b23..9e8d71e 100644 --- a/dll/logall.h +++ b/dll/logall.h @@ -30,3 +30,7 @@ #undef IsTraceE #define IsTraceE TRUE #endif +#ifdef IsDebug +#undef IsDebug +#define IsDebug TRUE +#endif diff --git a/dll/user32.cpp b/dll/user32.cpp index 71ee4ca..76d0974 100644 --- a/dll/user32.cpp +++ b/dll/user32.cpp @@ -843,8 +843,11 @@ LONG WINAPI extSetWindowLong(HWND hwnd, int nIndex, LONG dwNewLong, SetWindowLon } } + // v2.03.94.fx2: removed dxw.IsFullScreen() check here ... WinProc routine must be verified in all conditions + // fixes "Nascar Racing 3" that was setting the WinProc while still in non fullscreen mode! if (((nIndex==GWL_WNDPROC)||(nIndex==DWL_DLGPROC)) && - dxw.IsFullScreen() && // v2.02.51 - see A10 Cuba.... + dxw.Windowize && // v2.03.95 - replaced dxw.IsFullScreen() check + // dxw.IsFullScreen() && // v2.02.51 - see A10 Cuba.... !(dxw.dwFlags6 & NOWINDOWHOOKS)){ // v2.03.41 - debug flag WNDPROC lres; WNDPROC OldProc; @@ -856,7 +859,8 @@ LONG WINAPI extSetWindowLong(HWND hwnd, int nIndex, LONG dwNewLong, SetWindowLon } // GPL fix - if(dxw.IsRealDesktop(hwnd) && dxw.Windowize) { + // v2.03.94.fx2: moved dxw.IsFullScreen() check here ... + if(dxw.IsRealDesktop(hwnd) && dxw.Windowize && dxw.IsFullScreen()) { hwnd=dxw.GethWnd(); OutTraceDW("SetWindowLong: DESKTOP hwnd, FIXING hwnd=%x\n",hwnd); } diff --git a/dll/winmm.cpp b/dll/winmm.cpp index b6d0e41..f3575a6 100644 --- a/dll/winmm.cpp +++ b/dll/winmm.cpp @@ -15,7 +15,7 @@ #define EMULATEJOY TRUE #define INVERTJOYAXIS TRUE -// #include "logall.h" // comment when not debugging +#include "logall.h" // comment when not debugging BOOL IsWithinMCICall = FALSE; @@ -137,81 +137,222 @@ MMRESULT WINAPI exttimeKillEvent(UINT uTimerID) You can use the MCI_GETDEVCAPS_CAN_STRETCH flag with the MCI_GETDEVCAPS command to determine if a device scales the image. A device returns FALSE if it cannot scale the image. */ -MCIERROR WINAPI extmciSendCommand(mciSendCommand_Type pmciSendCommand, MCIDEVICEID IDDevice, UINT uMsg, DWORD_PTR fdwCommand, DWORD_PTR dwParam) +static char *sStatusItem(DWORD dwItem) +{ + char *s; + switch(dwItem){ + case MCI_STATUS_LENGTH: s = "LENGTH"; break; + case MCI_STATUS_POSITION: s = "POSITION"; break; + case MCI_STATUS_NUMBER_OF_TRACKS: s = "NUMBER_OF_TRACKS"; break; + case MCI_STATUS_MODE: s = "MODE"; break; + case MCI_STATUS_MEDIA_PRESENT: s = "MEDIA_PRESENT"; break; + case MCI_STATUS_TIME_FORMAT: s = "TIME_FORMAT"; break; + case MCI_STATUS_READY: s = "READY"; break; + case MCI_STATUS_CURRENT_TRACK: s = "CURRENT_TRACK"; break; + default: s = "???"; break; + } + return s; +} + +static char *sDeviceType(DWORD dt) +{ + char *s; + switch(dt){ + case MCI_ALL_DEVICE_ID: s="ALL_DEVICE_ID"; break; + case MCI_DEVTYPE_VCR: s="VCR"; break; + case MCI_DEVTYPE_VIDEODISC: s="VIDEODISC"; break; + case MCI_DEVTYPE_OVERLAY: s="OVERLAY"; break; + case MCI_DEVTYPE_CD_AUDIO: s="CD_AUDIO"; break; + case MCI_DEVTYPE_DAT: s="DAT"; break; + case MCI_DEVTYPE_SCANNER: s="SCANNER"; break; + case MCI_DEVTYPE_ANIMATION: s="ANIMATION"; break; + case MCI_DEVTYPE_DIGITAL_VIDEO: s="DIGITAL_VIDEO"; break; + case MCI_DEVTYPE_OTHER: s="OTHER"; break; + case MCI_DEVTYPE_WAVEFORM_AUDIO: s="WAVEFORM_AUDIO"; break; + case MCI_DEVTYPE_SEQUENCER: s="SEQUENCER"; break; + default: s="unknown"; break; + } + return s; +} + +static void DumpMciMessage(char *label, BOOL isAnsi, UINT uMsg, DWORD_PTR fdwCommand, DWORD_PTR dwParam) +{ + switch(uMsg){ + case MCI_BREAK: + { + LPMCI_BREAK_PARMS lpBreak = (LPMCI_BREAK_PARMS)dwParam; + OutTrace("mciSendCommand%s: MCI_BREAK cb=%x virtkey=%d hwndbreak=%x\n", + label, lpBreak->dwCallback, lpBreak->nVirtKey, lpBreak->hwndBreak); + } + break; + case MCI_INFO: + { + LPMCI_INFO_PARMS lpInfo = (LPMCI_INFO_PARMS)dwParam; + OutTrace("mciSendCommand%s: MCI_INFO cb=%x retsize=%x\n", + label, lpInfo->dwCallback, lpInfo->dwRetSize); + } + break; + case MCI_PLAY: + { + LPMCI_PLAY_PARMS lpPlay = (LPMCI_PLAY_PARMS)dwParam; + OutTrace("mciSendCommand%s: MCI_PLAY cb=%x from=%x to=%x\n", + label, lpPlay->dwCallback, lpPlay->dwFrom, lpPlay->dwTo); + } + break; + case MCI_GETDEVCAPS: + { + LPMCI_GETDEVCAPS_PARMS lpDevCaps = (LPMCI_GETDEVCAPS_PARMS)dwParam; + OutTrace("mciSendCommand%s: MCI_GETDEVCAPS cb=%x ret=%x item=%x\n", + label, lpDevCaps->dwCallback, lpDevCaps->dwReturn, lpDevCaps->dwItem); + } + break; + case MCI_OPEN: + { + DWORD dwFlags = (DWORD)fdwCommand; + // how to dump LPMCI_OPEN_PARMS strings without crash? + if(isAnsi){ + LPMCI_OPEN_PARMSA lpOpen = (LPMCI_OPEN_PARMSA)dwParam; + OutTrace("mciSendCommand%s: MCI_OPEN %scb=%x devid=%x devtype=%s elementname=%s alias=%s\n", + label, + (dwFlags & MCI_OPEN_SHAREABLE) ? "OPEN_SHAREABLE " : "", + lpOpen->dwCallback, lpOpen->wDeviceID, + "", //(dwFlags & MCI_OPEN_TYPE) ? lpOpen->lpstrDeviceType : "", + (dwFlags & MCI_OPEN_ELEMENT) ? lpOpen->lpstrElementName : "", + (dwFlags & MCI_OPEN_ALIAS) ? lpOpen->lpstrAlias : ""); + } + else{ + LPMCI_OPEN_PARMSW lpOpen = (LPMCI_OPEN_PARMSW)dwParam; + OutTrace("mciSendCommand%s: MCI_OPEN cb=%x devid=%x devtype=%ls elementname=%ls alias=%ls\n", + label, + (dwFlags & MCI_OPEN_SHAREABLE) ? "OPEN_SHAREABLE " : "", + lpOpen->dwCallback, lpOpen->wDeviceID, + L"", //(dwFlags & MCI_OPEN_TYPE) ? lpOpen->lpstrDeviceType : L"", + (dwFlags & MCI_OPEN_ELEMENT) ? lpOpen->lpstrElementName : L"", + (dwFlags & MCI_OPEN_ALIAS) ? lpOpen->lpstrAlias : L""); + } + } + break; + case MCI_STATUS: + { + LPMCI_STATUS_PARMS lpStatus = (LPMCI_STATUS_PARMS)dwParam; + OutTrace("mciSendCommand%s: MCI_STATUS cb=%x ret=%x item=%x(%s) track=%x\n", + label, lpStatus->dwCallback, lpStatus->dwReturn, lpStatus->dwItem, sStatusItem(lpStatus->dwItem), lpStatus->dwTrack); + } + break; + case MCI_SYSINFO: + { + LPMCI_SYSINFO_PARMS lpSysInfo = (LPMCI_SYSINFO_PARMS)dwParam; + OutTrace("mciSendCommand%s: MCI_SYSINFO cb=%x retsize=%x number=%x devtype=%x(%s)\n", + label, lpSysInfo->dwCallback, lpSysInfo->dwRetSize, lpSysInfo->dwNumber, lpSysInfo->wDeviceType, sDeviceType(lpSysInfo->wDeviceType)); + } + break; + default: + { + LPMCI_GENERIC_PARMS lpGeneric = (LPMCI_GENERIC_PARMS)dwParam; + OutTrace("mciSendCommand%s: %s cb=%x\n", + label, ExplainMCICommands(uMsg), lpGeneric->dwCallback); + } + break; + } +} + +MCIERROR WINAPI extmciSendCommand(BOOL isAnsi, mciSendCommand_Type pmciSendCommand, MCIDEVICEID IDDevice, UINT uMsg, DWORD_PTR fdwCommand, DWORD_PTR dwParam) { RECT saverect; MCIERROR ret; MCI_ANIM_RECT_PARMS *pr; MCI_OVLY_WINDOW_PARMSW *pw; - OutTraceDW("mciSendCommand: IDDevice=%x msg=%x(%s) Command=%x(%s)\n", - IDDevice, uMsg, ExplainMCICommands(uMsg), fdwCommand, ExplainMCIFlags(uMsg, fdwCommand)); + OutTraceDW("mciSendCommand%c: IDDevice=%x msg=%x(%s) Command=%x(%s)\n", + isAnsi ? 'A' : 'W', + IDDevice, + uMsg, ExplainMCICommands(uMsg), + fdwCommand, ExplainMCIFlags(uMsg, fdwCommand)); + + if(IsDebug) DumpMciMessage(">>", isAnsi, uMsg, fdwCommand, dwParam); if(dxw.dwFlags6 & BYPASSMCI){ - if((uMsg == MCI_STATUS) && (fdwCommand & MCI_STATUS_ITEM)){ - // fix for Tie Fighter 95: when bypassing, let the caller know you have no CD tracks - // otherwise you risk an almost endless loop going through the unassigned returned - // number of ghost tracks - // fix for "Emperor of the Fading Suns": the MCI_STATUS_ITEM is set in .or. with - // MCI_TRACK - MCI_STATUS_PARMS *p = (MCI_STATUS_PARMS *)dwParam; - OutTraceDW("mciSendCommand: MCI_STATUS item=%d track=%d ret=%d\n", p->dwItem, p->dwReturn, p->dwTrack); - if(fdwCommand & MCI_TRACK){ - p->dwReturn = 1; - } - else{ - p->dwItem = 0; - p->dwTrack = 0; - p->dwReturn = 0; - } - OutTraceDW("mciSendCommand: BYPASS fixing MCI_STATUS\n"); + //MCI_OPEN_PARMS *op; + MCI_STATUS_PARMS *sp; + switch(uMsg){ + case MCI_STATUS: + if(fdwCommand & MCI_STATUS_ITEM){ + // fix for Tie Fighter 95: when bypassing, let the caller know you have no CD tracks + // otherwise you risk an almost endless loop going through the unassigned returned + // number of ghost tracks + // fix for "Emperor of the Fading Suns": the MCI_STATUS_ITEM is set in .or. with + // MCI_TRACK + sp = (MCI_STATUS_PARMS *)dwParam; + switch(fdwCommand){ + case MCI_TRACK: + sp->dwReturn = 1; + break; + default: + sp->dwTrack = 0; + if(sp->dwItem == MCI_STATUS_CURRENT_TRACK) sp->dwTrack = 1; + if(sp->dwItem == MCI_STATUS_NUMBER_OF_TRACKS) sp->dwTrack = 1; + if(sp->dwItem == MCI_STATUS_LENGTH) sp->dwTrack = 200; + sp->dwReturn = 0; + break; + } + } + ret = 0; + break; + default: + ret = 0; + break; } - else{ - OutTraceDW("mciSendCommand: BYPASS\n"); - } - return 0; + if(IsDebug) DumpMciMessage("<<", isAnsi, uMsg, fdwCommand, dwParam); + return ret; } if(dxw.IsFullScreen()){ switch(uMsg){ - case MCI_WINDOW: - pw = (MCI_OVLY_WINDOW_PARMSW *)dwParam; - OutTraceB("mciSendCommand: hwnd=%x CmdShow=%x\n", - pw->hWnd, pw->nCmdShow); - //fdwCommand |= MCI_ANIM_WINDOW_ENABLE_STRETCH; - //fdwCommand &= ~MCI_ANIM_WINDOW_DISABLE_STRETCH; - //fdwCommand &= ~MCI_ANIM_WINDOW_HWND; - if(dxw.IsRealDesktop(pw->hWnd)) { - pw->hWnd = dxw.GethWnd(); - OutTraceB("mciSendCommand: REDIRECT hwnd=%x\n", pw->hWnd); - } - break; - case MCI_PUT: - RECT client; - pr = (MCI_ANIM_RECT_PARMS *)dwParam; - OutTraceB("mciSendCommand: rect=(%d,%d),(%d,%d)\n", - pr->rc.left, pr->rc.top, pr->rc.right, pr->rc.bottom); - (*pGetClientRect)(dxw.GethWnd(), &client); - //fdwCommand |= MCI_ANIM_PUT_DESTINATION; - fdwCommand |= MCI_ANIM_RECT; - saverect=pr->rc; - pr->rc.top = (pr->rc.top * client.bottom) / dxw.GetScreenHeight(); - pr->rc.bottom = (pr->rc.bottom * client.bottom) / dxw.GetScreenHeight(); - pr->rc.left = (pr->rc.left * client.right) / dxw.GetScreenWidth(); - pr->rc.right = (pr->rc.right * client.right) / dxw.GetScreenWidth(); - OutTraceB("mciSendCommand: fixed rect=(%d,%d),(%d,%d)\n", - pr->rc.left, pr->rc.top, pr->rc.right, pr->rc.bottom); - break; - case MCI_PLAY: - if(dxw.dwFlags6 & NOMOVIES) return 0; // ??? - break; - case MCI_OPEN: - if(dxw.dwFlags6 & NOMOVIES) return 275; // quite brutal, but working .... - break; + case MCI_WINDOW: + pw = (MCI_OVLY_WINDOW_PARMSW *)dwParam; + OutTraceB("mciSendCommand: hwnd=%x CmdShow=%x\n", + pw->hWnd, pw->nCmdShow); + if(dxw.IsRealDesktop(pw->hWnd)) { + pw->hWnd = dxw.GethWnd(); + OutTraceB("mciSendCommand: REDIRECT hwnd=%x\n", pw->hWnd); + } + break; + case MCI_PUT: + RECT client; + pr = (MCI_ANIM_RECT_PARMS *)dwParam; + OutTraceB("mciSendCommand: rect=(%d,%d),(%d,%d)\n", + pr->rc.left, pr->rc.top, pr->rc.right, pr->rc.bottom); + (*pGetClientRect)(dxw.GethWnd(), &client); + //fdwCommand |= MCI_ANIM_PUT_DESTINATION; + fdwCommand |= MCI_ANIM_RECT; + saverect=pr->rc; + pr->rc.top = (pr->rc.top * client.bottom) / dxw.GetScreenHeight(); + pr->rc.bottom = (pr->rc.bottom * client.bottom) / dxw.GetScreenHeight(); + pr->rc.left = (pr->rc.left * client.right) / dxw.GetScreenWidth(); + pr->rc.right = (pr->rc.right * client.right) / dxw.GetScreenWidth(); + OutTraceB("mciSendCommand: fixed rect=(%d,%d),(%d,%d)\n", + pr->rc.left, pr->rc.top, pr->rc.right, pr->rc.bottom); + break; + case MCI_PLAY: + if(dxw.dwFlags6 & NOMOVIES) return 0; // ??? + break; + case MCI_OPEN: + if(dxw.dwFlags6 & NOMOVIES) return 275; // quite brutal, but working .... + break; + case MCI_STOP: + if(dxw.dwFlags6 & NOMOVIES) return 0; // ??? + break; } } + LPMCI_GENERIC_PARMS lpGeneric = (LPMCI_GENERIC_PARMS)dwParam; + if(HIWORD(lpGeneric->dwCallback) == NULL) { + lpGeneric->dwCallback = MAKELONG(dxw.GethWnd(),0); + OutTraceB("mciSendCommand: REDIRECT dwCallback=%x\n", dxw.GethWnd()); + } + ret=(*pmciSendCommand)(IDDevice, uMsg, fdwCommand, dwParam); + if(IsDebug) DumpMciMessage("<<", isAnsi, uMsg, fdwCommand, dwParam); if(ret == 0){ switch(uMsg){ @@ -229,12 +370,12 @@ MCIERROR WINAPI extmciSendCommand(mciSendCommand_Type pmciSendCommand, MCIDEVICE MCIERROR WINAPI extmciSendCommandA(MCIDEVICEID IDDevice, UINT uMsg, DWORD_PTR fdwCommand, DWORD_PTR dwParam) { - return extmciSendCommand(pmciSendCommandA, IDDevice, uMsg, fdwCommand, dwParam); + return extmciSendCommand(TRUE, pmciSendCommandA, IDDevice, uMsg, fdwCommand, dwParam); } MCIERROR WINAPI extmciSendCommandW(MCIDEVICEID IDDevice, UINT uMsg, DWORD_PTR fdwCommand, DWORD_PTR dwParam) { - return extmciSendCommand(pmciSendCommandW, IDDevice, uMsg, fdwCommand, dwParam); + return extmciSendCommand(FALSE, pmciSendCommandW, IDDevice, uMsg, fdwCommand, dwParam); } MCIERROR WINAPI extmciSendStringA(LPCTSTR lpszCommand, LPTSTR lpszReturnString, UINT cchReturn, HANDLE hwndCallback)