HOOKFUNC VOID WINAPI MySleep(DWORD dwMilliseconds) { BOOL isFrameThread = tls_IsPrimaryThread();//tls.isFrameThread; ENTER(dwMilliseconds); //if(dwMilliseconds && s_frameThreadId == GetCurrentThreadId()) // main thread waiting? if (dwMilliseconds && isFrameThread) { // add a delay for framerate adjustment and to take large sleeps into account. // // we ignore <5 millisecond sleeps here because they can accumulate in our timer // // to make the game run at a lower framerate than it naturally does. // if(dwMilliseconds >= 5) if (VerifyIsTrustedCaller(!tls.callerisuntrusted)) detTimer.AddDelay(dwMilliseconds, TRUE, TRUE); dwMilliseconds = 0; //verbosedebugprintf("DENIED: transferred to timer\n"); LOG() << "sleep transferred to timer."; return; } //if(dwMilliseconds>0 && tasflags.fastForward) // fast-forward? //{ // dwMilliseconds = 0; // // verbosedebugprintf("DENIED because of fast-forward\n"); //} { Sleep(dwMilliseconds); } }
HOOKFUNC BOOL WINAPI MyStretchBlt(HDC hdcDest, int nXOriginDest, int nYOriginDest, int nWidthDest, int nHeightDest, HDC hdcSrc, int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc, DWORD dwRop) { bool isFrameBoundary = false; if (!usingSDLOrDD /*&& !inPauseHandler*/ && !redrawingScreen) { if (dwRop == SRCCOPY) { HWND hwnd = WindowFromDC(hdcDest); if (hwnd /*&& !hwndRespondingToPaintMessage[hwnd]*/) { if ((/*s_gdiPhaseDetector.AdvanceAndCheckCycleBoundary(MAKELONG(nXOriginDest,nYOriginDest)) ||*/ tls.peekedMessage) && VerifyIsTrustedCaller(!tls.callerisuntrusted)) { if ((nWidthSrc >= gdiFrameBigEnoughWidth && nHeightSrc >= gdiFrameBigEnoughHeight) || HDCSizeBigEnoughForFrameBoundary(hdcSrc)) { isFrameBoundary = true; } } } } } ENTER(); BOOL rv = TRUE; if (!ShouldSkipDrawing(false, WindowFromDC(hdcDest) != 0)) { if (s_gdiPendingRefresh && !redrawingScreen) { redrawingScreen = true; RedrawScreenGDI(); redrawingScreen = false; } rv = StretchBlt(hdcDest, nXOriginDest, nYOriginDest, nWidthDest, nHeightDest, hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc, dwRop); } else s_gdiPendingRefresh = true; if (isFrameBoundary) { tls.peekedMessage = FALSE; if (!(tasflags.aviMode & 1)) FrameBoundary(NULL, CAPTUREINFO_TYPE_NONE); else FrameBoundaryHDCtoAVI(hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc); s_hdcSrcSaved = hdcSrc; s_hdcDstSaved = hdcDest; } return rv; }
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 MyGetWindowRect(HWND hWnd, LPRECT lpRect) { // see coments in MyGetClientRect if(fakeDisplayValid/* && IsWindowFakeFullscreen(hWnd)*/ && VerifyIsTrustedCaller(!tls.callerisuntrusted)) { if(!lpRect) return FALSE; lpRect->left = 0; lpRect->top = 0; lpRect->right = fakeDisplayWidth; lpRect->bottom = fakeDisplayHeight; return TRUE; } return GetWindowRect(hWnd, lpRect); }
HOOKFUNC BOOL WINAPI MyGetClientRect(HWND hWnd, LPRECT lpRect) { // 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; lpRect->left = 0; lpRect->top = 0; lpRect->right = fakeDisplayWidth; lpRect->bottom = fakeDisplayHeight; return TRUE; } return GetClientRect(hWnd, lpRect); }
HOOKFUNC VOID WINAPI MySleepEx(DWORD dwMilliseconds, BOOL bAlertable) { ENTER(dwMilliseconds, bAlertable); BOOL isFrameThread = tls_IsPrimaryThread();//tls.isFrameThread; //if(dwMilliseconds && s_frameThreadId == GetCurrentThreadId()) // main thread waiting? if (dwMilliseconds && isFrameThread && !(bAlertable || !VerifyIsTrustedCaller(!tls.callerisuntrusted))) { // add a delay for framerate adjustment and to take large sleeps into account. // // we ignore <5 millisecond sleeps here because they can accumulate in our timer // // to make the game run at a lower framerate than it naturally does. // if(dwMilliseconds >= 5) detTimer.AddDelay(dwMilliseconds, TRUE, TRUE); dwMilliseconds = 0; //verbosedebugprintf("DENIED: transferred to timer\n"); return; } //if(dwMilliseconds>0 && tasflags.fastForward) // fast-forward? //{ // dwMilliseconds = 0; // // verbosedebugprintf("DENIED because of fast-forward\n"); //} // not needed anymore now that joypad functions like joyGetPosEx are replaced. // might become a problem again later in which case this can be re-enabled. //// hack for rotategear intro in wrapped thread mode //// TODO (look into): happens because the joypad? thread sleeps for 60 seconds (why?) //// and the bug is that somehow that blocks the main thread for that full duration //if(!isFrameThread && dwMilliseconds > 1000 && framecount < 30) //{ // dwMilliseconds -= ((dwMilliseconds - 1000) / 32) * 31; //} { //debugprintf("sleepex: %d\n", dwMilliseconds); SleepEx(dwMilliseconds, bAlertable); } }
void TransferWait(DWORD& dwMilliseconds/*, bool rly=true*/) { #if 0 if (tls.isFrameThread) // only apply this to the main thread { if (dwMilliseconds < 500 && tasflags.fastForward) dwMilliseconds = 0; } return; #endif // basically, we have to make it so we're in control of the waits, not the game. // otherwise the game will run too slowly or (at best) won't be fast-forward capable. // don't enter a waiting state if the timeout is infinite or greater than some amount // (the amount chosen might affect sync) FIXME: amounts 500 or higher cause random desyncs... should clamp down better on what can call WaitForSingleObject/WaitForMultipleObjects if (dwMilliseconds != INFINITE && dwMilliseconds < 100) { // the only sync-safe thing we can do is assume the full time-out time always elapses. // luckily, most games that trigger this in the main thread will // pass in a time-out time that exactly corresponds with their desired framerate. if (dwMilliseconds > 0) { // if(tls.isFrameThread && !tls.callerisuntrusted) // only apply this to the main thread if (tls_IsPrimaryThread() && VerifyIsTrustedCaller(!tls.callerisuntrusted)) // only apply this to the main thread { // add the delay logically but don't physically wait yet // if(rly){ //detTimer.AddDelay(dwMilliseconds, FALSE, tasflags.waitSyncMode<2, TRUE); // attempted sync hack detTimer.AddDelay(dwMilliseconds, FALSE, TRUE, TRUE); // FIXME: might desync? //detTimer.AddDelay(dwMilliseconds, FALSE, FALSE, TRUE); // FIXME... sync hack // } // if in fast-forward mode, nullify the physical wait (if any remains) // if(tasflags.fastForward && (tasflags.fastForwardFlags & FFMODE_WAITSKIP)) dwMilliseconds = 0; // because of this, we have to be careful not to accidentally call this function twice in a row. // for example, if we hook both WaitForSingleObject and WaitForSingleObjectEx, // and WaitForSingleObject sometimes calls WaitForSingleObjectEx, // that would make fast-forward cause desyncs. } } } }
HOOKFUNC int WINAPI MyStretchDIBits(HDC hdc, int xDest, int yDest, int DestWidth, int DestHeight, int xSrc, int ySrc, int SrcWidth, int SrcHeight, CONST VOID * lpBits, CONST BITMAPINFO * lpbmi, UINT iUsage, DWORD rop) { int rv = StretchDIBits(hdc, xDest, yDest, DestWidth, DestHeight, xSrc, ySrc, SrcWidth, SrcHeight, lpBits, lpbmi, iUsage, rop); if (!usingSDLOrDD /*&& !inPauseHandler*/ && !redrawingScreen) { HWND hwnd = WindowFromDC(hdc); if (hwnd /*&& !hwndRespondingToPaintMessage[hwnd]*/) { if (rv != 0 && rv != GDI_ERROR && rop == SRCCOPY && (/*s_gdiPhaseDetector.AdvanceAndCheckCycleBoundary(MAKELONG(xDest,yDest)) ||*/ tls.peekedMessage) && VerifyIsTrustedCaller(!tls.callerisuntrusted)) { if (!(tasflags.aviMode & 1)) FrameBoundary(NULL, CAPTUREINFO_TYPE_NONE); else FrameBoundaryDIBitsToAVI(lpBits, *lpbmi); tls.peekedMessage = FALSE; } } } return rv; }
HOOKFUNC int WINAPI MySetDIBitsToDevice(HDC hdc, int xDest, int yDest, DWORD w, DWORD h, int xSrc, int ySrc, UINT StartScan, UINT cLines, CONST VOID * lpvBits, CONST BITMAPINFO * lpbmi, UINT ColorUse) { int rv = SetDIBitsToDevice(hdc, xDest, yDest, w, h, xSrc, ySrc, StartScan, cLines, lpvBits, lpbmi, ColorUse); if (!usingSDLOrDD /*&& !inPauseHandler*/ && !redrawingScreen) { HWND hwnd = WindowFromDC(hdc); if (hwnd /*&& !hwndRespondingToPaintMessage[hwnd]*/) { if (rv != 0 && rv != GDI_ERROR && (/*s_gdiPhaseDetector.AdvanceAndCheckCycleBoundary(MAKELONG(xDest,yDest)) ||*/ tls.peekedMessage) && VerifyIsTrustedCaller(!tls.callerisuntrusted)) { if (!(tasflags.aviMode & 1)) FrameBoundary(NULL, CAPTUREINFO_TYPE_NONE); else FrameBoundaryDIBitsToAVI(lpvBits, *lpbmi); tls.peekedMessage = FALSE; } } } return rv; }
HOOKFUNC BOOL WINAPI MyBitBlt(HDC hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, HDC hdcSrc, int nXSrc, int nYSrc, DWORD dwRop) { bool isFrameBoundary = false; if (!usingSDLOrDD /*&& !inPauseHandler*/ && !redrawingScreen) { if (dwRop == SRCCOPY) { HWND hwnd = WindowFromDC(hdcDest); if (hwnd /*&& !hwndRespondingToPaintMessage[hwnd]*/) { if ((/*s_gdiPhaseDetector.AdvanceAndCheckCycleBoundary(MAKELONG(nXDest,nYDest)) ||*/ tls.peekedMessage) && VerifyIsTrustedCaller(!tls.callerisuntrusted)) { if ((nWidth >= gdiFrameBigEnoughWidth && nHeight >= gdiFrameBigEnoughHeight) || HDCSizeBigEnoughForFrameBoundary(hdcSrc)) { isFrameBoundary = true; } } } } } ENTER(hdcDest, nXDest, nYDest, nWidth, nHeight, hdcSrc, nXSrc, nYSrc, dwRop); BOOL rv = TRUE; if (!ShouldSkipDrawing(false, WindowFromDC(hdcDest) != 0)) { if (s_gdiPendingRefresh && !redrawingScreen) { redrawingScreen = true; RedrawScreenGDI(); redrawingScreen = false; } if (!fakeDisplayValid) { rv = BitBlt(hdcDest, nXDest, nYDest, nWidth, nHeight, hdcSrc, nXSrc, nYSrc, dwRop); } else { HWND hwnd = WindowFromDC(hdcDest); 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 (realRect.right > fakeDisplayWidth || realRect.bottom > fakeDisplayHeight) { // sidestep clip region (it can't be expanded without switching HDCs) hdcTemp = GetDC(hwnd); hdc = hdcTemp; } // FIXME this feature actually broke, it's drawing at 100% size again no matter the window size, // it probably broke because of extra hooking of functions like GetClientRect. 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); } } } else s_gdiPendingRefresh = true; if (isFrameBoundary) { tls.peekedMessage = FALSE; if (!(tasflags.aviMode & 1)) FrameBoundary(NULL, CAPTUREINFO_TYPE_NONE); else FrameBoundaryHDCtoAVI(hdcSrc, nXSrc, nYSrc, nWidth, nHeight); s_hdcSrcSaved = hdcSrc; s_hdcDstSaved = hdcDest; } return rv; }