diff --git a/src/wintasee/hooks/ddrawhooks.cpp b/src/wintasee/hooks/ddrawhooks.cpp index f6f3d9c..a1bbc30 100644 --- a/src/wintasee/hooks/ddrawhooks.cpp +++ b/src/wintasee/hooks/ddrawhooks.cpp @@ -46,6 +46,49 @@ static RECT c_bltsaved_buf = {0}; static DDBLTFX e_bltsaved_buf = {0}; +void RescaleRect(RECT& rect, RECT from, RECT to); +void ConfineRect(RECT& rect, RECT bounds) +{ + RECT tempRect = rect; + if(tempRect.left < bounds.left) + { + tempRect.right += bounds.left - tempRect.left; + tempRect.left = bounds.left; + if(tempRect.right > bounds.right) + tempRect.right = bounds.right; + } + else if(tempRect.right > bounds.right) + { + tempRect.left += bounds.right - tempRect.right; + tempRect.right = bounds.right; + if(tempRect.left < bounds.left) + tempRect.left = bounds.left; + } + if(tempRect.top < bounds.top) + { + tempRect.bottom += bounds.top - tempRect.top; + tempRect.top = bounds.top; + if(tempRect.bottom > bounds.bottom) + tempRect.bottom = bounds.bottom; + } + else if(tempRect.bottom > bounds.bottom) + { + tempRect.top += bounds.bottom - tempRect.bottom; + tempRect.bottom = bounds.bottom; + if(tempRect.top < bounds.top) + tempRect.top = bounds.top; + } + rect = tempRect; +} +//LPRECT ExpandRect(RECT& rect, RECT other) +//{ +// RECT src = rect; +// rect.left = min(src.left, other.left); +// rect.top = min(src.top, other.top); +// rect.right = max(src.right, other.right); +// rect.bottom = max(src.bottom, other.bottom); +// return ▭ +//} @@ -181,7 +224,7 @@ struct MyDirectDrawSurface return rv; } - static void HandleNewFrame(int xOrigin, int yOrigin, DIRECTDRAWSURFACEN* pThis, DIRECTDRAWSURFACEN* pSource) + static void HandleNewFrame(int xOrigin, int yOrigin, DIRECTDRAWSURFACEN* pThis, DIRECTDRAWSURFACEN* pSource, RECT* destRect, RECT* srcRect) { usingSDLOrDD = true; @@ -191,9 +234,16 @@ struct MyDirectDrawSurface // it's a new frame, so do frame boundary calculations // first save enough data for RedrawScreen to work when the state is loaded - //a_bltsaved = 0;//destRect ? ((a_bltsaved_buf = *destRect), &a_bltsaved_buf) : 0; + //static int lastFrameXOrigin=-1, lastFrameYOrigin=-1; + //if(lastFrameXOrigin != xOrigin || lastFrameYOrigin != yOrigin) + //{ + // lastFrameXOrigin = xOrigin; + // lastFrameYOrigin = yOrigin; + // a_bltsaved_buf = *destRect; + //} + //a_bltsaved = destRect ? ExpandRect(a_bltsaved_buf, *destRect) : 0; b_bltsaved = (LPDIRECTDRAWSURFACE)pSource; - //c_bltsaved = 0;//srcRect ? ((c_bltsaved_buf = *srcRect), &c_bltsaved_buf) : 0; + //c_bltsaved = srcRect ? ExpandRect(c_bltsaved_buf, *srcRect) : 0; //d_bltsaved = 0;//flags; //e_bltsaved = 0;//bltfx ? ((e_bltsaved_buf = *bltfx), &e_bltsaved_buf) : 0; dds_bltsaved = (LPDIRECTDRAWSURFACE)pThis; @@ -261,15 +311,34 @@ struct MyDirectDrawSurface DDSCAPSN caps; GetCaps(pThis, &caps); - bool destIsPrimary = (caps.dwCaps & (DDSCAPS_PRIMARYSURFACE|DDSCAPS_FRONTBUFFER)) != 0; + + DDSCAPSN caps2; + bool srcIsPrimary; + if(pSource) + { + GetCaps(pSource, &caps2); + srcIsPrimary = (caps2.dwCaps & (DDSCAPS_PRIMARYSURFACE|DDSCAPS_FRONTBUFFER)) != 0; + } + else + { + srcIsPrimary = false; + } + RECT idRect; + RECT tempSrcRect; + RECT tempDstRect; - if(destIsPrimary) + if(srcIsPrimary || destIsPrimary) { + // might need to set or override the destination rect to front buffer + // could be because we're in force-windowed mode, or because we're preventing the game from knowing where the window really is + + if(destIsPrimary) { + // we want idrect to be independent of any adjustments we make to the destrect if(destRect) - memcpy(&idRect, destRect, sizeof(RECT)); + idRect = *destRect; else memset(&idRect, 0, sizeof(RECT)); //POINT pt = {0,0}; @@ -277,53 +346,71 @@ struct MyDirectDrawSurface // OffsetRect(&idRect, pt.x, pt.y); } - // might need to set or override the destination rect to front buffer - // could be because we're in force-windowed mode, or because we're preventing the game from knowing where the window really is + RECT realRect; + if(GetClientRect(gamehwnd, &realRect) && ClientToScreen(gamehwnd, (LPPOINT)&realRect.left) && ClientToScreen(gamehwnd, (LPPOINT)&realRect.right)) { - static RECT rect; - if(GetClientRect(gamehwnd, &rect) && ClientToScreen(gamehwnd, (LPPOINT)&rect.left) && ClientToScreen(gamehwnd, (LPPOINT)&rect.right)) + DDSURFACEDESCN ddsdBB = { sizeof(DDSURFACEDESCN) }; + if(!s_theBackBuffer || FAILED(GetSurfaceDesc((DIRECTDRAWSURFACEN*)s_theBackBuffer, &ddsdBB))) { - //debugprintf("AAAA: if(!%d || (%d && %d >= %d && %d >= %d))\n", destRect, fakeDisplayValid, destRect->right-destRect->left, fakeDisplayWidth, destRect->bottom-destRect->top, fakeDisplayHeight); - if(!destRect || (fakeDisplayValid && destRect->right-destRect->left >= fakeDisplayWidth && destRect->bottom-destRect->top >= fakeDisplayHeight)) - destRect = ▭ + ddsdBB.dwWidth = fakeDisplayWidth; + ddsdBB.dwHeight = fakeDisplayHeight; + } + + if(destIsPrimary) + { + if(!destRect) + { + // whole screen was target, so fill client + destRect = &tempDstRect; // avoid altering original rect, since the game owns it + *destRect = realRect; + } + else if(fakeDisplayValid) + { + //debugprintf("destRect = %d %d %d %d\n", destRect->left, destRect->top, destRect->right, destRect->bottom); + + // part of screen was target, so rescale to client + RECT fakeRect = {0, 0, fakeDisplayWidth, fakeDisplayHeight}; + tempDstRect = *destRect; destRect = &tempDstRect; // avoid altering original rect, since the game owns it + RescaleRect(*destRect, fakeRect, realRect); + //ConfineRect(*destRect, realRect); // shouldn't need this + } else { - // because some games calculate and draw only "dirty regions", - // we can't assume any draw to the front buffer fills the whole thing, - // so only offset the destination rect as necessary to keep it in the window - static RECT rect2; - memcpy(&rect2, destRect, sizeof(RECT)); - destRect = &rect2; - if(rect2.left < rect.left) - { - rect2.right += rect.left - rect2.left; - rect2.left = rect.left; - if(rect2.right > rect.right) - rect2.right = rect.right; - } - else if(rect2.right > rect.right) - { - rect2.left += rect.right - rect2.right; - rect2.right = rect.right; - if(rect2.left < rect.left) - rect2.left = rect.left; - } - if(rect2.top < rect.top) - { - rect2.bottom += rect.top - rect2.top; - rect2.top = rect.top; - if(rect2.bottom > rect.bottom) - rect2.bottom = rect.bottom; - } - else if(rect2.bottom > rect.bottom) - { - rect2.top += rect.bottom - rect2.bottom; - rect2.bottom = rect.bottom; - if(rect2.top < rect.top) - rect2.top = rect.top; - } + // in this case, we're not currently sure where the game thinks it's rendering to, + // so the best we can do is push it into view if the game got it wrong. + // here we make the assumption that the game wants to render into somewhere in its own window's client region. + tempDstRect = *destRect; destRect = &tempDstRect; // avoid altering original rect, since the game owns it + ConfineRect(*destRect, realRect); } } + + if(srcIsPrimary) + { + if(!srcRect) + { + // whole screen was source, so fill client + srcRect = &tempSrcRect; // avoid altering original rect, since the game owns it + *srcRect = realRect; + } + else if(fakeDisplayValid) + { + // part of screen was source, so rescale to client + RECT fakeRect = {0, 0, fakeDisplayWidth, fakeDisplayHeight}; + tempSrcRect = *srcRect; srcRect = &tempSrcRect; // avoid altering original rect, since the game owns it + RescaleRect(*srcRect, fakeRect, realRect); + } + else + { + tempSrcRect = *srcRect; srcRect = &tempSrcRect; // avoid altering original rect, since the game owns it + ConfineRect(*srcRect, realRect); + } + } + + // TODO: if destIsPrimary and source is not the corresponding part of the backbuffer, + // then this blit won't show up in AVIs. + // for example, Nova 3000 does frontbuffer-to-frontbuffer blits + // and incomplete-backbuffer-to-frontbuffer blits for screen transitions. + // probably need a virtual frontbuffer to support such effects. } } @@ -334,51 +421,36 @@ struct MyDirectDrawSurface rv = DD_OK; else { -#if 1 - if(pSource) - { - DDSCAPSN caps2; - GetCaps(pSource, &caps2); - if((caps2.dwCaps & (DDSCAPS_PRIMARYSURFACE|DDSCAPS_FRONTBUFFER))) // srcIsPrimary? - { - if(tasflags.forceWindowed && s_theBackBuffer) - { - DDSURFACEDESCN ddsdBB = { sizeof(DDSURFACEDESCN) }; - GetSurfaceDesc((DIRECTDRAWSURFACEN*)s_theBackBuffer, &ddsdBB); - RECT bbRect = {0, 0, ddsdBB.dwWidth, ddsdBB.dwHeight }; - static RECT rect; - rect = bbRect; - POINT pt = {0,0}; - if(ClientToScreen(gamehwnd, &pt) && OffsetRect(&rect, pt.x, pt.y)) - { - if(!srcRect || (fakeDisplayValid && srcRect->right-destRect->left >= fakeDisplayWidth && srcRect->bottom-destRect->top >= fakeDisplayHeight)) - { - srcRect = ▭ - } - } - } - } - } -#endif + //if(srcIsPrimary && srcRect && destRect) + //{ + // debugprintf("src = %d %d %d %d 0x%X\n", srcRect->left, srcRect->top, srcRect->right, srcRect->bottom, pSource); + // debugprintf("dst = %d %d %d %d 0x%X\n", destRect->left, destRect->top, destRect->right, destRect->bottom, pThis); + //} + + // do the actual blit rv = Blt(pThis, destRect,pSource,srcRect,flags,bltfx); + if(!destIsPrimary) videoMemoryBackupDirty[pThis] = TRUE; else if(pSource) // catch the case where the back buffer changes but is never blitted to: videoMemoryBackupDirty[pSource] = TRUE; } - if(destIsPrimary && pSource) + if(!srcIsPrimary) { - if(s_theBackBuffer != pSource) - ddrawdebugprintf("s_theBackBuffer: 0x%X -> 0x%X\n", s_theBackBuffer, pSource); - s_theBackBuffer = pSource; - } + if(destIsPrimary && pSource) + { + if(s_theBackBuffer != pSource) + ddrawdebugprintf("s_theBackBuffer: 0x%X -> 0x%X\n", s_theBackBuffer, pSource); + s_theBackBuffer = pSource; + } - // if the destination is the front buffer, this is likely a new frame - if(destIsPrimary && !redrawingScreen) - HandleNewFrame(idRect.left, idRect.top, pThis, pSource); + // if the destination is the front buffer, this is likely a new frame + if(destIsPrimary && !redrawingScreen) + HandleNewFrame(idRect.left, idRect.top, pThis, pSource, destRect, srcRect); + } return rv; } @@ -408,7 +480,7 @@ struct MyDirectDrawSurface { if(!destIsPrimary) { -#if 1 +#if 1 // TODO: I can't remember what game this is for, but the code should be updated if not deleted. if(pSource) { DDSCAPSN caps2; @@ -471,9 +543,20 @@ struct MyDirectDrawSurface //if(destIsPrimary && pSource) //{ - // if(s_theBackBuffer != pSource) - // ddrawdebugprintf("s_theBackBuffer: 0x%X -> 0x%X\n", s_theBackBuffer, pSource); - // s_theBackBuffer = pSource; + // DDSCAPSN caps2; + // GetCaps(pSource, &caps2); + // bool srcIsPrimary = (caps2.dwCaps & (DDSCAPS_PRIMARYSURFACE|DDSCAPS_FRONTBUFFER)) != 0; + // if(srcIsPrimary) + // { + // // if the destination and source are both the front buffer, don't treat it as a new frame + // destIsPrimary = false; + // } + // else + // { + // if(s_theBackBuffer != pSource) + // ddrawdebugprintf("s_theBackBuffer: 0x%X -> 0x%X\n", s_theBackBuffer, pSource); + // s_theBackBuffer = pSource; + // } //} //// if the destination is the front buffer, this is likely a new frame @@ -584,7 +667,7 @@ struct MyDirectDrawSurface s_theBackBuffer = pThis; if(!redrawingScreen) - HandleNewFrame(0, 0, pOther, pThis); + HandleNewFrame(0, 0, pOther, pThis, NULL, NULL); } else { @@ -593,16 +676,7 @@ struct MyDirectDrawSurface GetAttachedFakeBackBuf(pThis, &pOther); } - LPRECT lpRect = NULL; - RECT rect; - POINT pt = {0,0}; - if(GetClientRect(gamehwnd, &rect) && ClientToScreen(gamehwnd, &pt) && OffsetRect(&rect, pt.x, pt.y)) - lpRect = ▭ - - //if(lpRect) - // debugprintf("%dx%d\n", lpRect->right-lpRect->left, lpRect->bottom-lpRect->top); - - rv = MyBlt(pThis, lpRect, pOther, 0, DDBLT_WAIT, 0); + rv = MyBlt(pThis, NULL, pOther, 0, DDBLT_WAIT, 0); } return rv; @@ -690,7 +764,7 @@ struct MyDirectDrawSurface { rv = Unlock(pThis, lockRect); if(VerifyIsTrustedCaller(!tls.callerisuntrusted)) // needed for rescue: the beagles (must avoid setting usingSDLOrDD there, at least the way MySwapBuffers was originally) and could help avoid spurious extra frames in some other games - HandleNewFrame(0, 0, pThis, pThis); + HandleNewFrame(0, 0, pThis, pThis, (LPRECT)lockRect, (LPRECT)lockRect); } else { @@ -705,7 +779,7 @@ struct MyDirectDrawSurface #endif //cmdprintf("DEBUGPAUSE: 4"); if(VerifyIsTrustedCaller(!tls.callerisuntrusted)) // needed for rescue: the beagles (must avoid setting usingSDLOrDD there, at least the way MySwapBuffers was originally) and could help avoid spurious extra frames in some other games - HandleNewFrame(0, 0, pThis, pBackbuffer); + HandleNewFrame(0, 0, pThis, pBackbuffer, (LPRECT)lockRect, (LPRECT)lockRect); } } @@ -1091,7 +1165,6 @@ class MyDirectDraw : public IDirectDrawN { // debugprintf(__FUNCTION__ ": m_dd = 0x%X\n", m_dd); ddrawdebugprintf(__FUNCTION__ " called (#%d).\n", ++s_numMyDirectDraws); - fakeDisplayValid = FALSE; } ~MyDirectDraw() { @@ -1524,6 +1597,18 @@ class MyDirectDraw : public IDirectDrawN ddrawdebugprintf(__FUNCTION__ " called.\n"); ThreadLocalStuff& curtls = tls; + + if(VerifyIsTrustedCaller(!curtls.callerisuntrusted)) + { + if(!a) + return DDERR_INVALIDPARAMS; + memset(a, 0, sizeof(DDDEVICEIDENTIFIERN)); + strcpy(a->szDriver, "hourglass_ddraw"); + strcpy(a->szDescription, "Hourglass DirectDraw Wrapper"); + a->guidDeviceIdentifier.Data1 = 0x12345432; + return DD_OK; + } + const char* oldName = curtls.curThreadCreateName; curtls.curThreadCreateName = "DirectDraw"; curtls.callerisuntrusted++; diff --git a/src/wintasee/hooks/gdihooks.cpp b/src/wintasee/hooks/gdihooks.cpp index f37b870..e25df67 100644 --- a/src/wintasee/hooks/gdihooks.cpp +++ b/src/wintasee/hooks/gdihooks.cpp @@ -133,6 +133,34 @@ static void FrameBoundaryHDCtoAVI(HDC hdc,int xSrc,int ySrc,int xRes,int yRes) #endif } +static const int gdiFrameBigEnoughWidth = 120; +static const int gdiFrameBigEnoughHeight = 80; +static bool HDCSizeBigEnoughForFrameBoundary(HDC hdc) +{ +#ifdef UNSELECT_BEFORE_HDC_CAPTURE + // the docs say: "The bitmap identified by the hbmp parameter must not be selected into a device context when the application calls this function." + HBITMAP bitmap = (HBITMAP)SelectObject(hdc, CreateCompatibleBitmap(hdc, 1, 1)); +#else + // but this appears to work fine and it's probably at least a little bit faster, so... + HBITMAP bitmap = (HBITMAP)GetCurrentObject(hdc, OBJ_BITMAP); +#endif + + BITMAPINFO bmi = {sizeof(BITMAPINFOHEADER)}; + GetDIBits(hdc, bitmap, 0, 0, 0, &bmi, DIB_RGB_COLORS); + + int width = bmi.bmiHeader.biWidth; + int height = bmi.bmiHeader.biHeight; + if(height < 0) height = -height; + + bool bigEnough = (width >= gdiFrameBigEnoughWidth && height >= gdiFrameBigEnoughHeight); + +#ifdef UNSELECT_BEFORE_HDC_CAPTURE + DeleteObject(SelectObject(hdc, bitmap)); +#endif + + return bigEnough; +} + int depth_SwapBuffers = 0; @@ -164,6 +192,20 @@ HOOKFUNC BOOL MySwapBuffers(HDC hdc) } + + +void RescaleRect(RECT& rect, RECT from, RECT to) +{ + rect.left = ((rect.left - from.left) * (to.right - to.left)) / (from.right - from.left) + to.left; + rect.top = ((rect.top - from.top) * (to.bottom - to.top)) / (from.bottom - from.top) + to.top; + rect.right = ((rect.right - from.left) * (to.right - to.left)) / (from.right - from.left) + to.left; + rect.bottom = ((rect.bottom - from.top) * (to.bottom - to.top)) / (from.bottom - from.top) + to.top; +} + + + + + static HDC s_hdcSrcSaved; static HDC s_hdcDstSaved; static bool s_gdiPendingRefresh; @@ -190,7 +232,11 @@ HOOKFUNC BOOL WINAPI MyStretchBlt( if((/*s_gdiPhaseDetector.AdvanceAndCheckCycleBoundary(MAKELONG(nXOriginDest,nYOriginDest)) ||*/ tls.peekedMessage) && VerifyIsTrustedCaller(!tls.callerisuntrusted)) { - isFrameBoundary = true; + if((nWidthSrc >= gdiFrameBigEnoughWidth && nHeightSrc >= gdiFrameBigEnoughHeight) + || HDCSizeBigEnoughForFrameBoundary(hdcSrc)) + { + isFrameBoundary = true; + } } } } @@ -245,7 +291,11 @@ HOOKFUNC BOOL WINAPI MyBitBlt( if((/*s_gdiPhaseDetector.AdvanceAndCheckCycleBoundary(MAKELONG(nXDest,nYDest)) ||*/ tls.peekedMessage) && VerifyIsTrustedCaller(!tls.callerisuntrusted)) { - isFrameBoundary = true; + if((nWidth >= gdiFrameBigEnoughWidth && nHeight >= gdiFrameBigEnoughHeight) + || HDCSizeBigEnoughForFrameBoundary(hdcSrc)) + { + isFrameBoundary = true; + } } } } @@ -271,29 +321,27 @@ HOOKFUNC BOOL WINAPI MyBitBlt( else { HWND hwnd = WindowFromDC(hdcDest); - RECT rect; - if(!GetClientRect(hwnd, &rect) || (rect.right == fakeDisplayWidth && rect.bottom == fakeDisplayHeight)) + RECT realRect; + if(!GetClientRect(hwnd, &realRect) || (realRect.right == fakeDisplayWidth && realRect.bottom == fakeDisplayHeight)) { rv = BitBlt(hdcDest,nXDest,nYDest,nWidth,nHeight,hdcSrc,nXSrc,nYSrc,dwRop); } else { // support resized fake-fullscreen windows in games like Lyle in Cube Sector + // a little iffy: sprites leave pixels behind occasionally at non-integral scales HDC hdcTemp = 0; HDC hdc = hdcDest; - if(rect.right > fakeDisplayWidth || rect.bottom > fakeDisplayHeight) + if(realRect.right > fakeDisplayWidth || realRect.bottom > fakeDisplayHeight) { // sidestep clip region (it can't be expanded without switching HDCs) hdcTemp = GetDC(hwnd); hdc = hdcTemp; } - // iffy, sprites leave pixels behind occasionally at non-integral scales - int x0 = (nXDest * rect.right) / fakeDisplayWidth; - int y0 = (nYDest * rect.bottom) / fakeDisplayHeight; - int x1 = ((nXDest+nWidth) * rect.right) / fakeDisplayWidth; - int y1 = ((nYDest+nHeight) * rect.bottom) / fakeDisplayHeight; - //debugprintf("%dx%d -> %dx%d ... (%d,%d,%d,%d) -> (%d,%d,%d,%d)\n", fakeDisplayWidth, fakeDisplayHeight, rect.right, rect.bottom, nXDest,nYDest,nXDest+nWidth,nYDest+nHeight, x0,y0,x1,y1); - rv = StretchBlt(hdc,x0,y0,x1-x0,y1-y0,hdcSrc,nXSrc,nYSrc,nWidth,nHeight,dwRop); + RECT dstRect = {nXDest, nYDest, nXDest+nWidth, nYDest+nHeight}; + RECT fakeRect = {0, 0, fakeDisplayWidth, fakeDisplayHeight}; + RescaleRect(dstRect, fakeRect, realRect); + rv = StretchBlt(hdc, dstRect.left,dstRect.top,dstRect.right-dstRect.left,dstRect.bottom-dstRect.top, hdcSrc, nXSrc,nYSrc,nWidth,nHeight, dwRop); if(hdcTemp) ReleaseDC(hwnd, hdcTemp); } diff --git a/src/wintasee/hooks/messagehooks.cpp b/src/wintasee/hooks/messagehooks.cpp index e444efc..7921a48 100644 --- a/src/wintasee/hooks/messagehooks.cpp +++ b/src/wintasee/hooks/messagehooks.cpp @@ -462,9 +462,12 @@ static MessageActionFlags GetMessageActionFlags(UINT message, WPARAM wParam, LPA case WM_SETCURSOR: case WM_NOTIFY: case WM_SHOWWINDOW: - case WM_COMMAND: case WM_SYSCOMMAND: break;//return MAF_INTERCEPT | MAF_RETURN_0; // maybe ok to ditch? + case WM_COMMAND: + if(VerifyIsTrustedCaller(!tls.callerisuntrusted)) + return MAF_PASSTHROUGH | MAF_RETURN_OS; // hack to fix F2 command in Eternal Daughter + break; default: debuglog(LCF_MESSAGES|LCF_FREQUENT|LCF_TODO, "CONSIDER: 0x%X (%s), 0x%X, 0x%X\n", message, GetWindowsMessageName(message), wParam, lParam); //cmdprintf("SHORTTRACE: 3,50"); @@ -636,7 +639,9 @@ HOOKFUNC LRESULT WINAPI MyDefWindowProcW(HWND hWnd, UINT Msg, WPARAM wParam, LPA // MyWndProcInternal LRESULT DispatchMessageInternal(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, bool ascii/*=true*/, MessageActionFlags maf/*=MAF_PASSTHROUGH|MAF_RETURN_OS*/) { - if(/*inPauseHandler || */(tls.callerisuntrusted > (InSendMessage()?1:0))) // if it's the OS or pause handler calling us back, + LONG untrusted = tls.callerisuntrusted; + //untrusted = VerifyIsTrustedCaller(!untrusted) ? 0 : (untrusted ? untrusted : 1); + if(/*inPauseHandler || */(untrusted > (InSendMessage()?1:0))) // if it's the OS or pause handler calling us back, { // we can't rely on it doing so consistently across systems, if(!(maf & (MAF_INTERCEPT|MAF_BYPASSGAME))) { @@ -1545,7 +1550,9 @@ HOOKFUNC LRESULT WINAPI MyDispatchMessageA(CONST MSG *lpMsg) //return MyWndProcA(msg.hwnd, msg.message, msg.wParam, msg.lParam); return DispatchMessageInternal(msg.hwnd, msg.message, msg.wParam, msg.lParam, true); #else - LRESULT rv = DispatchMessageA(lpMsg); +// LRESULT rv = DispatchMessageA(lpMsg); + const MSG& msg = *lpMsg; + LRESULT rv = DispatchMessageInternal(msg.hwnd, msg.message, msg.wParam, msg.lParam, true, MAF_PASSTHROUGH|MAF_RETURN_OS); return rv; #endif } @@ -1559,7 +1566,9 @@ HOOKFUNC LRESULT WINAPI MyDispatchMessageW(CONST MSG *lpMsg) //return MyWndProcW(msg.hwnd, msg.message, msg.wParam, msg.lParam); return DispatchMessageInternal(msg.hwnd, msg.message, msg.wParam, msg.lParam, false); #else - LRESULT rv = DispatchMessageW(lpMsg); +// LRESULT rv = DispatchMessageW(lpMsg); + const MSG& msg = *lpMsg; + LRESULT rv = DispatchMessageInternal(msg.hwnd, msg.message, msg.wParam, msg.lParam, false, MAF_PASSTHROUGH|MAF_RETURN_OS); return rv; #endif } diff --git a/src/wintasee/hooks/threadhooks.cpp b/src/wintasee/hooks/threadhooks.cpp index 9068b05..e4f69ae 100644 --- a/src/wintasee/hooks/threadhooks.cpp +++ b/src/wintasee/hooks/threadhooks.cpp @@ -138,13 +138,13 @@ HOOKFUNC HANDLE WINAPI MyCreateThread( debuglog(LCF_THREAD, __FUNCTION__"(0x%X) called, tls.curThreadCreateName = %s\n", (DWORD)lpStartAddress, tls.curThreadCreateName); //cmdprintf("SHORTTRACE: 3,50"); - if(tasflags.threadMode == 0 || tasflags.threadMode == 3 && !tls.curThreadCreateName || tasflags.threadMode == 4 && tls.curThreadCreateName) + if(tasflags.threadMode == 0 || tasflags.threadMode == 3 && !tls.curThreadCreateName || tasflags.threadMode == 4 && tls.curThreadCreateName || (tasflags.threadMode == 5 && !VerifyIsTrustedCaller(!tls.callerisuntrusted))) { - debuglog(LCF_THREAD, __FUNCTION__": thread creation denied.\n"); - cmdprintf("DENIEDTHREAD: %Iu", lpStartAddress); - const char* threadTypeName = tls.curThreadCreateName; + debuglog(LCF_THREAD, __FUNCTION__": thread creation denied. \"%s\"\n", threadTypeName?threadTypeName:"unknown_thread"); + cmdprintf("DENIEDTHREAD: %Iu", lpStartAddress); + // FIXME: it's a terrible hack to choose between these two methods depending on whether we have a thread name, // but it gets herocore working with threads disabled, and I can't think of a better solution at the moment. // the reason it helps there is that herocore.exe crashes if it can't create a thread but works correctly if it creates a do-nothing thread, diff --git a/src/wintasee/hooks/windowhooks.cpp b/src/wintasee/hooks/windowhooks.cpp index 8648bd1..67b98e7 100644 --- a/src/wintasee/hooks/windowhooks.cpp +++ b/src/wintasee/hooks/windowhooks.cpp @@ -469,7 +469,10 @@ HOOKFUNC BOOL WINAPI MyShowWindow(HWND hWnd, int nCmdShow) HOOKFUNC BOOL WINAPI MyGetClientRect(HWND hWnd, LPRECT lpRect) { - if(fakeDisplayValid && IsWindowFakeFullscreen(hWnd)) + // IsWindowFakeFullscreen checks disabled because they let window position info leak to Eternal Daughter and possibly others (maybe need to use ::IsChild inside IsWindowFakeFullscreen) + // VerifyIsTrustedCaller checks added as a hack so that DirectDrawClipper can still get the real window coordinates + // TODO: instead of calling VerifyIsTrustedCaller we could probably have MyDirectDrawSurface::MyBlt set a flag for us. although maybe this way is safer. + if(fakeDisplayValid/* && IsWindowFakeFullscreen(hWnd)*/ && VerifyIsTrustedCaller(!tls.callerisuntrusted)) { if(!lpRect) return FALSE; @@ -477,12 +480,14 @@ HOOKFUNC BOOL WINAPI MyGetClientRect(HWND hWnd, LPRECT lpRect) lpRect->top = 0; lpRect->right = fakeDisplayWidth; lpRect->bottom = fakeDisplayHeight; + return TRUE; } return GetClientRect(hWnd, lpRect); } HOOKFUNC BOOL WINAPI MyGetWindowRect(HWND hWnd, LPRECT lpRect) { - if(fakeDisplayValid && IsWindowFakeFullscreen(hWnd)) + // see coments in MyGetClientRect + if(fakeDisplayValid/* && IsWindowFakeFullscreen(hWnd)*/ && VerifyIsTrustedCaller(!tls.callerisuntrusted)) { if(!lpRect) return FALSE; @@ -490,10 +495,30 @@ HOOKFUNC BOOL WINAPI MyGetWindowRect(HWND hWnd, LPRECT lpRect) lpRect->top = 0; lpRect->right = fakeDisplayWidth; lpRect->bottom = fakeDisplayHeight; + return TRUE; } return GetWindowRect(hWnd, lpRect); } +HOOKFUNC BOOL WINAPI MyClientToScreen(HWND hWnd, LPPOINT lpPoint) +{ + // see coments in MyGetClientRect + if(fakeDisplayValid/* && IsWindowFakeFullscreen(hWnd)*/ && VerifyIsTrustedCaller(!tls.callerisuntrusted)) + { + return (lpPoint != NULL); + } + return ClientToScreen(hWnd, lpPoint); +} +HOOKFUNC BOOL WINAPI MyScreenToClient(HWND hWnd, LPPOINT lpPoint) +{ + // see coments in MyGetClientRect + if(fakeDisplayValid/* && IsWindowFakeFullscreen(hWnd)*/ && VerifyIsTrustedCaller(!tls.callerisuntrusted)) + { + return (lpPoint != NULL); + } + return ScreenToClient(hWnd, lpPoint); +} + HOOKFUNC BOOL WINAPI MySetWindowTextA(HWND hWnd, LPCSTR lpString) { debuglog(LCF_WINDOW, __FUNCTION__ "(0x%X, \"%s\") called.\n", hWnd, lpString); @@ -620,6 +645,8 @@ void ApplyWindowIntercepts() MAKE_INTERCEPT(1, USER32, ShowWindow), MAKE_INTERCEPT(1, USER32, GetClientRect), MAKE_INTERCEPT(1, USER32, GetWindowRect), + MAKE_INTERCEPT(1, USER32, ClientToScreen), + MAKE_INTERCEPT(1, USER32, ScreenToClient), MAKE_INTERCEPT(1, USER32, SetWindowTextA), MAKE_INTERCEPT(1, USER32, SetWindowTextW), //MAKE_INTERCEPT(1, USER32, InvalidateRect), diff --git a/src/wintasee/tramps/windowtramps.h b/src/wintasee/tramps/windowtramps.h index 6d754f2..524ad53 100644 --- a/src/wintasee/tramps/windowtramps.h +++ b/src/wintasee/tramps/windowtramps.h @@ -58,6 +58,10 @@ TRAMPFUNC BOOL WINAPI ShowWindow(HWND hWnd, int nCmdShow) TRAMPOLINE_DEF TRAMPFUNC BOOL WINAPI GetClientRect(HWND hWnd, LPRECT lpRect) TRAMPOLINE_DEF #define GetWindowRect TrampGetWindowRect TRAMPFUNC BOOL WINAPI GetWindowRect(HWND hWnd, LPRECT lpRect) TRAMPOLINE_DEF +#define ClientToScreen TrampClientToScreen +TRAMPFUNC BOOL WINAPI ClientToScreen(HWND hWnd, LPPOINT lpPoint) TRAMPOLINE_DEF +#define ScreenToClient TrampScreenToClient +TRAMPFUNC BOOL WINAPI ScreenToClient(HWND hWnd, LPPOINT lpPoint) TRAMPOLINE_DEF #define SetWindowTextA TrampSetWindowTextA TRAMPFUNC BOOL WINAPI SetWindowTextA(HWND hWnd, LPCSTR lpString) TRAMPOLINE_DEF #define SetWindowTextW TrampSetWindowTextW diff --git a/src/wintasee/wintasee.cpp b/src/wintasee/wintasee.cpp index f0906ba..9f23049 100644 --- a/src/wintasee/wintasee.cpp +++ b/src/wintasee/wintasee.cpp @@ -1205,7 +1205,8 @@ void FrameBoundary(void* captureInfo, int captureInfoType) #ifdef EMULATE_MESSAGE_QUEUES PostMessageInternal(hwnd, WM_KEYDOWN, i, 0); #else - SendMessage(hwnd, toggleWhitelistMessage(WM_KEYDOWN), i, 0); + //SendMessage(hwnd, toggleWhitelistMessage(WM_KEYDOWN), i, 0); + MyWndProcA(hwnd, toggleWhitelistMessage(WM_KEYDOWN), i, 0); #endif // also send a WM_CHAR event in case some games need it (HACK, should do this in TranslateMessage) @@ -1219,7 +1220,8 @@ void FrameBoundary(void* captureInfo, int captureInfoType) #ifdef EMULATE_MESSAGE_QUEUES PostMessageInternal(hwnd, WM_CHAR, c, 0); #else - SendMessage(hwnd, toggleWhitelistMessage(WM_CHAR), c, 0); + //SendMessage(hwnd, toggleWhitelistMessage(WM_CHAR), c, 0); + MyWndProcA(hwnd, toggleWhitelistMessage(WM_CHAR), c, 0); #endif } } @@ -1229,7 +1231,8 @@ void FrameBoundary(void* captureInfo, int captureInfoType) #ifdef EMULATE_MESSAGE_QUEUES PostMessageInternal(hwnd, WM_KEYUP, i, 0); #else - SendMessage(hwnd, toggleWhitelistMessage(WM_KEYUP), i, 0); + //SendMessage(hwnd, toggleWhitelistMessage(WM_KEYUP), i, 0); + MyWndProcA(hwnd, toggleWhitelistMessage(WM_KEYUP), i, 0); #endif } } diff --git a/src/wintaser/inputsetup.cpp b/src/wintaser/inputsetup.cpp index c3a9c06..a0a661a 100644 --- a/src/wintaser/inputsetup.cpp +++ b/src/wintaser/inputsetup.cpp @@ -2397,6 +2397,8 @@ void Build_Main_Menu(HMENU& MainMenu, HWND hWnd) MENU_L(ExecMultithreading, i++, Flags | ((threadMode == 2)?MF_CHECKED:MF_UNCHECKED) | (usedThreadMode==1?MF_GRAYED:0), ID_EXEC_THREADS_ALLOW, "", "&Allow (normal thread creation)", "can't set while running after wrapped threads created"); MENU_L(ExecMultithreading, i++, Flags | ((threadMode == 3)?MF_CHECKED:MF_UNCHECKED) | (usedThreadMode==1?MF_GRAYED:0), ID_EXEC_THREADS_KNOWN, "", "Allow &known threads only", "can't set while running after wrapped threads created"); MENU_L(ExecMultithreading, i++, Flags | ((threadMode == 4)?MF_CHECKED:MF_UNCHECKED) | (usedThreadMode==1?MF_GRAYED:0), ID_EXEC_THREADS_UNKNOWN, "", "Allow &unknown threads only", "can't set while running after wrapped threads created"); + MENU_L(ExecMultithreading, i++, Flags | ((threadMode == 5)?MF_CHECKED:MF_UNCHECKED) | (usedThreadMode==1?MF_GRAYED:0), ID_EXEC_THREADS_TRUSTED, "", "Allow &trusted threads only", "can't set while running after wrapped threads created"); + // Timers Submenu i = 0; diff --git a/src/wintaser/resource.h b/src/wintaser/resource.h index 69d5b98..6e371fd 100644 --- a/src/wintaser/resource.h +++ b/src/wintaser/resource.h @@ -312,11 +312,12 @@ #define ID_INPUT_BACKGROUNDINPUTS_TASER 40376 #define ID_INPUT_BACKGROUNDINPUTS_TASEE 40377 #define ID_INPUT_BACKGROUNDINPUTS_OTHER 40378 -#define ID_EXEC_THREADS_DISABLE 40380 -#define ID_EXEC_THREADS_WRAP 40381 -#define ID_EXEC_THREADS_ALLOW 40382 -#define ID_EXEC_THREADS_KNOWN 40383 -#define ID_EXEC_THREADS_UNKNOWN 40384 +#define ID_EXEC_THREADS_DISABLE 40379 +#define ID_EXEC_THREADS_WRAP 40380 +#define ID_EXEC_THREADS_ALLOW 40381 +#define ID_EXEC_THREADS_KNOWN 40382 +#define ID_EXEC_THREADS_UNKNOWN 40383 +#define ID_EXEC_THREADS_TRUSTED 40384 #define ID_EXEC_TIMERS_DISABLE 40385 #define ID_EXEC_TIMERS_SYNC 40386 #define ID_EXEC_TIMERS_ASYNC 40387 diff --git a/src/wintaser/wintaser.cpp b/src/wintaser/wintaser.cpp index 6e7f17f..3db2b58 100644 --- a/src/wintaser/wintaser.cpp +++ b/src/wintaser/wintaser.cpp @@ -2077,7 +2077,7 @@ void SendTASFlags() messageSyncMode, waitSyncMode, aviMode, - emuMode | (((recoveringStale||(fastForwardFlags&FFMODE_SOUNDSKIP))&&fastforward) ? EMUMODE_NOPLAYBUFFERS : 0), + emuMode | (((recoveringStale||(fastForwardFlags&FFMODE_SOUNDSKIP))&&fastforward) ? EMUMODE_NOPLAYBUFFERS : 0) | ((threadMode==0||threadMode==4||threadMode==5) ? EMUMODE_VIRTUALDIRECTSOUND : 0), forceWindowed, fastforward, forceSurfaceMemory, @@ -2085,7 +2085,7 @@ void SendTASFlags() audioBitsPerSecond, audioChannels, stateLoaded, - fastForwardFlags,// | (recoveringStale ? (FFMODE_FRONTSKIP|FFMODE_BACKSKIP|FFMODE_SOUNDSKIP) ? 0), + fastForwardFlags,// | (recoveringStale ? (FFMODE_FRONTSKIP|FFMODE_BACKSKIP) ? 0), initialTime, debugPrintMode, timescale, timescaleDivisor, @@ -3992,7 +3992,7 @@ void WriteAVIAudio() } } - if(aviCompressedStream /*&& aviSoundFrameCount < 30*/) + if(aviCompressedStream && (aviSoundFrameCount < 30 || aviSoundFrameCount+8 < aviFrameCount)) while(aviSoundFrameCount < aviFrameCount/*-aviEmptyFrameCount*/) aviFrameQueue->FillEmptyAudioFrame(); // in case video started before audio @@ -8402,6 +8402,11 @@ BOOL CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) tasFlagsDirty = true; CheckDialogChanges(-1); break; + case ID_EXEC_THREADS_TRUSTED: + threadMode = 5; + tasFlagsDirty = true; + CheckDialogChanges(-1); + break; case ID_EXEC_TIMERS_DISABLE: timersMode = 0; @@ -8837,7 +8842,7 @@ BOOL CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) //CheckDlgButton(hDlg, IDC_AVIVIDEO, aviMode & 1); //CheckDlgButton(hDlg, IDC_AVIAUDIO, aviMode & 2); bool wasPlayback = playback; - TerminateDebuggerThread(6500); + TerminateDebuggerThread(12000); if(unsaved) SaveMovieToFile(moviefilename); terminateRequest = false; @@ -9121,7 +9126,7 @@ BOOL CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) SetWindowText(GetDlgItem(hDlg, IDC_EDIT_EXE), filename); SendMessage(GetDlgItem(hDlg, IDC_EDIT_EXE), EM_SETSEL, -2, -1); } - else if(!stricmp(dot, ".wtf")) // windows TAS file (input movie) + else if(!_strnicmp(dot, ".wtf", 4)) // windows TAS file (input movie) { SetWindowText(GetDlgItem(hDlg, IDC_EDIT_MOVIE), filename); SendMessage(GetDlgItem(hDlg, IDC_EDIT_MOVIE), EM_SETSEL, -2, -1);