/* Copyright (C) 1997-2001 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // sys_win.h #include "../qcommon/qcommon.h" #include "winquake.h" #include "resource.h" #include #include #include #include #include #include #include #include #include "../win32/conproc.h" #define MINIMUM_WIN_MEMORY 0x0a00000 #define MAXIMUM_WIN_MEMORY 0x1000000 qboolean s_win95; int starttime; qboolean ActiveApp; qboolean Minimized; static HANDLE hinput, houtput; unsigned sys_msg_time; unsigned sys_frame_time; static HANDLE qwclsemaphore; #define MAX_NUM_ARGVS 128 int argc; char *argv[MAX_NUM_ARGVS]; /* =============================================================================== SYSTEM IO =============================================================================== */ void Sys_Error (char *error, ...) { va_list argptr; char text[1024]; CL_Shutdown (); va_start (argptr, error); vsnprintf (text, sizeof(text), error, argptr); va_end (argptr); MessageBox(NULL, text, "Error", 0 /* MB_OK */ ); if (qwclsemaphore) CloseHandle (qwclsemaphore); // shut down QHOST hooks if necessary DeinitConProc (); Qcommon_Shutdown (); exit (1); } void Sys_Quit (void) { timeEndPeriod( 1 ); CL_Shutdown(); CloseHandle (qwclsemaphore); if (dedicated && dedicated->integer) FreeConsole (); // shut down QHOST hooks if necessary DeinitConProc (); Qcommon_Shutdown (); exit (0); } //================================================================ /* ================ Sys_Milliseconds ================ */ // wsw: pb adapted High Res Performance Counter code from ezquake static __int64 freq; static qboolean hwtimer = qfalse; unsigned int curtime; static void Sys_InitTime(void) { // disabled by now if(COM_CheckParm("-hwtimer") && QueryPerformanceFrequency((LARGE_INTEGER *)&freq) && freq > 0) { // hardware timer available Com_Printf("Using High Resolution Performance Counter\n"); // QueryPerformanceCounter()/freq will give us seconds hwtimer = qtrue; } else { Com_Printf("Using timeGetTime\n"); } } static void Sys_Milliseconds_QPC(void) { static unsigned __int64 startcount; static qboolean first = qtrue; unsigned __int64 pcount; QueryPerformanceCounter ((LARGE_INTEGER *)&pcount); if (first) { first = qfalse; startcount=pcount; curtime=0; } curtime=((pcount-startcount)*1000)/freq; } static void Sys_Milliseconds_TGT(void) { static unsigned int base; static qboolean initialized = qfalse; unsigned int now; if (!initialized) { // let base retain 16 bits of effectively random data which is used //for quickly generating random numbers base = timeGetTime() & 0xffff0000; initialized = qtrue; } now=timeGetTime(); curtime=now-base; } unsigned int Sys_Milliseconds (void) { if (hwtimer) Sys_Milliseconds_QPC(); else Sys_Milliseconds_TGT(); return curtime; } void Sys_Mkdir (const char *path) { _mkdir (path); } //=============================================================================== char findbase[MAX_OSPATH]; char findpath[MAX_OSPATH]; int findhandle = -1; static qboolean CompareAttributes( unsigned found, unsigned musthave, unsigned canthave ) { if ( ( found & _A_RDONLY ) && ( canthave & SFF_RDONLY ) ) return qfalse; if ( ( found & _A_HIDDEN ) && ( canthave & SFF_HIDDEN ) ) return qfalse; if ( ( found & _A_SYSTEM ) && ( canthave & SFF_SYSTEM ) ) return qfalse; if ( ( found & _A_SUBDIR ) && ( canthave & SFF_SUBDIR ) ) return qfalse; if ( ( found & _A_ARCH ) && ( canthave & SFF_ARCH ) ) return qfalse; if ( ( musthave & SFF_RDONLY ) && !( found & _A_RDONLY ) ) return qfalse; if ( ( musthave & SFF_HIDDEN ) && !( found & _A_HIDDEN ) ) return qfalse; if ( ( musthave & SFF_SYSTEM ) && !( found & _A_SYSTEM ) ) return qfalse; if ( ( musthave & SFF_SUBDIR ) && !( found & _A_SUBDIR ) ) return qfalse; if ( ( musthave & SFF_ARCH ) && !( found & _A_ARCH ) ) return qfalse; return qtrue; } char *Sys_FindFirst (char *path, unsigned musthave, unsigned canthave ) { struct _finddata_t findinfo; if (findhandle != -1) Sys_Error ("Sys_BeginFind without close"); COM_FilePath (path, findbase); findhandle = _findfirst (path, &findinfo); while (findhandle != -1) { if ( !CompareAttributes( findinfo.attrib, musthave, canthave ) ) { _findclose ( findhandle ); findhandle = _findnext ( findhandle, &findinfo ); } else { Q_snprintfz (findpath, sizeof(findpath), "%s/%s", findbase, findinfo.name); return findpath; } } return NULL; } char *Sys_FindNext ( unsigned musthave, unsigned canthave ) { struct _finddata_t findinfo; if (findhandle == -1) return NULL; while (_findnext (findhandle, &findinfo) != -1) { if ( CompareAttributes( findinfo.attrib, musthave, canthave ) ) { Q_snprintfz (findpath, sizeof(findpath), "%s/%s", findbase, findinfo.name); return findpath; } } return NULL; } void Sys_FindClose (void) { if (findhandle != -1) _findclose (findhandle); findhandle = -1; } /* ================= Sys_GetHomeDirectory ================= */ char *Sys_GetHomeDirectory (void) { return NULL; } //=============================================================================== /* ================ Sys_Init ================ */ void Sys_Init (void) { OSVERSIONINFO vinfo; timeBeginPeriod(1); Sys_InitTime(); vinfo.dwOSVersionInfoSize = sizeof(vinfo); if (!GetVersionEx (&vinfo)) Sys_Error ("Couldn't get OS info"); if (vinfo.dwMajorVersion < 4) Sys_Error ("%s requires windows version 4 or greater", APPLICATION); if (vinfo.dwPlatformId == VER_PLATFORM_WIN32s) Sys_Error ("%s doesn't run on Win32s", APPLICATION); else if ( vinfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ) s_win95 = qtrue; if (dedicated->integer) { SetPriorityClass (GetCurrentProcess(), HIGH_PRIORITY_CLASS); if (!AllocConsole ()) Sys_Error ("Couldn't create dedicated server console"); hinput = GetStdHandle (STD_INPUT_HANDLE); houtput = GetStdHandle (STD_OUTPUT_HANDLE); // let QHOST hook in InitConProc (argc, argv); } } static char console_text[256]; static int console_textlen; /* ================ Sys_ConsoleInput ================ */ char *Sys_ConsoleInput (void) { INPUT_RECORD rec; int ch; DWORD dummy; DWORD numread, numevents; if (!dedicated || !dedicated->integer) return NULL; for ( ;; ) { if (!GetNumberOfConsoleInputEvents (hinput, &numevents)) Sys_Error ("Error getting # of console events"); if (numevents <= 0) break; if (!ReadConsoleInput(hinput, &rec, 1, &numread)) Sys_Error ("Error reading console input"); if (numread != 1) Sys_Error ("Couldn't read console input"); if (rec.EventType == KEY_EVENT) { if (!rec.Event.KeyEvent.bKeyDown) { ch = rec.Event.KeyEvent.uChar.AsciiChar; switch (ch) { case '\r': WriteFile(houtput, "\r\n", 2, &dummy, NULL); if (console_textlen) { console_text[console_textlen] = 0; console_textlen = 0; return console_text; } break; case '\b': if (console_textlen) { console_textlen--; WriteFile(houtput, "\b \b", 3, &dummy, NULL); } break; default: if (ch >= ' ') { if (console_textlen < sizeof(console_text)-2) { WriteFile(houtput, &ch, 1, &dummy, NULL); console_text[console_textlen] = ch; console_textlen++; } } break; } } } } return NULL; } /* ================ Sys_ConsoleOutput Print text to the dedicated console ================ */ void Sys_ConsoleOutput (char *string) { DWORD dummy; char text[256]; if (!dedicated || !dedicated->integer) return; if (console_textlen) { text[0] = '\r'; memset(&text[1], ' ', console_textlen); text[console_textlen+1] = '\r'; text[console_textlen+2] = 0; WriteFile(houtput, text, console_textlen+2, &dummy, NULL); } WriteFile(houtput, string, (unsigned)strlen(string), &dummy, NULL); if (console_textlen) WriteFile(houtput, console_text, console_textlen, &dummy, NULL); } /* ================ Sys_SendKeyEvents Send Key_Event calls ================ */ void Sys_SendKeyEvents( void ) { MSG msg; while( PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE) ) { if(!GetMessage(&msg, NULL, 0, 0)) Sys_Quit(); sys_msg_time = msg.time; TranslateMessage(&msg); DispatchMessage(&msg); } // grab frame time sys_frame_time = timeGetTime(); // FIXME: should this be at start? } /* ================ Sys_GetClipboardData ================ */ char *Sys_GetClipboardData( void ) { char *data = NULL; char *cliptext; if( OpenClipboard( NULL ) != 0 ) { HANDLE hClipboardData; if( ( hClipboardData = GetClipboardData( CF_TEXT ) ) != 0 ) { if( ( cliptext = GlobalLock( hClipboardData ) ) != 0 ) { data = Q_malloc( GlobalSize( hClipboardData ) + 1 ); strcpy( data, cliptext ); GlobalUnlock( hClipboardData ); } } CloseClipboard(); } return data; } /* ============================================================================== WINDOWS CRAP ============================================================================== */ /* ================= Sys_AppActivate ================= */ void Sys_AppActivate (void) { #ifndef DEDICATED_ONLY ShowWindow ( cl_hwnd, SW_RESTORE); SetForegroundWindow ( cl_hwnd ); #endif } /* ======================================================================== DLL ======================================================================== */ /* ================= Sys_UnloadLibrary ================= */ void Sys_UnloadLibrary( void **lib ) { if( lib && *lib ) { if( !FreeLibrary( *lib ) ) Com_Error( ERR_FATAL, "FreeLibrary failed" ); *lib = NULL; } } /* ================= Sys_LoadLibrary ================= */ void *Sys_LoadLibrary( char *name, dllfunc_t *funcs ) { HINSTANCE lib; dllfunc_t *func; if( !name || !name[0] || !funcs ) return NULL; Com_DPrintf( "LoadLibrary (%s)\n", name ); lib = LoadLibrary( name ); if( !lib ) return NULL; for( func = funcs; func->name; func++ ) { *(func->funcPointer) = ( void * )GetProcAddress( lib, func->name ); if( !(*(func->funcPointer)) ) { Sys_UnloadLibrary( ( void ** )&lib ); Com_Error( ERR_FATAL, "%s: GetProcAddress failed for %s", name, func->name ); } } return lib; } /* ======================================================================== GAME DLL ======================================================================== */ static HINSTANCE game_library = NULL; static HINSTANCE cgame_library = NULL; static HINSTANCE ui_library = NULL; #ifdef __cplusplus # define EXTERN_API_FUNC extern "C" #else # define EXTERN_API_FUNC extern #endif /* ================= Sys_UnloadGameLibrary ================= */ void Sys_UnloadGameLibrary( gamelib_t gamelib ) { HINSTANCE *lib; lib=NULL; switch( gamelib ) { case LIB_GAME: lib = &game_library; #ifdef GAME_HARD_LINKED *lib = NULL; #endif break; case LIB_CGAME: lib = &cgame_library; #ifdef CGAME_HARD_LINKED *lib = NULL; #endif break; case LIB_UI: lib = &ui_library; #ifdef UI_HARD_LINKED *lib = NULL; #endif break; default: assert( qfalse ); } if( *lib ) { if( !FreeLibrary( *lib ) ) Com_Error( ERR_FATAL, "FreeLibrary failed" ); *lib = NULL; } } /* ================= Sys_LoadGameLibrary Loads the game dll ================= */ #if defined _M_IX86 # define ARCH "x86" #elif defined _M_ALPHA # define ARCH "axp" #endif void *Sys_LoadGameLibrary( gamelib_t gamelib, void *parms ) { char name[MAX_OSPATH]; char *path; void *(*APIfunc) (void *); HINSTANCE *lib; const char *libname; const char *apifuncname; APIfunc = NULL; lib=NULL; libname=NULL; apifuncname=NULL; switch( gamelib ) { case LIB_GAME: { #ifdef GAME_HARD_LINKED EXTERN_API_FUNC void *GetGameAPI( void * ); APIfunc = GetGameAPI; #endif lib = &game_library; libname = "game_" ARCH ".dll"; apifuncname = "GetGameAPI"; break; } case LIB_CGAME: { #ifdef CGAME_HARD_LINKED EXTERN_API_FUNC void *GetCGameAPI( void * ); APIfunc = GetCGameAPI; #endif lib = &cgame_library; libname = "cgame_" ARCH ".dll"; apifuncname = "GetCGameAPI"; break; } case LIB_UI: { #ifdef UI_HARD_LINKED EXTERN_API_FUNC void *GetUIAPI( void * ); APIfunc = GetUIAPI; #endif lib = &ui_library; libname = "ui_" ARCH ".dll"; apifuncname = "GetUIAPI"; break; } default: assert( qfalse ); } if (*lib) Com_Error (ERR_FATAL, "Sys_LoadGameLibrary without Sys_UnloadGameLibrary"); if (APIfunc) { *lib = ( HINSTANCE )1; return APIfunc (parms); } // run through the search paths path = NULL; while (1) { path = FS_NextPath (path); if (!path) return NULL; // couldn't find one anywhere Q_snprintfz (name, sizeof(name), "%s/%s", path, libname); *lib = LoadLibrary (name); if (*lib) { Com_DPrintf ("LoadGameLibrary (%s)\n",name); break; } } APIfunc = (void *)GetProcAddress (*lib, apifuncname); if (!APIfunc) { Sys_UnloadGameLibrary (gamelib); return NULL; } return APIfunc (parms); } //======================================================================= /* ================== ParseCommandLine ================== */ void ParseCommandLine (LPSTR lpCmdLine) { argc = 1; argv[0] = "exe"; while (*lpCmdLine && (argc < MAX_NUM_ARGVS)) { while (*lpCmdLine && (*lpCmdLine <= 32 || *lpCmdLine > 126 || 34 == *lpCmdLine || 39 == *lpCmdLine)) lpCmdLine++; if (*lpCmdLine) { argv[argc] = lpCmdLine; argc++; while (*lpCmdLine && *lpCmdLine > 32 && *lpCmdLine <= 126 && 34 != *lpCmdLine && 39 != *lpCmdLine ) lpCmdLine++; if (*lpCmdLine) { *lpCmdLine = 0; lpCmdLine++; } } } } /* ================== WinMain ================== */ HINSTANCE global_hInstance; double pfreq; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { MSG msg; unsigned int oldtime, newtime,time; /* previous instances do not exist in Win32 */ if(hPrevInstance) return 0; global_hInstance = hInstance; ParseCommandLine (lpCmdLine); Qcommon_Init (argc, argv); oldtime = Sys_Milliseconds (); /* main window message loop */ while (1) { // if at a full screen console, don't update unless needed if (Minimized || (dedicated && dedicated->integer) ) { Sleep (1); } while (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) { if (!GetMessage (&msg, NULL, 0, 0)) Com_Quit (); sys_msg_time = msg.time; TranslateMessage (&msg); DispatchMessage (&msg); } do { newtime = Sys_Milliseconds(); time = newtime - oldtime; // no warp problem as unsigned } while (time < 1); //Com_Printf ("time:%5.2u - %5.2u = %5.2u\n", newtime, oldtime, time); // do as q3 (use the default floating point precision) // _controlfp( ~( _EM_ZERODIVIDE /*| _EM_INVALID*/ ), _MCW_EM ); //_controlfp( _PC_24, _MCW_PC ); Qcommon_Frame (time); oldtime = newtime; } // never gets here return TRUE; }