/*========================================================================== * * Copyright (c) 1995 - 1997 Microsoft Corporation. All Rights Reserved. * Copyright (C) 1994-1995 ATI Technologies Inc. All Rights Reserved. * * File: winfox.c * Content: Windows fox sample game * ***************************************************************************/ #include "foxbear.h" #include "rcids.h" // for FOX_ICON LPDIRECTDRAWSURFACE lpFrontBuffer; LPDIRECTDRAWSURFACE lpBackBuffer; LPDIRECTDRAWCLIPPER lpClipper; LPDIRECTDRAWSURFACE lpStretchBuffer; LPDIRECTDRAWSURFACE lpFrameRate; LPDIRECTDRAWSURFACE lpInfo; LPDIRECTDRAWPALETTE lpPalette; LPDIRECTDRAW lpDD; SHORT lastInput = 0; HWND hWndMain; RECT rcWindow; BOOL bShowFrameCount=TRUE; BOOL bIsActive; BOOL bPaused; BOOL bStretch; BOOL bFullscreen=TRUE; BOOL bStress=FALSE; // just keep running if true BOOL bHelp=FALSE; // help requested RECT GameRect; // game rect SIZE GameSize; // game is this size SIZE GameMode; // display mode size UINT GameBPP; // the bpp we want DWORD dwColorKey; // our color key DWORD AveFrameRate; DWORD AveFrameRateCount; BOOL bWantSound = TRUE; #define OUR_APP_NAME "Win Fox Application" #define ODS OutputDebugString BOOL InitGame(void); void ExitGame(void); void initNumSurface(void); /* * PauseGame() */ void PauseGame() { Msg("**** PAUSE"); bPaused = TRUE; InvalidateRect(hWndMain, NULL, TRUE); } /* * UnPauseGame() */ void UnPauseGame() { if (GetForegroundWindow() == hWndMain) { Msg("**** UNPAUSE"); bPaused = FALSE; } } /* * RestoreGame() */ BOOL RestoreGame() { if (lpFrontBuffer == NULL || IDirectDrawSurface_Restore(lpFrontBuffer) != DD_OK) { Msg("***** cant restore FrontBuffer"); return FALSE; } if (!bFullscreen) { if (lpBackBuffer == NULL || IDirectDrawSurface_Restore(lpBackBuffer) != DD_OK) { Msg("***** cant restore BackBuffer"); return FALSE; } } if (lpStretchBuffer && IDirectDrawSurface_Restore(lpStretchBuffer) != DD_OK) { Msg("***** cant restore StretchBuffer"); return FALSE; } if (lpFrameRate == NULL || lpInfo == NULL || IDirectDrawSurface_Restore(lpFrameRate) != DD_OK || IDirectDrawSurface_Restore(lpInfo) != DD_OK) { Msg("***** cant restore frame rate stuff"); return FALSE; } initNumSurface(); if (!gfxRestoreAll()) { Msg("***** cant restore art"); return FALSE; } return TRUE; } /* * ProcessFox */ BOOL ProcessFox(SHORT sInput) { if ((lpFrontBuffer && IDirectDrawSurface_IsLost(lpFrontBuffer) == DDERR_SURFACELOST) || (lpBackBuffer && IDirectDrawSurface_IsLost(lpBackBuffer) == DDERR_SURFACELOST)) { if (!RestoreGame()) { PauseGame(); return FALSE; } } ProcessInput(sInput); NewGameFrame(); return TRUE; } /* ProcessFox */ static HFONT hFont; DWORD dwFrameCount; DWORD dwFrameTime; DWORD dwFrames; DWORD dwFramesLast; SIZE sizeFPS; SIZE sizeINFO; int FrameRateX; char szFPS[] = "FPS %02d"; char szINFO[] = "%dx%dx%d%s F6=mode F8=x2 ALT+ENTER=Window"; char szINFOW[] = "%dx%dx%d%s F6=mode F8=x2 ALT+ENTER=Fullscreen"; char szFrameRate[128]; char szInfo[128]; COLORREF InfoColor = RGB(0,152,245); COLORREF FrameRateColor = RGB(255,255,0); COLORREF BackColor = RGB(255,255,255); /* * initNumSurface */ void initNumSurface( void ) { HDC hdc; RECT rc; int len; dwFramesLast = 0; len = wsprintf(szFrameRate, szFPS, 0, 0); if( lpFrameRate && IDirectDrawSurface_GetDC(lpFrameRate, &hdc ) == DD_OK ) { SelectObject(hdc, hFont); SetTextColor(hdc, FrameRateColor); SetBkColor(hdc, BackColor); SetBkMode(hdc, OPAQUE); SetRect(&rc, 0, 0, 10000, 10000); ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, szFrameRate, len, NULL); GetTextExtentPoint(hdc, szFrameRate, 4, &sizeFPS); FrameRateX = sizeFPS.cx; GetTextExtentPoint(hdc, szFrameRate, len, &sizeFPS); IDirectDrawSurface_ReleaseDC(lpFrameRate, hdc); } if (bFullscreen) len = wsprintf(szInfo, szINFO, GameSize.cx, GameSize.cy, GameBPP,bStretch ? " x2" : ""); else len = wsprintf(szInfo, szINFOW, GameSize.cx, GameSize.cy, GameBPP,bStretch ? " x2" : ""); if( lpInfo && IDirectDrawSurface_GetDC(lpInfo, &hdc ) == DD_OK ) { SelectObject(hdc, hFont); SetTextColor(hdc, InfoColor); SetBkColor(hdc, BackColor); SetBkMode(hdc, OPAQUE); SetRect(&rc, 0, 0, 10000, 10000); ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, szInfo, len, NULL); GetTextExtentPoint(hdc, szInfo, len, &sizeINFO); IDirectDrawSurface_ReleaseDC(lpInfo, hdc); } } /* initNumSurface */ /* * makeFontStuff */ static BOOL makeFontStuff( void ) { DDCOLORKEY ddck; HDC hdc; if (hFont != NULL) { DeleteObject(hFont); } hFont = CreateFont( GameSize.cx <= 512 ? 12 : 24, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, NONANTIALIASED_QUALITY, // DEFAULT_QUALITY, VARIABLE_PITCH, "Arial" ); /* * make a sample string so we can measure it with the current font. */ initNumSurface(); hdc = GetDC(NULL); SelectObject(hdc, hFont); GetTextExtentPoint(hdc, szFrameRate, lstrlen(szFrameRate), &sizeFPS); GetTextExtentPoint(hdc, szInfo, lstrlen(szInfo), &sizeINFO); ReleaseDC(NULL, hdc); /* * Create a surface to copy our bits to. */ lpFrameRate = DDCreateSurface(sizeFPS.cx, sizeFPS.cy, FALSE,TRUE); lpInfo = DDCreateSurface(sizeINFO.cx, sizeINFO.cy, FALSE,TRUE); if( lpFrameRate == NULL || lpInfo == NULL ) { return FALSE; } /* * now set the color key, we use a totaly different color than * the rest of the app, just to be different so drivers dont always * get white or black as the color key... * * dont forget when running on a dest colorkey device, we need * to use the same color key as the rest of the app. */ if( bTransDest ) BackColor = RGB(255,255,255); else BackColor = RGB(128,64,255); ddck.dwColorSpaceLowValue = DDColorMatch(lpInfo, BackColor); ddck.dwColorSpaceHighValue = ddck.dwColorSpaceLowValue; IDirectDrawSurface_SetColorKey( lpInfo, DDCKEY_SRCBLT, &ddck); IDirectDrawSurface_SetColorKey( lpFrameRate, DDCKEY_SRCBLT, &ddck); /* * now draw the text for real */ initNumSurface(); return TRUE; } /* * DisplayFrameRate */ void DisplayFrameRate( void ) { DWORD time2; char buff[256]; HDC hdc; HRESULT ddrval; RECT rc; DWORD dw; if( !bShowFrameCount ) { return; } dwFrameCount++; time2 = timeGetTime() - dwFrameTime; if( time2 > 1000 ) { dwFrames = (dwFrameCount*1000)/time2; dwFrameTime = timeGetTime(); dwFrameCount = 0; AveFrameRate += dwFrames; AveFrameRateCount++; } if( dwFrames == 0 ) { return; } if( dwFrames != dwFramesLast ) { dwFramesLast = dwFrames; if( IDirectDrawSurface_GetDC(lpFrameRate, &hdc ) == DD_OK ) { buff[0] = (char)((dwFrames / 10) + '0'); buff[1] = (char)((dwFrames % 10) + '0'); SelectObject(hdc, hFont); SetTextColor(hdc, FrameRateColor); SetBkColor(hdc, BackColor); TextOut(hdc, FrameRateX, 0, buff, 2); IDirectDrawSurface_ReleaseDC(lpFrameRate, hdc); } } /* * put the text on the back buffer. */ if (bTransDest) dw = DDBLTFAST_DESTCOLORKEY | DDBLTFAST_WAIT; else dw = DDBLTFAST_SRCCOLORKEY | DDBLTFAST_WAIT; SetRect(&rc, 0, 0, sizeFPS.cx, sizeFPS.cy); ddrval = IDirectDrawSurface_BltFast(lpBackBuffer, GameRect.left + (GameSize.cx - sizeFPS.cx)/2, GameRect.top + 20, lpFrameRate, &rc, dw); SetRect(&rc, 0, 0, sizeINFO.cx, sizeINFO.cy); ddrval = IDirectDrawSurface_BltFast(lpBackBuffer, GameRect.left + 10, GameRect.bottom - sizeINFO.cy - 10, lpInfo, &rc, dw); } /* DisplayFrameRate */ /* * MainWndProc * * Callback for all Windows messages */ long FAR PASCAL MainWndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) { PAINTSTRUCT ps; HDC hdc; int i; switch( message ) { case WM_SIZE: case WM_MOVE: if (IsIconic(hWnd)) { Msg("FoxBear is minimized, pausing"); PauseGame(); } if (bFullscreen) { SetRect(&rcWindow, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN)); } else { GetClientRect(hWnd, &rcWindow); ClientToScreen(hWnd, (LPPOINT)&rcWindow); ClientToScreen(hWnd, (LPPOINT)&rcWindow+1); } Msg("WINDOW RECT: [%d,%d,%d,%d]", rcWindow.left, rcWindow.top, rcWindow.right, rcWindow.bottom); break; case WM_ACTIVATEAPP: bIsActive = (BOOL)wParam && GetForegroundWindow() == hWnd; if (bIsActive) Msg("FoxBear is active"); else Msg("FoxBear is not active"); // // while we were not-active something bad happened that caused us // to pause, like a surface restore failing or we got a palette // changed, now that we are active try to fix things // if (bPaused && bIsActive) { if (RestoreGame()) { UnPauseGame(); } else { if (GetForegroundWindow() == hWnd) { // // we are unable to restore, this can happen when // the screen resolution or bitdepth has changed // we just reload all the art again and re-create // the front and back buffers. this is a little // overkill we could handle a screen res change by // just recreating the front and back buffers we dont // need to redo the art, but this is way easier. // if (InitGame()) { UnPauseGame(); } } } } break; case WM_QUERYNEWPALETTE: // // we are getting the palette focus, select our palette // if (!bFullscreen && lpPalette && lpFrontBuffer) { HRESULT ddrval; ddrval = IDirectDrawSurface_SetPalette(lpFrontBuffer,lpPalette); if( ddrval == DDERR_SURFACELOST ) { IDirectDrawSurface_Restore( lpFrontBuffer ); ddrval= IDirectDrawSurface_SetPalette(lpFrontBuffer,lpPalette); if( ddrval == DDERR_SURFACELOST ) { Msg(" Failed to restore palette after second try"); } } // // Restore normal title if palette is ours // if( ddrval == DD_OK ) { SetWindowText( hWnd, OUR_APP_NAME ); } } break; case WM_PALETTECHANGED: // // if another app changed the palette we dont have full control // of the palette. NOTE this only applies for FoxBear in a window // when we are fullscreen we get all the palette all of the time. // if ((HWND)wParam != hWnd) { if( !bFullscreen ) { if( !bStress ) { Msg("***** PALETTE CHANGED, PAUSING GAME"); PauseGame(); } else { Msg("Lost palette but continuing"); SetWindowText( hWnd, OUR_APP_NAME " - palette changed COLORS PROBABLY WRONG" ); } } } break; case WM_DISPLAYCHANGE: break; case WM_CREATE: break; case WM_SETCURSOR: if (bFullscreen && bIsActive) { SetCursor(NULL); return TRUE; } break; case WM_SYSKEYUP: switch( wParam ) { // handle ALT+ENTER (fullscreen) case VK_RETURN: bFullscreen = !bFullscreen; ExitGame(); DDDisable(TRUE); // destroy DirectDraw object InitGame(); break; } break; case WM_KEYDOWN: switch( wParam ) { case VK_NUMPAD5: lastInput=KEY_STOP; break; case VK_DOWN: case VK_NUMPAD2: lastInput=KEY_DOWN; break; case VK_LEFT: case VK_NUMPAD4: lastInput=KEY_LEFT; break; case VK_RIGHT: case VK_NUMPAD6: lastInput=KEY_RIGHT; break; case VK_UP: case VK_NUMPAD8: lastInput=KEY_UP; break; case VK_HOME: case VK_NUMPAD7: lastInput=KEY_JUMP; break; case VK_NUMPAD3: lastInput=KEY_THROW; break; case VK_F5: bShowFrameCount = !bShowFrameCount; if( bShowFrameCount ) { dwFrameCount = 0; dwFrameTime = timeGetTime(); } break; case VK_F6: // // find our current mode in the mode list // if(bFullscreen) { for (i=0; i= NumModes) i = 0; Msg("ModeList %d %d",i,NumModes); GameMode.cx = ModeList[i].w; GameMode.cy = ModeList[i].h; GameBPP = ModeList[i].bpp; bStretch = FALSE; InitGame(); break; case VK_F7: GameBPP = GameBPP == 8 ? 16 : 8; InitGame(); break; case VK_F8: if (bFullscreen) { bStretch = !bStretch; InitGame(); } else { RECT rc; GetClientRect(hWnd, &rc); bStretch = (rc.right != GameSize.cx) || (rc.bottom != GameSize.cy); if (bStretch = !bStretch) SetRect(&rc, 0, 0, GameMode.cx*2, GameMode.cy*2); else SetRect(&rc, 0, 0, GameMode.cx, GameMode.cy); AdjustWindowRectEx(&rc, GetWindowStyle(hWnd), GetMenu(hWnd) != NULL, GetWindowExStyle(hWnd)); SetWindowPos(hWnd, NULL, 0, 0, rc.right-rc.left, rc.bottom-rc.top, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); } break; case VK_F4: // treat F4 like ALT+ENTER (fullscreen) PostMessage(hWnd, WM_SYSKEYUP, VK_RETURN, 0); break; case VK_F3: bPaused = !bPaused; break; case VK_ESCAPE: case VK_F12: PostMessage(hWnd, WM_CLOSE, 0, 0); return 0; } break; case WM_PAINT: hdc = BeginPaint( hWnd, &ps ); if (bPaused) { char *sz = "Game is paused, this is not a bug."; TextOut(ps.hdc, 0, 0, sz, lstrlen(sz)); } EndPaint( hWnd, &ps ); return 1; case WM_DESTROY: hWndMain = NULL; lastInput=0; DestroyGame(); // end of game DDDisable(TRUE); // destroy DirectDraw object PostQuitMessage( 0 ); break; } return DefWindowProc(hWnd, message, wParam, lParam); } /* MainWndProc */ /* * initApplication * * Do that Windows initialization stuff... */ static BOOL initApplication( HINSTANCE hInstance, int nCmdShow ) { WNDCLASS wc; BOOL rc; wc.style = CS_DBLCLKS; wc.lpfnWndProc = MainWndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon( hInstance, MAKEINTATOM(FOX_ICON)); wc.hCursor = LoadCursor( NULL, IDC_ARROW ); wc.hbrBackground = GetStockObject(BLACK_BRUSH); wc.lpszMenuName = NULL; wc.lpszClassName = "WinFoxClass"; rc = RegisterClass( &wc ); if( !rc ) { return FALSE; } hWndMain = CreateWindowEx( WS_EX_APPWINDOW, "WinFoxClass", OUR_APP_NAME, WS_VISIBLE | // so we dont have to call ShowWindow WS_SYSMENU | // so we get a icon in in our tray button WS_POPUP, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), NULL, NULL, hInstance, NULL ); if( !hWndMain ) { return FALSE; } UpdateWindow( hWndMain ); SetFocus( hWndMain ); return TRUE; } /* initApplication */ /* * ExitGame * * Exiting current game, clean up */ void ExitGame( void ) { if( lpFrameRate ) { IDirectDrawSurface_Release(lpFrameRate); lpFrameRate = NULL; } if( lpInfo ) { IDirectDrawSurface_Release(lpInfo); lpInfo = NULL; } if( lpPalette ) { IDirectDrawSurface_Release(lpPalette); lpPalette = NULL; } DestroyGame(); } /* ExitGame */ /* * InitGame * * Initializing current game */ BOOL InitGame( void ) { ExitGame(); GameSize = GameMode; /* * initialize sound */ InitSound( hWndMain ); /* * init DirectDraw, set mode, ... * NOTE GameMode might be set to 640x480 if we cant get the asked for mode. */ if( !PreInitializeGame() ) { return FALSE; } if (bStretch && bFullscreen) { GameSize.cx = GameMode.cx / 2; GameSize.cy = GameMode.cy / 2; GameRect.left = GameMode.cx - GameSize.cx; GameRect.top = GameMode.cy - GameSize.cy; GameRect.right = GameMode.cx; GameRect.bottom = GameMode.cy; if (lpStretchBuffer) Msg("Stretching using a system-memory stretch buffer"); else Msg("Stretching using a VRAM->VRAM blt"); } else { GameRect.left = (GameMode.cx - GameSize.cx) / 2; GameRect.top = (GameMode.cy - GameSize.cy) / 2; GameRect.right = GameRect.left + GameSize.cx; GameRect.bottom = GameRect.top + GameSize.cy; } /* * setup our palette */ if( GameBPP == 8 ) { lpPalette = ReadPalFile( NULL ); // create a 332 palette if( lpPalette == NULL ) { Msg( "Palette create failed" ); return FALSE; } IDirectDrawSurface_SetPalette( lpFrontBuffer, lpPalette ); } /* * load all the art and things. */ if( !InitializeGame() ) { return FALSE; } /* * init our code to draw the FPS */ makeFontStuff(); /* * spew some stats */ { DDCAPS ddcaps; ddcaps.dwSize = sizeof( ddcaps ); IDirectDraw_GetCaps( lpDD, &ddcaps, NULL ); Msg( "Total=%ld, Free VRAM=%ld", ddcaps.dwVidMemTotal, ddcaps.dwVidMemFree ); Msg( "Used = %ld", ddcaps.dwVidMemTotal- ddcaps.dwVidMemFree ); } return TRUE; } /* InitGame */ #define IS_NUM(c) ((c) >= '0' && (c) <= '9') #define IS_SPACE(c) ((c) == ' ' || (c) == '\r' || (c) == '\n' || (c) == '\t' || (c) == 'x') int getint(char**p, int def) { int i=0; while (IS_SPACE(**p)) (*p)++; if (!IS_NUM(**p)) return def; while (IS_NUM(**p)) i = i*10 + *(*p)++ - '0'; while (IS_SPACE(**p)) (*p)++; return i; } /* * WinMain */ int PASCAL WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) { MSG msg; while( lpCmdLine[0] == '-' ) { int iMoviePos = 0; BOOL bDone = FALSE; lpCmdLine++; switch (*lpCmdLine++) { case 'c': bCamera = TRUE; break; case 'm': bMovie = TRUE; for (;!bDone;) { switch (*lpCmdLine) { case ' ': case 0: case '-': case '\n': case '\r': bDone = TRUE; break; default: wszMovie[iMoviePos++] = (unsigned char)*lpCmdLine++; break; } } wszMovie[iMoviePos] = 0; break; case 'e': bUseEmulation = TRUE; break; case 'w': bFullscreen = FALSE; break; case 'f': bFullscreen = TRUE; break; case '1': CmdLineBufferCount = 1; break; case '2': case 'd': CmdLineBufferCount = 2; break; case '3': CmdLineBufferCount = 3; break; case 's': bStretch = TRUE; break; case 'S': bWantSound = FALSE; break; case 'x': bStress= TRUE; break; case '?': bHelp= TRUE; bFullscreen= FALSE; // give help in windowed mode break; } while( IS_SPACE(*lpCmdLine) ) { lpCmdLine++; } } GameMode.cx = getint(&lpCmdLine, 640); GameMode.cy = getint(&lpCmdLine, 480); GameBPP = getint(&lpCmdLine, 8); /* * create window and other windows things */ if( !initApplication(hInstance, nCmdShow) ) { return FALSE; } /* * Give user help if asked for * * This is ugly for now because the whole screen is black * except for the popup box. This could be fixed with some * work to get the window size right when it was created instead * of delaying that work. see ddraw.c * */ if( bHelp ) { MessageBox(hWndMain, "F12 - Quit\n" "NUMPAD 2 - crouch\n" "NUMPAD 3 - apple\n" "NUMPAD 4 - right\n" "NUMPAD 5 - stop\n" "NUMPAD 6 - left\n" "NUMPAD 7 - jump\n" "\n" "Command line parameters\n" "\n" "-e Use emulator\n" "-S No Sound\n" "-1 No backbuffer\n" "-2 One backbuffer\n" "-4 Three backbuffers\n" "-s Use stretch\n" "-x Demo or stress mode\n" "-mfoo.bar Movie name\n" "-c Overlay camera input\n", OUR_APP_NAME, MB_OK ); } /* * initialize for game play */ if( !InitGame() ) { return FALSE; } dwFrameTime = timeGetTime(); while( 1 ) { if (PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE)) { if (!GetMessage( &msg, NULL, 0, 0)) { break; } TranslateMessage(&msg); DispatchMessage(&msg); } else if (!bPaused && (bIsActive || !bFullscreen)) { ProcessFox(lastInput); lastInput=0; } else { WaitMessage(); } } if (AveFrameRateCount) { AveFrameRate = AveFrameRate / AveFrameRateCount; Msg("Average frame rate: %d", AveFrameRate); } return msg.wParam; } /* WinMain */ #ifdef DEBUG /* * Msg */ void __cdecl Msg( LPSTR fmt, ... ) { char buff[256]; va_list va; va_start(va, fmt); // // format message with header // lstrcpy( buff, "FOXBEAR:" ); wvsprintf( &buff[lstrlen(buff)], fmt, va ); lstrcat( buff, "\r\n" ); // // To the debugger unless we need to be quiet // if( !bStress ) { OutputDebugString( buff ); } } /* Msg */ #endif