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;
    }