File: win32\sys_win.c

    1 /*
    2 Copyright (C) 1997-2001 Id Software, Inc.
    3 
    4 This program is free software; you can redistribute it and/or
    5 modify it under the terms of the GNU General Public License
    6 as published by the Free Software Foundation; either version 2
    7 of the License, or (at your option) any later version.
    8 
    9 This program is distributed in the hope that it will be useful,
   10 but WITHOUT ANY WARRANTY; without even the implied warranty of
   11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
   12 
   13 See the GNU General Public License for more details.
   14 
   15 You should have received a copy of the GNU General Public License
   16 along with this program; if not, write to the Free Software
   17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
   18 
   19 */
   20 // sys_win.h
   21 
   22 #include "../qcommon/qcommon.h"
   23 #include "winquake.h"
   24 #include "resource.h"
   25 #include <errno.h>
   26 #include <float.h>
   27 #include <fcntl.h>
   28 #include <stdio.h>
   29 #include <direct.h>
   30 #include <io.h>
   31 #include <conio.h>
   32 #include "../win32/conproc.h"
   33 
   34 #define MINIMUM_WIN_MEMORY      0x0a00000
   35 #define MAXIMUM_WIN_MEMORY      0x1000000
   36 
   37 //#define DEMO
   38 
   39 qboolean s_win95;
   40 
   41 int                     starttime;
   42 int                     ActiveApp;
   43 qboolean        Minimized;
   44 
   45 static HANDLE           hinput, houtput;
   46 
   47 unsigned        sys_msg_time;
   48 unsigned        sys_frame_time;
   49 
   50 
   51 static HANDLE           qwclsemaphore;
   52 
   53 #define MAX_NUM_ARGVS   128
   54 int                     argc;
   55 char            *argv[MAX_NUM_ARGVS];
   56 
   57 
   58 /*
   59 ===============================================================================
   60 
   61 SYSTEM IO
   62 
   63 ===============================================================================
   64 */
   65 
   66 
   67 void Sys_Error (char *error, ...)
   68 {
   69         va_list         argptr;
   70         char            text[1024];
   71 
   72         CL_Shutdown ();
   73         Qcommon_Shutdown ();
   74 
   75         va_start (argptr, error);
   76         vsprintf (text, error, argptr);
   77         va_end (argptr);
   78 
   79         MessageBox(NULL, text, "Error", 0 /* MB_OK */ );
   80 
   81         if (qwclsemaphore)
   82                 CloseHandle (qwclsemaphore);
   83 
   84 // shut down QHOST hooks if necessary
   85         DeinitConProc ();
   86 
   87         exit (1);
   88 }
   89 
   90 void Sys_Quit (void)
   91 {
   92         timeEndPeriod( 1 );
   93 
   94         CL_Shutdown();
   95         Qcommon_Shutdown ();
   96         CloseHandle (qwclsemaphore);
   97         if (dedicated && dedicated->value)
   98                 FreeConsole ();
   99 
  100 // shut down QHOST hooks if necessary
  101         DeinitConProc ();
  102 
  103         exit (0);
  104 }
  105 
  106 
  107 void WinError (void)
  108 {
  109         LPVOID lpMsgBuf;
  110 
  111         FormatMessage( 
  112                 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
  113                 NULL,
  114                 GetLastError(),
  115                 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
  116                 (LPTSTR) &lpMsgBuf,
  117                 0,
  118                 NULL 
  119         );
  120 
  121         // Display the string.
  122         MessageBox( NULL, lpMsgBuf, "GetLastError", MB_OK|MB_ICONINFORMATION );
  123 
  124         // Free the buffer.
  125         LocalFree( lpMsgBuf );
  126 }
  127 
  128 //================================================================
  129 
  130 
  131 /*
  132 ================
  133 Sys_ScanForCD
  134 
  135 ================
  136 */
  137 char *Sys_ScanForCD (void)
  138 {
  139         static char     cddir[MAX_OSPATH];
  140         static qboolean done;
  141 #ifndef DEMO
  142         char            drive[4];
  143         FILE            *f;
  144         char            test[MAX_QPATH];
  145 
  146         if (done)               // don't re-check
  147                 return cddir;
  148 
  149         // no abort/retry/fail errors
  150         SetErrorMode (SEM_FAILCRITICALERRORS);
  151 
  152         drive[0] = 'c';
  153         drive[1] = ':';
  154         drive[2] = '\\';
  155         drive[3] = 0;
  156 
  157         done = true;
  158 
  159         // scan the drives
  160         for (drive[0] = 'c' ; drive[0] <= 'z' ; drive[0]++)
  161         {
  162                 // where activision put the stuff...
  163                 sprintf (cddir, "%sinstall\\data", drive);
  164                 sprintf (test, "%sinstall\\data\\quake2.exe", drive);
  165                 f = fopen(test, "r");
  166                 if (f)
  167                 {
  168                         fclose (f);
  169                         if (GetDriveType (drive) == DRIVE_CDROM)
  170                                 return cddir;
  171                 }
  172         }
  173 #endif
  174 
  175         cddir[0] = 0;
  176         
  177         return NULL;
  178 }
  179 
  180 /*
  181 ================
  182 Sys_CopyProtect
  183 
  184 ================
  185 */
  186 void    Sys_CopyProtect (void)
  187 {
  188 #ifndef DEMO
  189         char    *cddir;
  190 
  191         cddir = Sys_ScanForCD();
  192         if (!cddir[0])
  193                 Com_Error (ERR_FATAL, "You must have the Quake2 CD in the drive to play.");
  194 #endif
  195 }
  196 
  197 
  198 //================================================================
  199 
  200 
  201 /*
  202 ================
  203 Sys_Init
  204 ================
  205 */
  206 void Sys_Init (void)
  207 {
  208         OSVERSIONINFO   vinfo;
  209 
  210 #if 0
211 // allocate a named semaphore on the client so the 212 // front end can tell if it is alive 213 214 // mutex will fail if semephore already exists 215 qwclsemaphore = CreateMutex( 216 NULL, /* Security attributes */ 217 0, /* owner */ 218 "qwcl"); /* Semaphore name */ 219 if (!qwclsemaphore) 220 Sys_Error ("QWCL is already running on this system"); 221 CloseHandle (qwclsemaphore); 222 223 qwclsemaphore = CreateSemaphore( 224 NULL, /* Security attributes */ 225 0, /* Initial count */ 226 1, /* Maximum count */ 227 "qwcl"); /* Semaphore name */
228 #endif 229 230 timeBeginPeriod( 1 ); 231 232 vinfo.dwOSVersionInfoSize = sizeof(vinfo); 233 234 if (!GetVersionEx (&vinfo)) 235 Sys_Error ("Couldn't get OS info"); 236 237 if (vinfo.dwMajorVersion < 4) 238 Sys_Error ("Quake2 requires windows version 4 or greater"); 239 if (vinfo.dwPlatformId == VER_PLATFORM_WIN32s) 240 Sys_Error ("Quake2 doesn't run on Win32s"); 241 else if ( vinfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ) 242 s_win95 = true; 243 244 if (dedicated->value) 245 { 246 if (!AllocConsole ()) 247 Sys_Error ("Couldn't create dedicated server console"); 248 hinput = GetStdHandle (STD_INPUT_HANDLE); 249 houtput = GetStdHandle (STD_OUTPUT_HANDLE); 250 251 // let QHOST hook in 252 InitConProc (argc, argv); 253 } 254 } 255 256 257 static char console_text[256]; 258 static int console_textlen; 259 260 /* 261 ================ 262 Sys_ConsoleInput 263 ================ 264 */ 265 char *Sys_ConsoleInput (void) 266 { 267 INPUT_RECORD recs[1024]; 268 int dummy; 269 int ch, numread, numevents; 270 271 if (!dedicated || !dedicated->value) 272 return NULL; 273 274 275 for ( ;; ) 276 { 277 if (!GetNumberOfConsoleInputEvents (hinput, &numevents)) 278 Sys_Error ("Error getting # of console events"); 279 280 if (numevents <= 0) 281 break; 282 283 if (!ReadConsoleInput(hinput, recs, 1, &numread)) 284 Sys_Error ("Error reading console input"); 285 286 if (numread != 1) 287 Sys_Error ("Couldn't read console input"); 288 289 if (recs[0].EventType == KEY_EVENT) 290 { 291 if (!recs[0].Event.KeyEvent.bKeyDown) 292 { 293 ch = recs[0].Event.KeyEvent.uChar.AsciiChar; 294 295 switch (ch) 296 { 297 case '\r': 298 WriteFile(houtput, "\r\n", 2, &dummy, NULL); 299 300 if (console_textlen) 301 { 302 console_text[console_textlen] = 0; 303 console_textlen = 0; 304 return console_text; 305 } 306 break; 307 308 case '\b': 309 if (console_textlen) 310 { 311 console_textlen--; 312 WriteFile(houtput, "\b \b", 3, &dummy, NULL); 313 } 314 break; 315 316 default: 317 if (ch >= ' ') 318 { 319 if (console_textlen < sizeof(console_text)-2) 320 { 321 WriteFile(houtput, &ch, 1, &dummy, NULL); 322 console_text[console_textlen] = ch; 323 console_textlen++; 324 } 325 } 326 327 break; 328 329 } 330 } 331 } 332 } 333 334 return NULL; 335 } 336 337 338 /* 339 ================ 340 Sys_ConsoleOutput 341 342 Print text to the dedicated console 343 ================ 344 */ 345 void Sys_ConsoleOutput (char *string) 346 { 347 int dummy; 348 char text[256]; 349 350 if (!dedicated || !dedicated->value) 351 return; 352 353 if (console_textlen) 354 { 355 text[0] = '\r'; 356 memset(&text[1], ' ', console_textlen); 357 text[console_textlen+1] = '\r'; 358 text[console_textlen+2] = 0; 359 WriteFile(houtput, text, console_textlen+2, &dummy, NULL); 360 } 361 362 WriteFile(houtput, string, strlen(string), &dummy, NULL); 363 364 if (console_textlen) 365 WriteFile(houtput, console_text, console_textlen, &dummy, NULL); 366 } 367 368 369 /* 370 ================ 371 Sys_SendKeyEvents 372 373 Send Key_Event calls 374 ================ 375 */ 376 void Sys_SendKeyEvents (void) 377 { 378 MSG msg; 379 380 while (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) 381 { 382 if (!GetMessage (&msg, NULL, 0, 0)) 383 Sys_Quit (); 384 sys_msg_time = msg.time; 385 TranslateMessage (&msg); 386 DispatchMessage (&msg); 387 } 388 389 // grab frame time 390 sys_frame_time = timeGetTime(); // FIXME: should this be at start? 391 } 392 393 394 395 /* 396 ================ 397 Sys_GetClipboardData 398 399 ================ 400 */ 401 char *Sys_GetClipboardData( void ) 402 { 403 char *data = NULL; 404 char *cliptext; 405 406 if ( OpenClipboard( NULL ) != 0 ) 407 { 408 HANDLE hClipboardData; 409 410 if ( ( hClipboardData = GetClipboardData( CF_TEXT ) ) != 0 ) 411 { 412 if ( ( cliptext = GlobalLock( hClipboardData ) ) != 0 ) 413 { 414 data = malloc( GlobalSize( hClipboardData ) + 1 ); 415 strcpy( data, cliptext ); 416 GlobalUnlock( hClipboardData ); 417 } 418 } 419 CloseClipboard(); 420 } 421 return data; 422 } 423 424 /* 425 ============================================================================== 426 427 WINDOWS CRAP 428 429 ============================================================================== 430 */ 431 432 /* 433 ================= 434 Sys_AppActivate 435 ================= 436 */ 437 void Sys_AppActivate (void) 438 { 439 ShowWindow ( cl_hwnd, SW_RESTORE); 440 SetForegroundWindow ( cl_hwnd ); 441 } 442 443 /* 444 ======================================================================== 445 446 GAME DLL 447 448 ======================================================================== 449 */ 450 451 static HINSTANCE game_library; 452 453 /* 454 ================= 455 Sys_UnloadGame 456 ================= 457 */ 458 void Sys_UnloadGame (void) 459 { 460 if (!FreeLibrary (game_library)) 461 Com_Error (ERR_FATAL, "FreeLibrary failed for game library"); 462 game_library = NULL; 463 } 464 465 /* 466 ================= 467 Sys_GetGameAPI 468 469 Loads the game dll 470 ================= 471 */ 472 void *Sys_GetGameAPI (void *parms) 473 { 474 void *(*GetGameAPI) (void *); 475 char name[MAX_OSPATH]; 476 char *path; 477 char cwd[MAX_OSPATH]; 478 #if defined _M_IX86 479 const char *gamename = "gamex86.dll"; 480 481 #ifdef NDEBUG
482 const char *debugdir = "release";
483 #else 484 const char *debugdir = "debug"; 485 #endif 486
487 #elif defined _M_ALPHA 488 const char *gamename = "gameaxp.dll"; 489 490 #ifdef NDEBUG 491 const char *debugdir = "releaseaxp"; 492 #else 493 const char *debugdir = "debugaxp"; 494 #endif 495
496 #endif 497 498 if (game_library) 499 Com_Error (ERR_FATAL, "Sys_GetGameAPI without Sys_UnloadingGame"); 500 501 // check the current debug directory first for development purposes 502 _getcwd (cwd, sizeof(cwd)); 503 Com_sprintf (name, sizeof(name), "%s/%s/%s", cwd, debugdir, gamename); 504 game_library = LoadLibrary ( name ); 505 if (game_library) 506 { 507 Com_DPrintf ("LoadLibrary (%s)\n", name); 508 } 509 else 510 { 511 #ifdef DEBUG
512 // check the current directory for other development purposes 513 Com_sprintf (name, sizeof(name), "%s/%s", cwd, gamename); 514 game_library = LoadLibrary ( name ); 515 if (game_library) 516 { 517 Com_DPrintf ("LoadLibrary (%s)\n", name); 518 } 519 else
520 #endif 521 { 522 // now run through the search paths 523 path = NULL; 524 while (1) 525 { 526 path = FS_NextPath (path); 527 if (!path) 528 return NULL; // couldn't find one anywhere 529 Com_sprintf (name, sizeof(name), "%s/%s", path, gamename); 530 game_library = LoadLibrary (name); 531 if (game_library) 532 { 533 Com_DPrintf ("LoadLibrary (%s)\n",name); 534 break; 535 } 536 } 537 } 538 } 539 540 GetGameAPI = (void *)GetProcAddress (game_library, "GetGameAPI"); 541 if (!GetGameAPI) 542 { 543 Sys_UnloadGame (); 544 return NULL; 545 } 546 547 return GetGameAPI (parms); 548 } 549 550 //======================================================================= 551 552 553 /* 554 ================== 555 ParseCommandLine 556 557 ================== 558 */ 559 void ParseCommandLine (LPSTR lpCmdLine) 560 { 561 argc = 1; 562 argv[0] = "exe"; 563 564 while (*lpCmdLine && (argc < MAX_NUM_ARGVS)) 565 { 566 while (*lpCmdLine && ((*lpCmdLine <= 32) || (*lpCmdLine > 126))) 567 lpCmdLine++; 568 569 if (*lpCmdLine) 570 { 571 argv[argc] = lpCmdLine; 572 argc++; 573 574 while (*lpCmdLine && ((*lpCmdLine > 32) && (*lpCmdLine <= 126))) 575 lpCmdLine++; 576 577 if (*lpCmdLine) 578 { 579 *lpCmdLine = 0; 580 lpCmdLine++; 581 } 582 583 } 584 } 585 586 } 587 588 /* 589 ================== 590 WinMain 591 592 ================== 593 */ 594 HINSTANCE global_hInstance; 595 596 int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) 597 { 598 MSG msg; 599 int time, oldtime, newtime; 600 char *cddir; 601 602 /* previous instances do not exist in Win32 */ 603 if (hPrevInstance) 604 return 0; 605 606 global_hInstance = hInstance; 607 608 ParseCommandLine (lpCmdLine); 609 610 // if we find the CD, add a +set cddir xxx command line 611 cddir = Sys_ScanForCD (); 612 if (cddir && argc < MAX_NUM_ARGVS - 3) 613 { 614 int i; 615 616 // don't override a cddir on the command line 617 for (i=0 ; i<argc ; i++) 618 if (!strcmp(argv[i], "cddir")) 619 break; 620 if (i == argc) 621 { 622 argv[argc++] = "+set"; 623 argv[argc++] = "cddir"; 624 argv[argc++] = cddir; 625 } 626 } 627 628 Qcommon_Init (argc, argv); 629 oldtime = Sys_Milliseconds (); 630 631 /* main window message loop */ 632 while (1) 633 { 634 // if at a full screen console, don't update unless needed 635 if (Minimized || (dedicated && dedicated->value) ) 636 { 637 Sleep (1); 638 } 639 640 while (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) 641 { 642 if (!GetMessage (&msg, NULL, 0, 0)) 643 Com_Quit (); 644 sys_msg_time = msg.time; 645 TranslateMessage (&msg); 646 DispatchMessage (&msg); 647 } 648 649 do 650 { 651 newtime = Sys_Milliseconds (); 652 time = newtime - oldtime; 653 } while (time < 1); 654 // Con_Printf ("time:%5.2f - %5.2f = %5.2f\n", newtime, oldtime, time); 655 656 // _controlfp( ~( _EM_ZERODIVIDE /*| _EM_INVALID*/ ), _MCW_EM ); 657 _controlfp( _PC_24, _MCW_PC ); 658 Qcommon_Frame (time); 659 660 oldtime = newtime; 661 } 662 663 // never gets here 664 return TRUE; 665 } 666