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
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
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
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