win_main.c (26994B)
1 /* 2 =========================================================================== 3 Copyright (C) 1999-2005 Id Software, Inc. 4 5 This file is part of Quake III Arena source code. 6 7 Quake III Arena source code is free software; you can redistribute it 8 and/or modify it under the terms of the GNU General Public License as 9 published by the Free Software Foundation; either version 2 of the License, 10 or (at your option) any later version. 11 12 Quake III Arena source code is distributed in the hope that it will be 13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with Foobar; if not, write to the Free Software 19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 20 =========================================================================== 21 */ 22 // win_main.c 23 24 #include "../client/client.h" 25 #include "../qcommon/qcommon.h" 26 #include "win_local.h" 27 #include "resource.h" 28 #include <errno.h> 29 #include <float.h> 30 #include <fcntl.h> 31 #include <stdio.h> 32 #include <direct.h> 33 #include <io.h> 34 #include <conio.h> 35 36 #define CD_BASEDIR "quake3" 37 #define CD_EXE "quake3.exe" 38 #define CD_BASEDIR_LINUX "bin\\x86\\glibc-2.1" 39 #define CD_EXE_LINUX "quake3" 40 #define MEM_THRESHOLD 96*1024*1024 41 42 static char sys_cmdline[MAX_STRING_CHARS]; 43 44 // define this to use alternate spanking method 45 // I found out that the regular way doesn't work on my box for some reason 46 // see the associated spank.sh script 47 #define ALT_SPANK 48 #ifdef ALT_SPANK 49 #include <stdio.h> 50 #include <sys\stat.h> 51 52 int fh = 0; 53 54 void Spk_Open(char *name) 55 { 56 fh = open( name, O_TRUNC | O_CREAT | O_WRONLY, S_IREAD | S_IWRITE ); 57 }; 58 59 void Spk_Close() 60 { 61 if (!fh) 62 return; 63 64 close( fh ); 65 fh = 0; 66 } 67 68 void Spk_Printf (const char *text, ...) 69 { 70 va_list argptr; 71 char buf[32768]; 72 73 if (!fh) 74 return; 75 76 va_start (argptr,text); 77 vsprintf (buf, text, argptr); 78 write(fh, buf, strlen(buf)); 79 _commit(fh); 80 va_end (argptr); 81 82 }; 83 #endif 84 85 /* 86 ================== 87 Sys_LowPhysicalMemory() 88 ================== 89 */ 90 91 qboolean Sys_LowPhysicalMemory() { 92 MEMORYSTATUS stat; 93 GlobalMemoryStatus (&stat); 94 return (stat.dwTotalPhys <= MEM_THRESHOLD) ? qtrue : qfalse; 95 } 96 97 /* 98 ================== 99 Sys_BeginProfiling 100 ================== 101 */ 102 void Sys_BeginProfiling( void ) { 103 // this is just used on the mac build 104 } 105 106 /* 107 ============= 108 Sys_Error 109 110 Show the early console as an error dialog 111 ============= 112 */ 113 void QDECL Sys_Error( const char *error, ... ) { 114 va_list argptr; 115 char text[4096]; 116 MSG msg; 117 118 va_start (argptr, error); 119 vsprintf (text, error, argptr); 120 va_end (argptr); 121 122 Conbuf_AppendText( text ); 123 Conbuf_AppendText( "\n" ); 124 125 Sys_SetErrorText( text ); 126 Sys_ShowConsole( 1, qtrue ); 127 128 timeEndPeriod( 1 ); 129 130 IN_Shutdown(); 131 132 // wait for the user to quit 133 while ( 1 ) { 134 if (!GetMessage (&msg, NULL, 0, 0)) 135 Com_Quit_f (); 136 TranslateMessage (&msg); 137 DispatchMessage (&msg); 138 } 139 140 Sys_DestroyConsole(); 141 142 exit (1); 143 } 144 145 /* 146 ============== 147 Sys_Quit 148 ============== 149 */ 150 void Sys_Quit( void ) { 151 timeEndPeriod( 1 ); 152 IN_Shutdown(); 153 Sys_DestroyConsole(); 154 155 exit (0); 156 } 157 158 /* 159 ============== 160 Sys_Print 161 ============== 162 */ 163 void Sys_Print( const char *msg ) { 164 Conbuf_AppendText( msg ); 165 } 166 167 168 /* 169 ============== 170 Sys_Mkdir 171 ============== 172 */ 173 void Sys_Mkdir( const char *path ) { 174 _mkdir (path); 175 } 176 177 /* 178 ============== 179 Sys_Cwd 180 ============== 181 */ 182 char *Sys_Cwd( void ) { 183 static char cwd[MAX_OSPATH]; 184 185 _getcwd( cwd, sizeof( cwd ) - 1 ); 186 cwd[MAX_OSPATH-1] = 0; 187 188 return cwd; 189 } 190 191 /* 192 ============== 193 Sys_DefaultCDPath 194 ============== 195 */ 196 char *Sys_DefaultCDPath( void ) { 197 return ""; 198 } 199 200 /* 201 ============== 202 Sys_DefaultBasePath 203 ============== 204 */ 205 char *Sys_DefaultBasePath( void ) { 206 return Sys_Cwd(); 207 } 208 209 /* 210 ============================================================== 211 212 DIRECTORY SCANNING 213 214 ============================================================== 215 */ 216 217 #define MAX_FOUND_FILES 0x1000 218 219 void Sys_ListFilteredFiles( const char *basedir, char *subdirs, char *filter, char **list, int *numfiles ) { 220 char search[MAX_OSPATH], newsubdirs[MAX_OSPATH]; 221 char filename[MAX_OSPATH]; 222 int findhandle; 223 struct _finddata_t findinfo; 224 225 if ( *numfiles >= MAX_FOUND_FILES - 1 ) { 226 return; 227 } 228 229 if (strlen(subdirs)) { 230 Com_sprintf( search, sizeof(search), "%s\\%s\\*", basedir, subdirs ); 231 } 232 else { 233 Com_sprintf( search, sizeof(search), "%s\\*", basedir ); 234 } 235 236 findhandle = _findfirst (search, &findinfo); 237 if (findhandle == -1) { 238 return; 239 } 240 241 do { 242 if (findinfo.attrib & _A_SUBDIR) { 243 if (Q_stricmp(findinfo.name, ".") && Q_stricmp(findinfo.name, "..")) { 244 if (strlen(subdirs)) { 245 Com_sprintf( newsubdirs, sizeof(newsubdirs), "%s\\%s", subdirs, findinfo.name); 246 } 247 else { 248 Com_sprintf( newsubdirs, sizeof(newsubdirs), "%s", findinfo.name); 249 } 250 Sys_ListFilteredFiles( basedir, newsubdirs, filter, list, numfiles ); 251 } 252 } 253 if ( *numfiles >= MAX_FOUND_FILES - 1 ) { 254 break; 255 } 256 Com_sprintf( filename, sizeof(filename), "%s\\%s", subdirs, findinfo.name ); 257 if (!Com_FilterPath( filter, filename, qfalse )) 258 continue; 259 list[ *numfiles ] = CopyString( filename ); 260 (*numfiles)++; 261 } while ( _findnext (findhandle, &findinfo) != -1 ); 262 263 _findclose (findhandle); 264 } 265 266 static qboolean strgtr(const char *s0, const char *s1) { 267 int l0, l1, i; 268 269 l0 = strlen(s0); 270 l1 = strlen(s1); 271 272 if (l1<l0) { 273 l0 = l1; 274 } 275 276 for(i=0;i<l0;i++) { 277 if (s1[i] > s0[i]) { 278 return qtrue; 279 } 280 if (s1[i] < s0[i]) { 281 return qfalse; 282 } 283 } 284 return qfalse; 285 } 286 287 char **Sys_ListFiles( const char *directory, const char *extension, char *filter, int *numfiles, qboolean wantsubs ) { 288 char search[MAX_OSPATH]; 289 int nfiles; 290 char **listCopy; 291 char *list[MAX_FOUND_FILES]; 292 struct _finddata_t findinfo; 293 int findhandle; 294 int flag; 295 int i; 296 297 if (filter) { 298 299 nfiles = 0; 300 Sys_ListFilteredFiles( directory, "", filter, list, &nfiles ); 301 302 list[ nfiles ] = 0; 303 *numfiles = nfiles; 304 305 if (!nfiles) 306 return NULL; 307 308 listCopy = Z_Malloc( ( nfiles + 1 ) * sizeof( *listCopy ) ); 309 for ( i = 0 ; i < nfiles ; i++ ) { 310 listCopy[i] = list[i]; 311 } 312 listCopy[i] = NULL; 313 314 return listCopy; 315 } 316 317 if ( !extension) { 318 extension = ""; 319 } 320 321 // passing a slash as extension will find directories 322 if ( extension[0] == '/' && extension[1] == 0 ) { 323 extension = ""; 324 flag = 0; 325 } else { 326 flag = _A_SUBDIR; 327 } 328 329 Com_sprintf( search, sizeof(search), "%s\\*%s", directory, extension ); 330 331 // search 332 nfiles = 0; 333 334 findhandle = _findfirst (search, &findinfo); 335 if (findhandle == -1) { 336 *numfiles = 0; 337 return NULL; 338 } 339 340 do { 341 if ( (!wantsubs && flag ^ ( findinfo.attrib & _A_SUBDIR )) || (wantsubs && findinfo.attrib & _A_SUBDIR) ) { 342 if ( nfiles == MAX_FOUND_FILES - 1 ) { 343 break; 344 } 345 list[ nfiles ] = CopyString( findinfo.name ); 346 nfiles++; 347 } 348 } while ( _findnext (findhandle, &findinfo) != -1 ); 349 350 list[ nfiles ] = 0; 351 352 _findclose (findhandle); 353 354 // return a copy of the list 355 *numfiles = nfiles; 356 357 if ( !nfiles ) { 358 return NULL; 359 } 360 361 listCopy = Z_Malloc( ( nfiles + 1 ) * sizeof( *listCopy ) ); 362 for ( i = 0 ; i < nfiles ; i++ ) { 363 listCopy[i] = list[i]; 364 } 365 listCopy[i] = NULL; 366 367 do { 368 flag = 0; 369 for(i=1; i<nfiles; i++) { 370 if (strgtr(listCopy[i-1], listCopy[i])) { 371 char *temp = listCopy[i]; 372 listCopy[i] = listCopy[i-1]; 373 listCopy[i-1] = temp; 374 flag = 1; 375 } 376 } 377 } while(flag); 378 379 return listCopy; 380 } 381 382 void Sys_FreeFileList( char **list ) { 383 int i; 384 385 if ( !list ) { 386 return; 387 } 388 389 for ( i = 0 ; list[i] ; i++ ) { 390 Z_Free( list[i] ); 391 } 392 393 Z_Free( list ); 394 } 395 396 //======================================================== 397 398 399 /* 400 ================ 401 Sys_ScanForCD 402 403 Search all the drives to see if there is a valid CD to grab 404 the cddir from 405 ================ 406 */ 407 qboolean Sys_ScanForCD( void ) { 408 static char cddir[MAX_OSPATH]; 409 char drive[4]; 410 FILE *f; 411 char test[MAX_OSPATH]; 412 #if 0 413 // don't override a cdpath on the command line 414 if ( strstr( sys_cmdline, "cdpath" ) ) { 415 return; 416 } 417 #endif 418 419 drive[0] = 'c'; 420 drive[1] = ':'; 421 drive[2] = '\\'; 422 drive[3] = 0; 423 424 // scan the drives 425 for ( drive[0] = 'c' ; drive[0] <= 'z' ; drive[0]++ ) { 426 if ( GetDriveType (drive) != DRIVE_CDROM ) { 427 continue; 428 } 429 430 sprintf (cddir, "%s%s", drive, CD_BASEDIR); 431 sprintf (test, "%s\\%s", cddir, CD_EXE); 432 f = fopen( test, "r" ); 433 if ( f ) { 434 fclose (f); 435 return qtrue; 436 } else { 437 sprintf(cddir, "%s%s", drive, CD_BASEDIR_LINUX); 438 sprintf(test, "%s\\%s", cddir, CD_EXE_LINUX); 439 f = fopen( test, "r" ); 440 if ( f ) { 441 fclose (f); 442 return qtrue; 443 } 444 } 445 } 446 447 return qfalse; 448 } 449 450 /* 451 ================ 452 Sys_CheckCD 453 454 Return true if the proper CD is in the drive 455 ================ 456 */ 457 qboolean Sys_CheckCD( void ) { 458 // FIXME: mission pack 459 return qtrue; 460 //return Sys_ScanForCD(); 461 } 462 463 464 /* 465 ================ 466 Sys_GetClipboardData 467 468 ================ 469 */ 470 char *Sys_GetClipboardData( void ) { 471 char *data = NULL; 472 char *cliptext; 473 474 if ( OpenClipboard( NULL ) != 0 ) { 475 HANDLE hClipboardData; 476 477 if ( ( hClipboardData = GetClipboardData( CF_TEXT ) ) != 0 ) { 478 if ( ( cliptext = GlobalLock( hClipboardData ) ) != 0 ) { 479 data = Z_Malloc( GlobalSize( hClipboardData ) + 1 ); 480 Q_strncpyz( data, cliptext, GlobalSize( hClipboardData ) ); 481 GlobalUnlock( hClipboardData ); 482 483 strtok( data, "\n\r\b" ); 484 } 485 } 486 CloseClipboard(); 487 } 488 return data; 489 } 490 491 492 /* 493 ======================================================================== 494 495 LOAD/UNLOAD DLL 496 497 ======================================================================== 498 */ 499 500 /* 501 ================= 502 Sys_UnloadDll 503 504 ================= 505 */ 506 void Sys_UnloadDll( void *dllHandle ) { 507 if ( !dllHandle ) { 508 return; 509 } 510 if ( !FreeLibrary( dllHandle ) ) { 511 Com_Error (ERR_FATAL, "Sys_UnloadDll FreeLibrary failed"); 512 } 513 } 514 515 /* 516 ================= 517 Sys_LoadDll 518 519 Used to load a development dll instead of a virtual machine 520 521 TTimo: added some verbosity in debug 522 ================= 523 */ 524 extern char *FS_BuildOSPath( const char *base, const char *game, const char *qpath ); 525 526 // fqpath param added 7/20/02 by T.Ray - Sys_LoadDll is only called in vm.c at this time 527 // fqpath will be empty if dll not loaded, otherwise will hold fully qualified path of dll module loaded 528 // fqpath buffersize must be at least MAX_QPATH+1 bytes long 529 void * QDECL Sys_LoadDll( const char *name, char *fqpath , int (QDECL **entryPoint)(int, ...), 530 int (QDECL *systemcalls)(int, ...) ) { 531 static int lastWarning = 0; 532 HINSTANCE libHandle; 533 void (QDECL *dllEntry)( int (QDECL *syscallptr)(int, ...) ); 534 char *basepath; 535 char *cdpath; 536 char *gamedir; 537 char *fn; 538 #ifdef NDEBUG 539 int timestamp; 540 int ret; 541 #endif 542 char filename[MAX_QPATH]; 543 544 *fqpath = 0 ; // added 7/20/02 by T.Ray 545 546 Com_sprintf( filename, sizeof( filename ), "%sx86.dll", name ); 547 548 #ifdef NDEBUG 549 timestamp = Sys_Milliseconds(); 550 if( ((timestamp - lastWarning) > (5 * 60000)) && !Cvar_VariableIntegerValue( "dedicated" ) 551 && !Cvar_VariableIntegerValue( "com_blindlyLoadDLLs" ) ) { 552 if (FS_FileExists(filename)) { 553 lastWarning = timestamp; 554 ret = MessageBoxEx( NULL, "You are about to load a .DLL executable that\n" 555 "has not been verified for use with Quake III Arena.\n" 556 "This type of file can compromise the security of\n" 557 "your computer.\n\n" 558 "Select 'OK' if you choose to load it anyway.", 559 "Security Warning", MB_OKCANCEL | MB_ICONEXCLAMATION | MB_DEFBUTTON2 | MB_TOPMOST | MB_SETFOREGROUND, 560 MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ) ); 561 if( ret != IDOK ) { 562 return NULL; 563 } 564 } 565 } 566 #endif 567 568 #ifndef NDEBUG 569 libHandle = LoadLibrary( filename ); 570 if (libHandle) 571 Com_Printf("LoadLibrary '%s' ok\n", filename); 572 else 573 Com_Printf("LoadLibrary '%s' failed\n", filename); 574 if ( !libHandle ) { 575 #endif 576 basepath = Cvar_VariableString( "fs_basepath" ); 577 cdpath = Cvar_VariableString( "fs_cdpath" ); 578 gamedir = Cvar_VariableString( "fs_game" ); 579 580 fn = FS_BuildOSPath( basepath, gamedir, filename ); 581 libHandle = LoadLibrary( fn ); 582 #ifndef NDEBUG 583 if (libHandle) 584 Com_Printf("LoadLibrary '%s' ok\n", fn); 585 else 586 Com_Printf("LoadLibrary '%s' failed\n", fn); 587 #endif 588 589 if ( !libHandle ) { 590 if( cdpath[0] ) { 591 fn = FS_BuildOSPath( cdpath, gamedir, filename ); 592 libHandle = LoadLibrary( fn ); 593 #ifndef NDEBUG 594 if (libHandle) 595 Com_Printf("LoadLibrary '%s' ok\n", fn); 596 else 597 Com_Printf("LoadLibrary '%s' failed\n", fn); 598 #endif 599 } 600 601 if ( !libHandle ) { 602 return NULL; 603 } 604 } 605 #ifndef NDEBUG 606 } 607 #endif 608 609 dllEntry = ( void (QDECL *)( int (QDECL *)( int, ... ) ) )GetProcAddress( libHandle, "dllEntry" ); 610 *entryPoint = (int (QDECL *)(int,...))GetProcAddress( libHandle, "vmMain" ); 611 if ( !*entryPoint || !dllEntry ) { 612 FreeLibrary( libHandle ); 613 return NULL; 614 } 615 dllEntry( systemcalls ); 616 617 if ( libHandle ) Q_strncpyz ( fqpath , filename , MAX_QPATH ) ; // added 7/20/02 by T.Ray 618 return libHandle; 619 } 620 621 622 /* 623 ======================================================================== 624 625 BACKGROUND FILE STREAMING 626 627 ======================================================================== 628 */ 629 630 #if 1 631 632 void Sys_InitStreamThread( void ) { 633 } 634 635 void Sys_ShutdownStreamThread( void ) { 636 } 637 638 void Sys_BeginStreamedFile( fileHandle_t f, int readAhead ) { 639 } 640 641 void Sys_EndStreamedFile( fileHandle_t f ) { 642 } 643 644 int Sys_StreamedRead( void *buffer, int size, int count, fileHandle_t f ) { 645 return FS_Read( buffer, size * count, f ); 646 } 647 648 void Sys_StreamSeek( fileHandle_t f, int offset, int origin ) { 649 FS_Seek( f, offset, origin ); 650 } 651 652 653 #else 654 655 typedef struct { 656 fileHandle_t file; 657 byte *buffer; 658 qboolean eof; 659 qboolean active; 660 int bufferSize; 661 int streamPosition; // next byte to be returned by Sys_StreamRead 662 int threadPosition; // next byte to be read from file 663 } streamsIO_t; 664 665 typedef struct { 666 HANDLE threadHandle; 667 int threadId; 668 CRITICAL_SECTION crit; 669 streamsIO_t sIO[MAX_FILE_HANDLES]; 670 } streamState_t; 671 672 streamState_t stream; 673 674 /* 675 =============== 676 Sys_StreamThread 677 678 A thread will be sitting in this loop forever 679 ================ 680 */ 681 void Sys_StreamThread( void ) { 682 int buffer; 683 int count; 684 int readCount; 685 int bufferPoint; 686 int r, i; 687 688 while (1) { 689 Sleep( 10 ); 690 // EnterCriticalSection (&stream.crit); 691 692 for (i=1;i<MAX_FILE_HANDLES;i++) { 693 // if there is any space left in the buffer, fill it up 694 if ( stream.sIO[i].active && !stream.sIO[i].eof ) { 695 count = stream.sIO[i].bufferSize - (stream.sIO[i].threadPosition - stream.sIO[i].streamPosition); 696 if ( !count ) { 697 continue; 698 } 699 700 bufferPoint = stream.sIO[i].threadPosition % stream.sIO[i].bufferSize; 701 buffer = stream.sIO[i].bufferSize - bufferPoint; 702 readCount = buffer < count ? buffer : count; 703 704 r = FS_Read( stream.sIO[i].buffer + bufferPoint, readCount, stream.sIO[i].file ); 705 stream.sIO[i].threadPosition += r; 706 707 if ( r != readCount ) { 708 stream.sIO[i].eof = qtrue; 709 } 710 } 711 } 712 // LeaveCriticalSection (&stream.crit); 713 } 714 } 715 716 /* 717 =============== 718 Sys_InitStreamThread 719 720 ================ 721 */ 722 void Sys_InitStreamThread( void ) { 723 int i; 724 725 InitializeCriticalSection ( &stream.crit ); 726 727 // don't leave the critical section until there is a 728 // valid file to stream, which will cause the StreamThread 729 // to sleep without any overhead 730 // EnterCriticalSection( &stream.crit ); 731 732 stream.threadHandle = CreateThread( 733 NULL, // LPSECURITY_ATTRIBUTES lpsa, 734 0, // DWORD cbStack, 735 (LPTHREAD_START_ROUTINE)Sys_StreamThread, // LPTHREAD_START_ROUTINE lpStartAddr, 736 0, // LPVOID lpvThreadParm, 737 0, // DWORD fdwCreate, 738 &stream.threadId); 739 for(i=0;i<MAX_FILE_HANDLES;i++) { 740 stream.sIO[i].active = qfalse; 741 } 742 } 743 744 /* 745 =============== 746 Sys_ShutdownStreamThread 747 748 ================ 749 */ 750 void Sys_ShutdownStreamThread( void ) { 751 } 752 753 754 /* 755 =============== 756 Sys_BeginStreamedFile 757 758 ================ 759 */ 760 void Sys_BeginStreamedFile( fileHandle_t f, int readAhead ) { 761 if ( stream.sIO[f].file ) { 762 Sys_EndStreamedFile( stream.sIO[f].file ); 763 } 764 765 stream.sIO[f].file = f; 766 stream.sIO[f].buffer = Z_Malloc( readAhead ); 767 stream.sIO[f].bufferSize = readAhead; 768 stream.sIO[f].streamPosition = 0; 769 stream.sIO[f].threadPosition = 0; 770 stream.sIO[f].eof = qfalse; 771 stream.sIO[f].active = qtrue; 772 773 // let the thread start running 774 // LeaveCriticalSection( &stream.crit ); 775 } 776 777 /* 778 =============== 779 Sys_EndStreamedFile 780 781 ================ 782 */ 783 void Sys_EndStreamedFile( fileHandle_t f ) { 784 if ( f != stream.sIO[f].file ) { 785 Com_Error( ERR_FATAL, "Sys_EndStreamedFile: wrong file"); 786 } 787 // don't leave critical section until another stream is started 788 EnterCriticalSection( &stream.crit ); 789 790 stream.sIO[f].file = 0; 791 stream.sIO[f].active = qfalse; 792 793 Z_Free( stream.sIO[f].buffer ); 794 795 LeaveCriticalSection( &stream.crit ); 796 } 797 798 799 /* 800 =============== 801 Sys_StreamedRead 802 803 ================ 804 */ 805 int Sys_StreamedRead( void *buffer, int size, int count, fileHandle_t f ) { 806 int available; 807 int remaining; 808 int sleepCount; 809 int copy; 810 int bufferCount; 811 int bufferPoint; 812 byte *dest; 813 814 if (stream.sIO[f].active == qfalse) { 815 Com_Error( ERR_FATAL, "Streamed read with non-streaming file" ); 816 } 817 818 dest = (byte *)buffer; 819 remaining = size * count; 820 821 if ( remaining <= 0 ) { 822 Com_Error( ERR_FATAL, "Streamed read with non-positive size" ); 823 } 824 825 sleepCount = 0; 826 while ( remaining > 0 ) { 827 available = stream.sIO[f].threadPosition - stream.sIO[f].streamPosition; 828 if ( !available ) { 829 if ( stream.sIO[f].eof ) { 830 break; 831 } 832 if ( sleepCount == 1 ) { 833 Com_DPrintf( "Sys_StreamedRead: waiting\n" ); 834 } 835 if ( ++sleepCount > 100 ) { 836 Com_Error( ERR_FATAL, "Sys_StreamedRead: thread has died"); 837 } 838 Sleep( 10 ); 839 continue; 840 } 841 842 EnterCriticalSection( &stream.crit ); 843 844 bufferPoint = stream.sIO[f].streamPosition % stream.sIO[f].bufferSize; 845 bufferCount = stream.sIO[f].bufferSize - bufferPoint; 846 847 copy = available < bufferCount ? available : bufferCount; 848 if ( copy > remaining ) { 849 copy = remaining; 850 } 851 memcpy( dest, stream.sIO[f].buffer + bufferPoint, copy ); 852 stream.sIO[f].streamPosition += copy; 853 dest += copy; 854 remaining -= copy; 855 856 LeaveCriticalSection( &stream.crit ); 857 } 858 859 return (count * size - remaining) / size; 860 } 861 862 /* 863 =============== 864 Sys_StreamSeek 865 866 ================ 867 */ 868 void Sys_StreamSeek( fileHandle_t f, int offset, int origin ) { 869 870 // halt the thread 871 EnterCriticalSection( &stream.crit ); 872 873 // clear to that point 874 FS_Seek( f, offset, origin ); 875 stream.sIO[f].streamPosition = 0; 876 stream.sIO[f].threadPosition = 0; 877 stream.sIO[f].eof = qfalse; 878 879 // let the thread start running at the new position 880 LeaveCriticalSection( &stream.crit ); 881 } 882 883 #endif 884 885 /* 886 ======================================================================== 887 888 EVENT LOOP 889 890 ======================================================================== 891 */ 892 893 #define MAX_QUED_EVENTS 256 894 #define MASK_QUED_EVENTS ( MAX_QUED_EVENTS - 1 ) 895 896 sysEvent_t eventQue[MAX_QUED_EVENTS]; 897 int eventHead, eventTail; 898 byte sys_packetReceived[MAX_MSGLEN]; 899 900 /* 901 ================ 902 Sys_QueEvent 903 904 A time of 0 will get the current time 905 Ptr should either be null, or point to a block of data that can 906 be freed by the game later. 907 ================ 908 */ 909 void Sys_QueEvent( int time, sysEventType_t type, int value, int value2, int ptrLength, void *ptr ) { 910 sysEvent_t *ev; 911 912 ev = &eventQue[ eventHead & MASK_QUED_EVENTS ]; 913 if ( eventHead - eventTail >= MAX_QUED_EVENTS ) { 914 Com_Printf("Sys_QueEvent: overflow\n"); 915 // we are discarding an event, but don't leak memory 916 if ( ev->evPtr ) { 917 Z_Free( ev->evPtr ); 918 } 919 eventTail++; 920 } 921 922 eventHead++; 923 924 if ( time == 0 ) { 925 time = Sys_Milliseconds(); 926 } 927 928 ev->evTime = time; 929 ev->evType = type; 930 ev->evValue = value; 931 ev->evValue2 = value2; 932 ev->evPtrLength = ptrLength; 933 ev->evPtr = ptr; 934 } 935 936 /* 937 ================ 938 Sys_GetEvent 939 940 ================ 941 */ 942 sysEvent_t Sys_GetEvent( void ) { 943 MSG msg; 944 sysEvent_t ev; 945 char *s; 946 msg_t netmsg; 947 netadr_t adr; 948 949 // return if we have data 950 if ( eventHead > eventTail ) { 951 eventTail++; 952 return eventQue[ ( eventTail - 1 ) & MASK_QUED_EVENTS ]; 953 } 954 955 // pump the message loop 956 while (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) { 957 if ( !GetMessage (&msg, NULL, 0, 0) ) { 958 Com_Quit_f(); 959 } 960 961 // save the msg time, because wndprocs don't have access to the timestamp 962 g_wv.sysMsgTime = msg.time; 963 964 TranslateMessage (&msg); 965 DispatchMessage (&msg); 966 } 967 968 // check for console commands 969 s = Sys_ConsoleInput(); 970 if ( s ) { 971 char *b; 972 int len; 973 974 len = strlen( s ) + 1; 975 b = Z_Malloc( len ); 976 Q_strncpyz( b, s, len-1 ); 977 Sys_QueEvent( 0, SE_CONSOLE, 0, 0, len, b ); 978 } 979 980 // check for network packets 981 MSG_Init( &netmsg, sys_packetReceived, sizeof( sys_packetReceived ) ); 982 if ( Sys_GetPacket ( &adr, &netmsg ) ) { 983 netadr_t *buf; 984 int len; 985 986 // copy out to a seperate buffer for qeueing 987 // the readcount stepahead is for SOCKS support 988 len = sizeof( netadr_t ) + netmsg.cursize - netmsg.readcount; 989 buf = Z_Malloc( len ); 990 *buf = adr; 991 memcpy( buf+1, &netmsg.data[netmsg.readcount], netmsg.cursize - netmsg.readcount ); 992 Sys_QueEvent( 0, SE_PACKET, 0, 0, len, buf ); 993 } 994 995 // return if we have data 996 if ( eventHead > eventTail ) { 997 eventTail++; 998 return eventQue[ ( eventTail - 1 ) & MASK_QUED_EVENTS ]; 999 } 1000 1001 // create an empty event to return 1002 1003 memset( &ev, 0, sizeof( ev ) ); 1004 ev.evTime = timeGetTime(); 1005 1006 return ev; 1007 } 1008 1009 //================================================================ 1010 1011 /* 1012 ================= 1013 Sys_In_Restart_f 1014 1015 Restart the input subsystem 1016 ================= 1017 */ 1018 void Sys_In_Restart_f( void ) { 1019 IN_Shutdown(); 1020 IN_Init(); 1021 } 1022 1023 1024 /* 1025 ================= 1026 Sys_Net_Restart_f 1027 1028 Restart the network subsystem 1029 ================= 1030 */ 1031 void Sys_Net_Restart_f( void ) { 1032 NET_Restart(); 1033 } 1034 1035 1036 /* 1037 ================ 1038 Sys_Init 1039 1040 Called after the common systems (cvars, files, etc) 1041 are initialized 1042 ================ 1043 */ 1044 #define OSR2_BUILD_NUMBER 1111 1045 #define WIN98_BUILD_NUMBER 1998 1046 1047 void Sys_Init( void ) { 1048 int cpuid; 1049 1050 // make sure the timer is high precision, otherwise 1051 // NT gets 18ms resolution 1052 timeBeginPeriod( 1 ); 1053 1054 Cmd_AddCommand ("in_restart", Sys_In_Restart_f); 1055 Cmd_AddCommand ("net_restart", Sys_Net_Restart_f); 1056 1057 g_wv.osversion.dwOSVersionInfoSize = sizeof( g_wv.osversion ); 1058 1059 if (!GetVersionEx (&g_wv.osversion)) 1060 Sys_Error ("Couldn't get OS info"); 1061 1062 if (g_wv.osversion.dwMajorVersion < 4) 1063 Sys_Error ("Quake3 requires Windows version 4 or greater"); 1064 if (g_wv.osversion.dwPlatformId == VER_PLATFORM_WIN32s) 1065 Sys_Error ("Quake3 doesn't run on Win32s"); 1066 1067 if ( g_wv.osversion.dwPlatformId == VER_PLATFORM_WIN32_NT ) 1068 { 1069 Cvar_Set( "arch", "winnt" ); 1070 } 1071 else if ( g_wv.osversion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ) 1072 { 1073 if ( LOWORD( g_wv.osversion.dwBuildNumber ) >= WIN98_BUILD_NUMBER ) 1074 { 1075 Cvar_Set( "arch", "win98" ); 1076 } 1077 else if ( LOWORD( g_wv.osversion.dwBuildNumber ) >= OSR2_BUILD_NUMBER ) 1078 { 1079 Cvar_Set( "arch", "win95 osr2.x" ); 1080 } 1081 else 1082 { 1083 Cvar_Set( "arch", "win95" ); 1084 } 1085 } 1086 else 1087 { 1088 Cvar_Set( "arch", "unknown Windows variant" ); 1089 } 1090 1091 // save out a couple things in rom cvars for the renderer to access 1092 Cvar_Get( "win_hinstance", va("%i", (int)g_wv.hInstance), CVAR_ROM ); 1093 Cvar_Get( "win_wndproc", va("%i", (int)MainWndProc), CVAR_ROM ); 1094 1095 // 1096 // figure out our CPU 1097 // 1098 Cvar_Get( "sys_cpustring", "detect", 0 ); 1099 if ( !Q_stricmp( Cvar_VariableString( "sys_cpustring"), "detect" ) ) 1100 { 1101 Com_Printf( "...detecting CPU, found " ); 1102 1103 cpuid = Sys_GetProcessorId(); 1104 1105 switch ( cpuid ) 1106 { 1107 case CPUID_GENERIC: 1108 Cvar_Set( "sys_cpustring", "generic" ); 1109 break; 1110 case CPUID_INTEL_UNSUPPORTED: 1111 Cvar_Set( "sys_cpustring", "x86 (pre-Pentium)" ); 1112 break; 1113 case CPUID_INTEL_PENTIUM: 1114 Cvar_Set( "sys_cpustring", "x86 (P5/PPro, non-MMX)" ); 1115 break; 1116 case CPUID_INTEL_MMX: 1117 Cvar_Set( "sys_cpustring", "x86 (P5/Pentium2, MMX)" ); 1118 break; 1119 case CPUID_INTEL_KATMAI: 1120 Cvar_Set( "sys_cpustring", "Intel Pentium III" ); 1121 break; 1122 case CPUID_AMD_3DNOW: 1123 Cvar_Set( "sys_cpustring", "AMD w/ 3DNow!" ); 1124 break; 1125 case CPUID_AXP: 1126 Cvar_Set( "sys_cpustring", "Alpha AXP" ); 1127 break; 1128 default: 1129 Com_Error( ERR_FATAL, "Unknown cpu type %d\n", cpuid ); 1130 break; 1131 } 1132 } 1133 else 1134 { 1135 Com_Printf( "...forcing CPU type to " ); 1136 if ( !Q_stricmp( Cvar_VariableString( "sys_cpustring" ), "generic" ) ) 1137 { 1138 cpuid = CPUID_GENERIC; 1139 } 1140 else if ( !Q_stricmp( Cvar_VariableString( "sys_cpustring" ), "x87" ) ) 1141 { 1142 cpuid = CPUID_INTEL_PENTIUM; 1143 } 1144 else if ( !Q_stricmp( Cvar_VariableString( "sys_cpustring" ), "mmx" ) ) 1145 { 1146 cpuid = CPUID_INTEL_MMX; 1147 } 1148 else if ( !Q_stricmp( Cvar_VariableString( "sys_cpustring" ), "3dnow" ) ) 1149 { 1150 cpuid = CPUID_AMD_3DNOW; 1151 } 1152 else if ( !Q_stricmp( Cvar_VariableString( "sys_cpustring" ), "PentiumIII" ) ) 1153 { 1154 cpuid = CPUID_INTEL_KATMAI; 1155 } 1156 else if ( !Q_stricmp( Cvar_VariableString( "sys_cpustring" ), "axp" ) ) 1157 { 1158 cpuid = CPUID_AXP; 1159 } 1160 else 1161 { 1162 Com_Printf( "WARNING: unknown sys_cpustring '%s'\n", Cvar_VariableString( "sys_cpustring" ) ); 1163 cpuid = CPUID_GENERIC; 1164 } 1165 } 1166 Cvar_SetValue( "sys_cpuid", cpuid ); 1167 Com_Printf( "%s\n", Cvar_VariableString( "sys_cpustring" ) ); 1168 1169 Cvar_Set( "username", Sys_GetCurrentUser() ); 1170 1171 IN_Init(); // FIXME: not in dedicated? 1172 } 1173 1174 1175 //======================================================================= 1176 1177 int totalMsec, countMsec; 1178 1179 /* 1180 ================== 1181 WinMain 1182 1183 ================== 1184 */ 1185 int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { 1186 char cwd[MAX_OSPATH]; 1187 int startTime, endTime; 1188 1189 // should never get a previous instance in Win32 1190 if ( hPrevInstance ) { 1191 return 0; 1192 } 1193 1194 g_wv.hInstance = hInstance; 1195 Q_strncpyz( sys_cmdline, lpCmdLine, sizeof( sys_cmdline ) ); 1196 1197 // done before Com/Sys_Init since we need this for error output 1198 Sys_CreateConsole(); 1199 1200 // no abort/retry/fail errors 1201 SetErrorMode( SEM_FAILCRITICALERRORS ); 1202 1203 // get the initial time base 1204 Sys_Milliseconds(); 1205 #if 0 1206 // if we find the CD, add a +set cddir xxx command line 1207 Sys_ScanForCD(); 1208 #endif 1209 1210 Sys_InitStreamThread(); 1211 1212 Com_Init( sys_cmdline ); 1213 NET_Init(); 1214 1215 _getcwd (cwd, sizeof(cwd)); 1216 Com_Printf("Working directory: %s\n", cwd); 1217 1218 // hide the early console since we've reached the point where we 1219 // have a working graphics subsystems 1220 if ( !com_dedicated->integer && !com_viewlog->integer ) { 1221 Sys_ShowConsole( 0, qfalse ); 1222 } 1223 1224 // main game loop 1225 while( 1 ) { 1226 // if not running as a game client, sleep a bit 1227 if ( g_wv.isMinimized || ( com_dedicated && com_dedicated->integer ) ) { 1228 Sleep( 5 ); 1229 } 1230 1231 // set low precision every frame, because some system calls 1232 // reset it arbitrarily 1233 // _controlfp( _PC_24, _MCW_PC ); 1234 // _controlfp( -1, _MCW_EM ); // no exceptions, even if some crappy 1235 // syscall turns them back on! 1236 1237 startTime = Sys_Milliseconds(); 1238 1239 // make sure mouse and joystick are only called once a frame 1240 IN_Frame(); 1241 1242 // run the game 1243 Com_Frame(); 1244 1245 endTime = Sys_Milliseconds(); 1246 totalMsec += endTime - startTime; 1247 countMsec++; 1248 } 1249 1250 // never gets here 1251 } 1252 1253