// win_main.c #include "../client/client.h" #include "../qcommon/qcommon.h" #include "../win32/win_local.h" #include "../win32/resource.h" #include #include #include #include #include #include #include #include "../qcommon/stringed_ingame.h" #define CD_BASEDIR "gamedata\\gamedata" #define CD_EXE "jamp.exe" #define CD_BASEDIR_LINUX "bin\\x86\\glibc-2.1" #define CD_EXE_LINUX "jamp" #define CD_VOLUME "JEDIACAD" #define MEM_THRESHOLD 128*1024*1024 static char sys_cmdline[MAX_STRING_CHARS]; clientStatic_t cls; static int sys_monkeySpank; static int sys_checksum; void *Sys_GetBotAIAPI (void *parms ) { return NULL; } void Conbuf_AppendText( const char *pMsg ) { char msg[4096]; strcpy(msg, pMsg); printf(Q_CleanStr(msg)); printf("\n"); } /* ================== Sys_LowPhysicalMemory() ================== */ qboolean Sys_LowPhysicalMemory() { MEMORYSTATUS stat; GlobalMemoryStatus (&stat); return (stat.dwTotalPhys <= MEM_THRESHOLD) ? qtrue : qfalse; } /* ================== Sys_FunctionCmp ================== */ int Sys_FunctionCmp(void *f1, void *f2) { int i, j, l; byte func_end[32] = {0xC3, 0x90, 0x90, 0x00}; byte *ptr, *ptr2; byte *f1_ptr, *f2_ptr; ptr = (byte *) f1; if (*(byte *)ptr == 0xE9) { //Com_Printf("f1 %p1 jmp %d\n", (int *) f1, *(int*)(ptr+1)); f1_ptr = (byte*)(((byte*)f1) + (*(int *)(ptr+1)) + 5); } else { f1_ptr = ptr; } //Com_Printf("f1 ptr %p\n", f1_ptr); ptr = (byte *) f2; if (*(byte *)ptr == 0xE9) { //Com_Printf("f2 %p jmp %d\n", (int *) f2, *(int*)(ptr+1)); f2_ptr = (byte*)(((byte*)f2) + (*(int *)(ptr+1)) + 5); } else { f2_ptr = ptr; } //Com_Printf("f2 ptr %p\n", f2_ptr); #ifdef _DEBUG sprintf((char *)func_end, "%c%c%c%c%c%c%c", 0x5F, 0x5E, 0x5B, 0x8B, 0xE5, 0x5D, 0xC3); #endif for (i = 0; i < 1024; i++) { for (j = 0; func_end[j]; j++) { if (f1_ptr[i+j] != func_end[j]) break; } if (!func_end[j]) { break; } } #ifdef _DEBUG l = i + 7; #else l = i + 2; #endif //Com_Printf("function length = %d\n", l); for (i = 0; i < l; i++) { // check for a potential function call if (*((byte *) &f1_ptr[i]) == 0xE8) { // get the function pointers in case this really is a function call ptr = (byte *) (((byte *) &f1_ptr[i]) + (*(int *) &f1_ptr[i+1])) + 5; ptr2 = (byte *) (((byte *) &f2_ptr[i]) + (*(int *) &f2_ptr[i+1])) + 5; // if it was a function call and both f1 and f2 call the same function if (ptr == ptr2) { i += 4; continue; } } if (f1_ptr[i] != f2_ptr[i]) return qfalse; } return qtrue; } /* ================== Sys_FunctionCheckSum ================== */ int Sys_FunctionCheckSum(void *f1) { int i, j, l; byte func_end[32] = {0xC3, 0x90, 0x90, 0x00}; byte *ptr; byte *f1_ptr; ptr = (byte *) f1; if (*(byte *)ptr == 0xE9) { //Com_Printf("f1 %p1 jmp %d\n", (int *) f1, *(int*)(ptr+1)); f1_ptr = (byte*)(((byte*)f1) + (*(int *)(ptr+1)) + 5); } else { f1_ptr = ptr; } //Com_Printf("f1 ptr %p\n", f1_ptr); #ifdef _DEBUG sprintf((char *)func_end, "%c%c%c%c%c%c%c", 0x5F, 0x5E, 0x5B, 0x8B, 0xE5, 0x5D, 0xC3); #endif for (i = 0; i < 1024; i++) { for (j = 0; func_end[j]; j++) { if (f1_ptr[i+j] != func_end[j]) break; } if (!func_end[j]) { break; } } #ifdef _DEBUG l = i + 7; #else l = i + 2; #endif //Com_Printf("function length = %d\n", l); return Com_BlockChecksum( f1_ptr, l ); } /* ================== Sys_MonkeyShouldBeSpanked ================== */ int Sys_MonkeyShouldBeSpanked( void ) { return sys_monkeySpank; } /* ================== Sys_VerifyCodeChecksum ================== */ void Sys_VerifyCodeChecksum( void *codeBase ) { } /* =============== PrintMatches =============== */ static char g_consoleField1[256]; static char g_consoleField2[256]; static void PrintMatches( const char *s ) { if ( !Q_stricmpn( s, g_consoleField1, strlen( g_consoleField1 ) ) ) { printf( " %s\n", s ); } } //qboolean stdin_active = qtrue; char *Sys_ConsoleInput(void) { const char ClearLine[] = "\r \r"; static int len=0; static bool bPendingExtended = false; if (!kbhit()) return NULL; if (len == 0) memset(g_consoleField1,0,sizeof(g_consoleField1)); g_consoleField1[len] = getch(); if (bPendingExtended) { switch (g_consoleField1[len]) { case 'H': //up strcpy(g_consoleField1, g_consoleField2); printf(ClearLine); printf("%s",g_consoleField1); len = strlen(g_consoleField1); break; case 'K': //left break; case 'M': //right break; case 'P': //down break; } g_consoleField1[len] = 0; //erase last key hit bPendingExtended = false; } else switch ((unsigned char) g_consoleField1[len]) { case 0x00: //fkey is next case 0xe0: //extended = arrow keys g_consoleField1[len] = 0; //erase last key hit bPendingExtended = true; break; case 8: // backspace printf("%c %c",g_consoleField1[len],g_consoleField1[len]); g_consoleField1[len] = 0; if (len > 0) len--; g_consoleField1[len] = 0; break; case 9: //Tab if (len) { g_consoleField1[len] = 0; //erase last key hit printf( "\n"); // run through again, printing matches Cmd_CommandCompletion( PrintMatches ); Cvar_CommandCompletion( PrintMatches ); printf( "\n%s", g_consoleField1); } break; case 27: // esc // clear the line printf(ClearLine); len = 0; break; case '\r': //enter g_consoleField1[len] = 0; //erase last key hit printf("\n"); if (len) { len = 0; strcpy(g_consoleField2, g_consoleField1); return g_consoleField1; } break; case 'v' - 'a' + 1: // ctrl-v is paste g_consoleField1[len] = 0; //erase last key hit char *cbd; cbd = Sys_GetClipboardData(); if (cbd) { strncpy (&g_consoleField1[len], cbd, sizeof(g_consoleField1) ); printf("%s",cbd); len += strlen(cbd); Z_Free( cbd ); if (len == sizeof(g_consoleField1)) { len = 0; return g_consoleField1; } } break; default: printf("%c",g_consoleField1[len]); len++; if (len == sizeof(g_consoleField1)) { len = 0; return g_consoleField1; } break; } return NULL; } /* ================== Sys_BeginProfiling ================== */ void Sys_BeginProfiling( void ) { // this is just used on the mac build } void Sys_ShowConsole( int visLevel, qboolean quitOnClose ) { } /* ============= Sys_Error Show the early console as an error dialog ============= */ void QDECL Sys_Error( const char *error, ... ) { va_list argptr; char text[4096]; MSG msg; va_start (argptr, error); Q_vsnprintf (text, sizeof(text), error, argptr); va_end (argptr); Conbuf_AppendText( text ); Conbuf_AppendText( "\n" ); // Sys_SetErrorText( text ); Sys_ShowConsole( 1, qtrue ); timeEndPeriod( 1 ); IN_Shutdown(); // wait for the user to quit while ( 1 ) { if (!GetMessage (&msg, NULL, 0, 0)) Com_Quit_f (); TranslateMessage (&msg); DispatchMessage (&msg); } // Sys_DestroyConsole(); Com_ShutdownZoneMemory(); Com_ShutdownHunkMemory(); exit (1); } /* ============== Sys_Quit ============== */ void Sys_Quit( void ) { timeEndPeriod( 1 ); IN_Shutdown(); // Sys_DestroyConsole(); Com_ShutdownZoneMemory(); Com_ShutdownHunkMemory(); exit (0); } /* ============== Sys_Print ============== */ void Sys_Print( const char *msg ) { Conbuf_AppendText( msg ); } /* ============== Sys_Mkdir ============== */ void Sys_Mkdir( const char *path ) { _mkdir (path); } /* ============== Sys_Cwd ============== */ char *Sys_Cwd( void ) { static char cwd[MAX_OSPATH]; _getcwd( cwd, sizeof( cwd ) - 1 ); cwd[MAX_OSPATH-1] = 0; return cwd; } /* ============== Sys_DefaultCDPath ============== */ char *Sys_DefaultCDPath( void ) { return ""; } /* ============== Sys_DefaultBasePath ============== */ char *Sys_DefaultBasePath( void ) { return Sys_Cwd(); } /* ============================================================== DIRECTORY SCANNING ============================================================== */ #define MAX_FOUND_FILES 0x1000 void Sys_ListFilteredFiles( const char *basedir, char *subdirs, char *filter, char **psList, int *numfiles ) { char search[MAX_OSPATH], newsubdirs[MAX_OSPATH]; char filename[MAX_OSPATH]; int findhandle; struct _finddata_t findinfo; if ( *numfiles >= MAX_FOUND_FILES - 1 ) { return; } if (strlen(subdirs)) { Com_sprintf( search, sizeof(search), "%s\\%s\\*", basedir, subdirs ); } else { Com_sprintf( search, sizeof(search), "%s\\*", basedir ); } findhandle = _findfirst (search, &findinfo); if (findhandle == -1) { return; } do { if (findinfo.attrib & _A_SUBDIR) { if (Q_stricmp(findinfo.name, ".") && Q_stricmp(findinfo.name, "..")) { if (strlen(subdirs)) { Com_sprintf( newsubdirs, sizeof(newsubdirs), "%s\\%s", subdirs, findinfo.name); } else { Com_sprintf( newsubdirs, sizeof(newsubdirs), "%s", findinfo.name); } Sys_ListFilteredFiles( basedir, newsubdirs, filter, psList, numfiles ); } } if ( *numfiles >= MAX_FOUND_FILES - 1 ) { break; } Com_sprintf( filename, sizeof(filename), "%s\\%s", subdirs, findinfo.name ); if (!Com_FilterPath( filter, filename, qfalse )) continue; psList[ *numfiles ] = CopyString( filename ); (*numfiles)++; } while ( _findnext (findhandle, &findinfo) != -1 ); _findclose (findhandle); } static qboolean strgtr(const char *s0, const char *s1) { int l0, l1, i; l0 = strlen(s0); l1 = strlen(s1); if (l1 s0[i]) { return qtrue; } if (s1[i] < s0[i]) { return qfalse; } } return qfalse; } char **Sys_ListFiles( const char *directory, const char *extension, char *filter, int *numfiles, qboolean wantsubs ) { char search[MAX_OSPATH]; int nfiles; char **listCopy; char *list[MAX_FOUND_FILES]; struct _finddata_t findinfo; int findhandle; int flag; int i; if (filter) { nfiles = 0; Sys_ListFilteredFiles( directory, "", filter, list, &nfiles ); list[ nfiles ] = 0; *numfiles = nfiles; if (!nfiles) return NULL; listCopy = (char **)Z_Malloc( ( nfiles + 1 ) * sizeof( *listCopy ), TAG_FILESYS ); for ( i = 0 ; i < nfiles ; i++ ) { listCopy[i] = list[i]; } listCopy[i] = NULL; return listCopy; } if ( !extension) { extension = ""; } // passing a slash as extension will find directories if ( extension[0] == '/' && extension[1] == 0 ) { extension = ""; flag = 0; } else { flag = _A_SUBDIR; } Com_sprintf( search, sizeof(search), "%s\\*%s", directory, extension ); // search nfiles = 0; findhandle = _findfirst (search, &findinfo); if (findhandle == -1) { *numfiles = 0; return NULL; } do { if ( (!wantsubs && flag ^ ( findinfo.attrib & _A_SUBDIR )) || (wantsubs && findinfo.attrib & _A_SUBDIR) ) { if ( nfiles == MAX_FOUND_FILES - 1 ) { break; } list[ nfiles ] = CopyString( findinfo.name ); nfiles++; } } while ( _findnext (findhandle, &findinfo) != -1 ); list[ nfiles ] = 0; _findclose (findhandle); // return a copy of the list *numfiles = nfiles; if ( !nfiles ) { return NULL; } listCopy = (char **)Z_Malloc( ( nfiles + 1 ) * sizeof( *listCopy ), TAG_FILESYS ); for ( i = 0 ; i < nfiles ; i++ ) { listCopy[i] = list[i]; } listCopy[i] = NULL; do { flag = 0; for(i=1; i 0 ) { available = stream.sIO[f].threadPosition - stream.sIO[f].streamPosition; if ( !available ) { if ( stream.sIO[f].eof ) { break; } if ( sleepCount == 1 ) { Com_DPrintf( "Sys_StreamedRead: waiting\n" ); } if ( ++sleepCount > 100 ) { Com_Error( ERR_FATAL, "Sys_StreamedRead: thread has died"); } Sleep( 10 ); continue; } EnterCriticalSection( &stream.crit ); bufferPoint = stream.sIO[f].streamPosition % stream.sIO[f].bufferSize; bufferCount = stream.sIO[f].bufferSize - bufferPoint; copy = available < bufferCount ? available : bufferCount; if ( copy > remaining ) { copy = remaining; } memcpy( dest, stream.sIO[f].buffer + bufferPoint, copy ); stream.sIO[f].streamPosition += copy; dest += copy; remaining -= copy; LeaveCriticalSection( &stream.crit ); } return (count * size - remaining) / size; } /* =============== Sys_StreamSeek ================ */ void Sys_StreamSeek( fileHandle_t f, int offset, int origin ) { // halt the thread EnterCriticalSection( &stream.crit ); // clear to that point FS_Seek( f, offset, origin ); stream.sIO[f].streamPosition = 0; stream.sIO[f].threadPosition = 0; stream.sIO[f].eof = qfalse; // let the thread start running at the new position LeaveCriticalSection( &stream.crit ); } #endif /* ======================================================================== EVENT LOOP ======================================================================== */ #define MAX_QUED_EVENTS 256 #define MASK_QUED_EVENTS ( MAX_QUED_EVENTS - 1 ) sysEvent_t eventQue[MAX_QUED_EVENTS]; static int eventHead=0; static int eventTail=0; byte sys_packetReceived[MAX_MSGLEN]; /* ================ Sys_QueEvent A time of 0 will get the current time Ptr should either be null, or point to a block of data that can be freed by the game later. ================ */ void Sys_QueEvent( int time, sysEventType_t type, int value, int value2, int ptrLength, void *ptr ) { sysEvent_t *ev; ev = &eventQue[ eventHead & MASK_QUED_EVENTS ]; if ( eventHead - eventTail >= MAX_QUED_EVENTS ) { Com_Printf("Sys_QueEvent: overflow\n"); // we are discarding an event, but don't leak memory if ( ev->evPtr ) { Z_Free( ev->evPtr ); } eventTail++; } eventHead++; if ( time == 0 ) { time = Sys_Milliseconds(); } ev->evTime = time; ev->evType = type; ev->evValue = value; ev->evValue2 = value2; ev->evPtrLength = ptrLength; ev->evPtr = ptr; } /* ================ Sys_GetEvent ================ */ sysEvent_t Sys_GetEvent( void ) { MSG msg; sysEvent_t ev; char *s; msg_t netmsg; netadr_t adr; // return if we have data if ( eventHead > eventTail ) { eventTail++; return eventQue[ ( eventTail - 1 ) & MASK_QUED_EVENTS ]; } // pump the message loop while (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) { if ( !GetMessage (&msg, NULL, 0, 0) ) { Com_Quit_f(); } // save the msg time, because wndprocs don't have access to the timestamp // g_wv.sysMsgTime = msg.time; TranslateMessage (&msg); DispatchMessage (&msg); } // check for console commands s = Sys_ConsoleInput(); if ( s ) { char *b; int len; len = strlen( s ) + 1; b = (char *)Z_Malloc( len, TAG_EVENT ); Q_strncpyz( b, s, len ); Sys_QueEvent( 0, SE_CONSOLE, 0, 0, len, b ); } // check for network packets MSG_Init( &netmsg, sys_packetReceived, sizeof( sys_packetReceived ) ); if ( Sys_GetPacket ( &adr, &netmsg ) ) { netadr_t *buf; int len; // copy out to a seperate buffer for qeueing // the readcount stepahead is for SOCKS support len = sizeof( netadr_t ) + netmsg.cursize - netmsg.readcount; buf = (netadr_t *)Z_Malloc( len, TAG_EVENT, qtrue ); *buf = adr; memcpy( buf+1, &netmsg.data[netmsg.readcount], netmsg.cursize - netmsg.readcount ); Sys_QueEvent( 0, SE_PACKET, 0, 0, len, buf ); } // return if we have data if ( eventHead > eventTail ) { eventTail++; return eventQue[ ( eventTail - 1 ) & MASK_QUED_EVENTS ]; } // create an empty event to return memset( &ev, 0, sizeof( ev ) ); ev.evTime = timeGetTime(); return ev; } //================================================================ /* ================= Sys_In_Restart_f Restart the input subsystem ================= */ void Sys_In_Restart_f( void ) { IN_Shutdown(); IN_Init(); } /* ================= Sys_Net_Restart_f Restart the network subsystem ================= */ void Sys_Net_Restart_f( void ) { NET_Restart(); } /* ================ Sys_Init Called after the common systems (cvars, files, etc) are initialized ================ */ #define OSR2_BUILD_NUMBER 1111 #define WIN98_BUILD_NUMBER 1998 void Sys_Init( void ) { int cpuid; // make sure the timer is high precision, otherwise // NT gets 18ms resolution timeBeginPeriod( 1 ); Cmd_AddCommand ("in_restart", Sys_In_Restart_f); Cmd_AddCommand ("net_restart", Sys_Net_Restart_f); // g_wv.osversion.dwOSVersionInfoSize = sizeof( g_wv.osversion ); // if (!GetVersionEx (&g_wv.osversion)) // Sys_Error ("Couldn't get OS info"); // if (g_wv.osversion.dwMajorVersion < 4) // Sys_Error ("This game requires Windows version 4 or greater"); // if (g_wv.osversion.dwPlatformId == VER_PLATFORM_WIN32s) // Sys_Error ("This game doesn't run on Win32s"); // if ( g_wv.osversion.dwPlatformId == VER_PLATFORM_WIN32_NT ) // { // Cvar_Set( "arch", "winnt" ); // } // else if ( g_wv.osversion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ) // { // if ( LOWORD( g_wv.osversion.dwBuildNumber ) >= WIN98_BUILD_NUMBER ) // { // Cvar_Set( "arch", "win98" ); // } // else if ( LOWORD( g_wv.osversion.dwBuildNumber ) >= OSR2_BUILD_NUMBER ) // { // Cvar_Set( "arch", "win95 osr2.x" ); // } // else // { // Cvar_Set( "arch", "win95" ); // } // } // else // { // Cvar_Set( "arch", "unknown Windows variant" ); // } // save out a couple things in rom cvars for the renderer to access // Cvar_Get( "win_hinstance", va("%i", (int)g_wv.hInstance), CVAR_ROM ); // Cvar_Get( "win_wndproc", va("%i", (int)MainWndProc), CVAR_ROM ); // // figure out our CPU // Cvar_Get( "sys_cpustring", "detect", 0 ); if ( !Q_stricmp( Cvar_VariableString( "sys_cpustring"), "detect" ) ) { Com_Printf( "...detecting CPU, found " ); cpuid = Sys_GetProcessorId(); switch ( cpuid ) { case CPUID_GENERIC: Cvar_Set( "sys_cpustring", "generic" ); break; case CPUID_INTEL_UNSUPPORTED: Cvar_Set( "sys_cpustring", "x86 (pre-Pentium)" ); break; case CPUID_INTEL_PENTIUM: Cvar_Set( "sys_cpustring", "x86 (P5/PPro, non-MMX)" ); break; case CPUID_INTEL_MMX: Cvar_Set( "sys_cpustring", "x86 (P5/Pentium2, MMX)" ); break; case CPUID_INTEL_KATMAI: Cvar_Set( "sys_cpustring", "Intel Pentium III" ); break; case CPUID_INTEL_WILLIAMETTE: Cvar_Set( "sys_cpustring", "Intel Pentium IV" ); break; case CPUID_AMD_3DNOW: Cvar_Set( "sys_cpustring", "AMD w/ 3DNow!" ); break; case CPUID_AXP: Cvar_Set( "sys_cpustring", "Alpha AXP" ); break; default: Com_Error( ERR_FATAL, "Unknown cpu type %d\n", cpuid ); break; } } else { Com_Printf( "...forcing CPU type to " ); if ( !Q_stricmp( Cvar_VariableString( "sys_cpustring" ), "generic" ) ) { cpuid = CPUID_GENERIC; } else if ( !Q_stricmp( Cvar_VariableString( "sys_cpustring" ), "x87" ) ) { cpuid = CPUID_INTEL_PENTIUM; } else if ( !Q_stricmp( Cvar_VariableString( "sys_cpustring" ), "mmx" ) ) { cpuid = CPUID_INTEL_MMX; } else if ( !Q_stricmp( Cvar_VariableString( "sys_cpustring" ), "3dnow" ) ) { cpuid = CPUID_AMD_3DNOW; } else if ( !Q_stricmp( Cvar_VariableString( "sys_cpustring" ), "PentiumIII" ) ) { cpuid = CPUID_INTEL_KATMAI; } else if ( !Q_stricmp( Cvar_VariableString( "sys_cpustring" ), "PentiumIV" ) ) { cpuid = CPUID_INTEL_WILLIAMETTE; } else if ( !Q_stricmp( Cvar_VariableString( "sys_cpustring" ), "axp" ) ) { cpuid = CPUID_AXP; } else { Com_Printf( "WARNING: unknown sys_cpustring '%s'\n", Cvar_VariableString( "sys_cpustring" ) ); cpuid = CPUID_GENERIC; } } Cvar_SetValue( "sys_cpuid", cpuid ); Com_Printf( "%s\n", Cvar_VariableString( "sys_cpustring" ) ); Cvar_Set( "username", Sys_GetCurrentUser() ); IN_Init(); // FIXME: not in dedicated? } //======================================================================= //int totalMsec, countMsec; /* ================== WinMain ================== */ //int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { int main(int argc, char **argv) { char cwd[MAX_OSPATH]; char *cmdline; int i,len; // int startTime, endTime; // should never get a previous instance in Win32 // if ( hPrevInstance ) { // return 0; // } // sys_checksum = Sys_CodeInMemoryChecksum( hInstance ); // Sys_VerifyCodeChecksum( hInstance ); // merge the command line, this is kinda silly for (len = 1, i = 1; i < argc; i++) len += strlen(argv[i]) + 1; cmdline = (char *)malloc(len); *cmdline = 0; for (i = 1; i < argc; i++) { if (i > 1) strcat(cmdline, " "); strcat(cmdline, argv[i]); } // g_wv.hInstance = hInstance; // Q_strncpyz( sys_cmdline, lpCmdLine, sizeof( sys_cmdline ) ); // done before Com/Sys_Init since we need this for error output // Sys_CreateConsole(); // no abort/retry/fail errors SetErrorMode( SEM_FAILCRITICALERRORS ); // get the initial time base Sys_Milliseconds(); #if 0 // if we find the CD, add a +set cddir xxx command line Sys_ScanForCD(); #endif Sys_InitStreamThread(); Com_Init( cmdline ); NET_Init(); _getcwd (cwd, sizeof(cwd)); Com_Printf("Working directory: %s\n", cwd); // hide the early console since we've reached the point where we // have a working graphics subsystems if ( !com_dedicated->integer && !com_viewlog->integer ) { Sys_ShowConsole( 0, qfalse ); } #ifdef _DEBUG if ( sys_monkeySpank ) { Cvar_Set("cl_trn", "666"); } #endif // main game loop while( 1 ) { // if not running as a game client, sleep a bit // if ( g_wv.isMinimized || ( com_dedicated && com_dedicated->integer ) ) { Sleep( 5 ); // } // set low precision every frame, because some system calls // reset it arbitrarily // _controlfp( _PC_24, _MCW_PC ); // startTime = Sys_Milliseconds(); // make sure mouse and joystick are only called once a frame IN_Frame(); // run the game Com_Frame(); // endTime = Sys_Milliseconds(); // totalMsec += endTime - startTime; // countMsec++; } // never gets here return 0; }