1 /*
2  * sys_win.c -- Windows system interface code
3  * $Id: sys_win.c 6015 2018-02-21 17:30:51Z sezero $
4  *
5  * Copyright (C) 1996-1997  Id Software, Inc.
6  * Copyright (C) 2005-2012  O.Sezer <sezero@users.sourceforge.net>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or (at
11  * your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16  *
17  * See the GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License along
20  * with this program; if not, write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22  */
23 
24 #include "quakedef.h"
25 #include "winquake.h"
26 #include <mmsystem.h>
27 #include "resource.h"
28 #include "debuglog.h"
29 
30 
31 // heapsize: minimum 16mb, standart 32 mb, max is 96 mb.
32 // -heapsize argument will abide by these min/max settings
33 // unless the -forcemem argument is used
34 #define MIN_MEM_ALLOC	0x1000000
35 #define STD_MEM_ALLOC	0x2000000
36 #define MAX_MEM_ALLOC	0x6000000
37 
38 #define CONSOLE_ERROR_TIMEOUT	60.0	/* # of seconds to wait on Sys_Error running dedicated before exiting */
39 #define PAUSE_SLEEP		50	/* sleep time on pause or minimization		*/
40 #define NOT_FOCUS_SLEEP		20	/* sleep time when not focus			*/
41 
42 cvar_t		sys_nostdout = {"sys_nostdout", "0", CVAR_NONE};
43 cvar_t		sys_throttle = {"sys_throttle", "0.02", CVAR_ARCHIVE};
44 
45 qboolean	ActiveApp, Minimized;
46 qboolean	Win95, Win95old, WinNT, WinVista;
47 
48 qboolean		isDedicated;
49 
50 #define	TIME_WRAP_VALUE	(~(DWORD)0)
51 static DWORD		starttime;
52 static qboolean		sc_return_on_enter = false;
53 static HANDLE		hinput, houtput;
54 
55 static HANDLE	tevent;
56 
57 static volatile int	sys_checksum;
58 
59 
60 /*
61 ================
62 Sys_PageIn
63 ================
64 */
Sys_PageIn(void * ptr,int size)65 static void Sys_PageIn (void *ptr, int size)
66 {
67 	byte	*x;
68 	int		m, n;
69 
70 // touch all the memory to make sure it's there. The 16-page skip is to
71 // keep Win 95 from thinking we're trying to page ourselves in (we are
72 // doing that, of course, but there's no reason we shouldn't)
73 	x = (byte *)ptr;
74 
75 	for (n = 0; n < 4; n++)
76 	{
77 		for (m = 0; m < (size - 16 * 0x1000); m += 4)
78 		{
79 			sys_checksum += *(int *)&x[m];
80 			sys_checksum += *(int *)&x[m + 16 * 0x1000];
81 		}
82 	}
83 }
84 
85 
86 /*
87 ===============================================================================
88 
89 FILE IO
90 
91 ===============================================================================
92 */
93 
Sys_mkdir(const char * path,qboolean crash)94 int Sys_mkdir (const char *path, qboolean crash)
95 {
96 	if (CreateDirectory(path, NULL) != 0)
97 		return 0;
98 	if (GetLastError() == ERROR_ALREADY_EXISTS)
99 		return 0;
100 	if (crash)
101 		Sys_Error("Unable to create directory %s", path);
102 	return -1;
103 }
104 
Sys_rmdir(const char * path)105 int Sys_rmdir (const char *path)
106 {
107 	if (RemoveDirectory(path) != 0)
108 		return 0;
109 	return -1;
110 }
111 
Sys_unlink(const char * path)112 int Sys_unlink (const char *path)
113 {
114 	if (DeleteFile(path) != 0)
115 		return 0;
116 	return -1;
117 }
118 
Sys_rename(const char * oldp,const char * newp)119 int Sys_rename (const char *oldp, const char *newp)
120 {
121 	if (MoveFile(oldp, newp) != 0)
122 		return 0;
123 	return -1;
124 }
125 
Sys_filesize(const char * path)126 long Sys_filesize (const char *path)
127 {
128 	HANDLE fh;
129 	WIN32_FIND_DATA data;
130 	long size;
131 
132 	fh = FindFirstFile(path, &data);
133 	if (fh == INVALID_HANDLE_VALUE)
134 		return -1;
135 	FindClose(fh);
136 	if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
137 		return -1;
138 //	we're not dealing with gigabytes of files.
139 //	size should normally smaller than INT_MAX.
140 //	size = (data.nFileSizeHigh * (MAXDWORD + 1)) + data.nFileSizeLow;
141 	size = (long) data.nFileSizeLow;
142 	return size;
143 }
144 
145 #ifndef INVALID_FILE_ATTRIBUTES
146 #define INVALID_FILE_ATTRIBUTES	((DWORD)-1)
147 #endif
Sys_FileType(const char * path)148 int Sys_FileType (const char *path)
149 {
150 	DWORD result = GetFileAttributes(path);
151 
152 	if (result == INVALID_FILE_ATTRIBUTES)
153 		return FS_ENT_NONE;
154 	if (result & FILE_ATTRIBUTE_DIRECTORY)
155 		return FS_ENT_DIRECTORY;
156 
157 	return FS_ENT_FILE;
158 }
159 
Sys_CopyFile(const char * frompath,const char * topath)160 int Sys_CopyFile (const char *frompath, const char *topath)
161 {
162 /* 3rd param: whether to fail if 'topath' already exists */
163 	if (CopyFile(frompath, topath, FALSE) != 0)
164 		return 0;
165 	return -1;
166 }
167 
168 /*
169 =================================================
170 simplified findfirst/findnext implementation:
171 Sys_FindFirstFile and Sys_FindNextFile return
172 filenames only, not a dirent struct. this is
173 what we presently need in this engine.
174 =================================================
175 */
176 static HANDLE findhandle = INVALID_HANDLE_VALUE;
177 static WIN32_FIND_DATA finddata;
178 static char	findstr[MAX_OSPATH];
179 
Sys_FindFirstFile(const char * path,const char * pattern)180 const char *Sys_FindFirstFile (const char *path, const char *pattern)
181 {
182 	if (findhandle != INVALID_HANDLE_VALUE)
183 		Sys_Error ("Sys_FindFirst without FindClose");
184 	q_snprintf (findstr, sizeof(findstr), "%s/%s", path, pattern);
185 	findhandle = FindFirstFile(findstr, &finddata);
186 	if (findhandle == INVALID_HANDLE_VALUE)
187 		return NULL;
188 	if (finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
189 		return Sys_FindNextFile();
190 	return finddata.cFileName;
191 }
192 
Sys_FindNextFile(void)193 const char *Sys_FindNextFile (void)
194 {
195 	if (findhandle == INVALID_HANDLE_VALUE)
196 		return NULL;
197 	while (FindNextFile(findhandle, &finddata) != 0)
198 	{
199 		if (finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
200 			continue;
201 		return finddata.cFileName;
202 	}
203 	return NULL;
204 }
205 
Sys_FindClose(void)206 void Sys_FindClose (void)
207 {
208 	if (findhandle != INVALID_HANDLE_VALUE)
209 	{
210 		FindClose(findhandle);
211 		findhandle = INVALID_HANDLE_VALUE;
212 	}
213 }
214 
215 
216 /*
217 ===============================================================================
218 
219 SYSTEM IO
220 
221 ===============================================================================
222 */
223 
224 /*
225 ================
226 Sys_MakeCodeWriteable
227 ================
228 */
229 #if id386 && !defined(GLQUAKE)
Sys_MakeCodeWriteable(unsigned long startaddr,unsigned long length)230 void Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length)
231 {
232 	DWORD	flOldProtect;
233 //@@@ copy on write or just read-write?
234 	if (!VirtualProtect((LPVOID)startaddr, length, PAGE_EXECUTE_READWRITE, &flOldProtect))
235 		Sys_Error("Protection change failed\n");
236 }
237 #endif	/* id386, !GLQUAKE */
238 
239 
240 /*
241 ================
242 Sys_SetDPIAware
243 ================
244 */
245 typedef enum { dpi_unaware = 0, dpi_system_aware = 1, dpi_monitor_aware = 2 } dpi_awareness;
246 typedef BOOL (WINAPI *SetProcessDPIAwareFunc)();
247 typedef HRESULT (WINAPI *SetProcessDPIAwarenessFunc)(dpi_awareness value);
248 
Sys_SetDPIAware(void)249 static void Sys_SetDPIAware (void)
250 {
251 	HMODULE hUser32, hShcore;
252 	SetProcessDPIAwarenessFunc setDPIAwareness;
253 	SetProcessDPIAwareFunc setDPIAware;
254 
255 	/* We do not handle the OS scaling our window. Call
256 	 * SetProcessDpiAwareness() or SetProcessDPIAware()
257 	 * to opt out of scaling.
258 	 */
259 	hShcore = LoadLibraryA ("Shcore.dll");
260 	hUser32 = LoadLibraryA ("user32.dll");
261 	setDPIAwareness = (SetProcessDPIAwarenessFunc) (hShcore ? GetProcAddress (hShcore, "SetProcessDpiAwareness") : NULL);
262 	setDPIAware = (SetProcessDPIAwareFunc) (hUser32 ? GetProcAddress (hUser32, "SetProcessDPIAware") : NULL);
263 
264 	if (setDPIAwareness) /* Windows 8.1+ */
265 		setDPIAwareness (dpi_monitor_aware);
266 	else if (setDPIAware) /* Vista, Win7 or 8.0 */
267 		setDPIAware ();
268 
269 	if (hShcore)
270 		FreeLibrary (hShcore);
271 	if (hUser32)
272 		FreeLibrary (hUser32);
273 }
274 
275 /*
276 ================
277 Sys_Init
278 ================
279 */
Sys_Init(void)280 static void Sys_Init (void)
281 {
282 	OSVERSIONINFO	vinfo;
283 
284 	vinfo.dwOSVersionInfoSize = sizeof(vinfo);
285 
286 	if (!GetVersionEx (&vinfo))
287 		Sys_Error ("Couldn't get OS info");
288 
289 	if ((vinfo.dwMajorVersion < 4) ||
290 		(vinfo.dwPlatformId == VER_PLATFORM_WIN32s))
291 	{
292 		Sys_Error ("%s requires at least Win95 or NT 4.0", ENGINE_NAME);
293 	}
294 
295 	if (vinfo.dwPlatformId == VER_PLATFORM_WIN32_NT)
296 	{
297 		WinNT = true;
298 		if (vinfo.dwMajorVersion >= 6)
299 			WinVista = true;
300 	}
301 	else
302 	{
303 		WinNT = false; /* Win9x or WinME */
304 		if ((vinfo.dwMajorVersion == 4) && (vinfo.dwMinorVersion == 0))
305 		{
306 			Win95 = true;
307 			/* Win95-gold or Win95A can't switch bpp automatically */
308 			if (vinfo.szCSDVersion[1] != 'C' && vinfo.szCSDVersion[1] != 'B')
309 				Win95old = true;
310 		}
311 	}
312 
313 	if (!isDedicated)
314 		Sys_SetDPIAware ();
315 
316 	timeBeginPeriod (1);	/* 1 ms timer precision */
317 	starttime = timeGetTime ();
318 
319 /* do we really need these with opengl ?? */
320 	MaskExceptions ();
321 	Sys_SetFPCW ();
322 }
323 
324 
325 #define ERROR_PREFIX	"\nFATAL ERROR: "
Sys_Error(const char * error,...)326 void Sys_Error (const char *error, ...)
327 {
328 	va_list		argptr;
329 	char		text[MAX_PRINTMSG];
330 	const char	text2[] = ERROR_PREFIX;
331 	const char	text3[] = "\n";
332 	const char	text4[] = "\nPress Enter to exit\n";
333 	DWORD		dummy;
334 	double		err_begin;
335 
336 	host_parms->errstate++;
337 
338 	va_start (argptr, error);
339 	q_vsnprintf (text, sizeof (text), error, argptr);
340 	va_end (argptr);
341 
342 	if (con_debuglog)
343 	{
344 		LOG_Print (ERROR_PREFIX);
345 		LOG_Print (text);
346 		LOG_Print ("\n\n");
347 	}
348 
349 	Host_Shutdown ();
350 
351 	if (isDedicated)
352 	{
353 		WriteFile (houtput, text2, strlen(text2), &dummy, NULL);
354 		WriteFile (houtput, text,  strlen(text),  &dummy, NULL);
355 		WriteFile (houtput, text3, strlen(text3), &dummy, NULL);
356 		WriteFile (houtput, text4, strlen(text4), &dummy, NULL);
357 
358 		err_begin = Sys_DoubleTime ();
359 		sc_return_on_enter = true; /* so Enter will get us out of here */
360 		while (!Sys_ConsoleInput () &&
361 			((Sys_DoubleTime () - err_begin) < CONSOLE_ERROR_TIMEOUT))
362 		{
363 		}
364 	}
365 	else
366 	{
367 		MessageBox(NULL, text, ENGINE_NAME " Error", MB_OK | MB_SETFOREGROUND | MB_ICONSTOP);
368 	}
369 
370 	exit (1);
371 }
372 
Sys_PrintTerm(const char * msgtxt)373 void Sys_PrintTerm (const char *msgtxt)
374 {
375 	DWORD		dummy;
376 
377 	if (isDedicated)
378 	{
379 		if (sys_nostdout.integer)
380 			return;
381 
382 		WriteFile(houtput, msgtxt, strlen(msgtxt), &dummy, NULL);
383 	}
384 }
385 
Sys_Quit(void)386 void Sys_Quit (void)
387 {
388 	Host_Shutdown();
389 
390 	if (tevent)
391 		CloseHandle (tevent);
392 
393 	if (isDedicated)
394 		FreeConsole ();
395 
396 	exit (0);
397 }
398 
399 
400 /*
401 ================
402 Sys_DoubleTime
403 ================
404 */
Sys_DoubleTime(void)405 double Sys_DoubleTime (void)
406 {
407 	DWORD	now, passed;
408 
409 	now = timeGetTime();
410 	if (now < starttime)	/* wrapped? */
411 	{
412 		passed = TIME_WRAP_VALUE - starttime;
413 		passed += now;
414 	}
415 	else
416 	{
417 		passed = now - starttime;
418 	}
419 
420 	return (passed == 0) ? 0.0 : (passed / 1000.0);
421 }
422 
Sys_DateTimeString(char * buf)423 char *Sys_DateTimeString (char *buf)
424 {
425 	static char strbuf[24];
426 	SYSTEMTIME st;
427 	int val;
428 
429 	if (!buf) buf = strbuf;
430 
431 	GetLocalTime(&st);
432 
433 	val = st.wMonth;
434 	buf[0] = val / 10 + '0';
435 	buf[1] = val % 10 + '0';
436 	buf[2] = '/';
437 	val = st.wDay;
438 	buf[3] = val / 10 + '0';
439 	buf[4] = val % 10 + '0';
440 	buf[5] = '/';
441 	val = st.wYear / 100;
442 	buf[6] = val / 10 + '0';
443 	buf[7] = val % 10 + '0';
444 	val = st.wYear % 100;
445 	buf[8] = val / 10 + '0';
446 	buf[9] = val % 10 + '0';
447 
448 	buf[10] = ' ';
449 
450 	val = st.wHour;
451 	buf[11] = val / 10 + '0';
452 	buf[12] = val % 10 + '0';
453 	buf[13] = ':';
454 	val = st.wMinute;
455 	buf[14] = val / 10 + '0';
456 	buf[15] = val % 10 + '0';
457 	buf[16] = ':';
458 	val = st.wSecond;
459 	buf[17] = val / 10 + '0';
460 	buf[18] = val % 10 + '0';
461 
462 	buf[19] = '\0';
463 
464 	return buf;
465 }
466 
467 
Sys_ConsoleInput(void)468 const char *Sys_ConsoleInput (void)
469 {
470 	static char	con_text[256];
471 	static int	textlen;
472 	INPUT_RECORD	recs[1024];
473 	int		ch;
474 	DWORD		dummy, numread, numevents;
475 
476 	for ( ;; )
477 	{
478 		if (GetNumberOfConsoleInputEvents(hinput, &numevents) == 0)
479 			Sys_Error ("Error getting # of console events");
480 
481 		if (! numevents)
482 			break;
483 
484 		if (ReadConsoleInput(hinput, recs, 1, &numread) == 0)
485 			Sys_Error ("Error reading console input");
486 
487 		if (numread != 1)
488 			Sys_Error ("Couldn't read console input");
489 
490 		if (recs[0].EventType == KEY_EVENT)
491 		{
492 		    if (recs[0].Event.KeyEvent.bKeyDown == FALSE)
493 		    {
494 			ch = recs[0].Event.KeyEvent.uChar.AsciiChar;
495 
496 			switch (ch)
497 			{
498 			case '\r':
499 				WriteFile(houtput, "\r\n", 2, &dummy, NULL);
500 				if (textlen != 0)
501 				{
502 					con_text[textlen] = 0;
503 					textlen = 0;
504 					return con_text;
505 				}
506 				else if (sc_return_on_enter)
507 				{
508 				/* special case to allow exiting
509 				from the error handler on Enter */
510 					con_text[0] = '\r';
511 					textlen = 0;
512 					return con_text;
513 				}
514 
515 				break;
516 
517 			case '\b':
518 				WriteFile(houtput, "\b \b", 3, &dummy, NULL);
519 				if (textlen != 0)
520 					textlen--;
521 
522 				break;
523 
524 			default:
525 				if (ch >= ' ')
526 				{
527 					WriteFile(houtput, &ch, 1, &dummy, NULL);
528 					con_text[textlen] = ch;
529 					textlen = (textlen + 1) & 0xff;
530 				}
531 
532 				break;
533 			}
534 		    }
535 		}
536 	}
537 
538 	return NULL;
539 }
540 
Sys_Sleep(unsigned long msecs)541 void Sys_Sleep (unsigned long msecs)
542 {
543 	Sleep (msecs);
544 }
545 
546 
Sys_SendKeyEvents(void)547 void Sys_SendKeyEvents (void)
548 {
549 	MSG	msg;
550 
551 	while (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE))
552 	{
553 	// we always update if there are any event, even if we're paused
554 		scr_skipupdate = 0;
555 
556 		if (!GetMessage (&msg, NULL, 0, 0))
557 			Sys_Quit ();
558 		TranslateMessage (&msg);
559 		DispatchMessage (&msg);
560 	}
561 }
562 
563 #define MAX_CLIPBOARDTXT	MAXCMDLINE	/* 256 */
Sys_GetClipboardData(void)564 char *Sys_GetClipboardData (void)
565 {
566 	char *data = NULL;
567 	char *cliptext;
568 
569 	if (OpenClipboard(NULL) != 0)
570 	{
571 		HANDLE hClipboardData;
572 
573 		if ((hClipboardData = GetClipboardData(CF_TEXT)) != NULL)
574 		{
575 			cliptext = (char *) GlobalLock(hClipboardData);
576 			if (cliptext != NULL)
577 			{
578 				size_t size = GlobalSize(hClipboardData) + 1;
579 			/* this is intended for simple small text copies
580 			 * such as an ip address, etc:  do chop the size
581 			 * here, otherwise we may experience Z_Malloc()
582 			 * failures and all other not-oh-so-fun stuff. */
583 				size = q_min(MAX_CLIPBOARDTXT, size);
584 				data = (char *) Z_Malloc(size, Z_MAINZONE);
585 				q_strlcpy (data, cliptext, size);
586 				GlobalUnlock (hClipboardData);
587 			}
588 		}
589 		CloseClipboard ();
590 	}
591 	return data;
592 }
593 
594 
Sys_GetBasedir(char * argv0,char * dst,size_t dstsize)595 static int Sys_GetBasedir (char *argv0, char *dst, size_t dstsize)
596 {
597 	char *tmp;
598 	size_t rc;
599 
600 	rc = GetCurrentDirectory(dstsize, dst);
601 	if (rc == 0 || rc > dstsize)
602 		return -1;
603 
604 	tmp = dst;
605 	while (*tmp != 0)
606 		tmp++;
607 	while (*tmp == 0 && tmp != dst)
608 	{
609 		--tmp;
610 		if (tmp != dst && (*tmp == '/' || *tmp == '\\'))
611 			*tmp = 0;
612 	}
613 
614 	return 0;
615 }
616 
617 /*
618 ==============================================================================
619 
620  WINDOWS CRAP
621 
622 ==============================================================================
623 */
624 
625 
626 /*
627 ==================
628 SleepUntilInput
629 ==================
630 */
SleepUntilInput(unsigned long msecs)631 static void SleepUntilInput (unsigned long msecs)
632 {
633 	MsgWaitForMultipleObjects(1, &tevent, FALSE, msecs, QS_ALLINPUT);
634 }
635 
636 
PrintVersion(void)637 static void PrintVersion (void)
638 {
639 	Sys_Printf ("Hammer of Thyrion, release %s (%s)\n", HOT_VERSION_STR, HOT_VERSION_REL_DATE);
640 	Sys_Printf ("running on %s engine %4.2f (%s)\n", ENGINE_NAME, ENGINE_VERSION, PLATFORM_STRING);
641 	Sys_Printf ("More info / sending bug reports:  http://uhexen2.sourceforge.net\n");
642 }
643 
644 /*
645 ==================
646 WinMain
647 ==================
648 */
649 HINSTANCE	global_hInstance;
650 int			global_nCmdShow;
651 #if !defined(NO_SPLASHES)
652 HWND		hwnd_dialog;
653 #endif	/* NO_SPLASHES */
654 static char	*argv[MAX_NUM_ARGVS];
655 static char	cwd[1024];
656 static char	prog[MAX_PATH];
657 static quakeparms_t	parms;
658 
Sys_CreateInitSplash(HINSTANCE hInstance)659 static void Sys_CreateInitSplash (HINSTANCE hInstance)
660 {
661 #if !defined(NO_SPLASHES)
662 	RECT		rect;
663 
664 	hwnd_dialog = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, NULL);
665 	if (!hwnd_dialog)
666 		return;
667 
668 	if (GetWindowRect (hwnd_dialog, &rect))
669 	{
670 		if (rect.left > (rect.top * 2))
671 		{
672 			SetWindowPos (hwnd_dialog, 0,
673 					(rect.left / 2) - ((rect.right - rect.left) / 2),
674 					rect.top, 0, 0,
675 					SWP_NOZORDER | SWP_NOSIZE);
676 		}
677 	}
678 
679 	ShowWindow (hwnd_dialog, SW_SHOWDEFAULT);
680 	UpdateWindow (hwnd_dialog);
681 	SetForegroundWindow (hwnd_dialog);
682 #endif	/* NO_SPLASHES */
683 }
684 
WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)685 int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
686 {
687 	int		i;
688 	double		time, oldtime, newtime;
689 	MEMORYSTATUS	lpBuffer;
690 
691 	/* previous instances do not exist in Win32 */
692 	if (hPrevInstance)
693 		return 0;
694 
695 	global_hInstance = hInstance;
696 	global_nCmdShow = nCmdShow;
697 
698 	lpBuffer.dwLength = sizeof(MEMORYSTATUS);
699 	GlobalMemoryStatus (&lpBuffer);
700 	/* Maximum of 2GiB to work around signed int */
701 	if (lpBuffer.dwAvailPhys > 0x7FFFFFFF)
702 		lpBuffer.dwAvailPhys = 0x7FFFFFFF;
703 	if (lpBuffer.dwTotalPhys > 0x7FFFFFFF)
704 		lpBuffer.dwTotalPhys = 0x7FFFFFFF;
705 
706 	memset (&parms, 0, sizeof(parms));
707 	parms.basedir = cwd;
708 	parms.userdir = cwd;	/* no userdir on win32 */
709 	parms.errstate = 0;
710 	host_parms = &parms;	/* initialize the host params */
711 
712 	memset (cwd, 0, sizeof(cwd));
713 	if (Sys_GetBasedir(NULL, cwd, sizeof(cwd)) != 0)
714 		Sys_Error ("Couldn't determine current directory");
715 
716 	parms.argc = 1;
717 	argv[0] = prog;
718 	if (GetModuleFileName(NULL, prog, sizeof(prog)) == 0)
719 		prog[0] = '\0';
720 	else	prog[MAX_PATH - 1] = '\0';
721 
722 	while (*lpCmdLine && (parms.argc < MAX_NUM_ARGVS))
723 	{
724 		while (*lpCmdLine && ((*lpCmdLine <= 32) || (*lpCmdLine > 126)))
725 			lpCmdLine++;
726 
727 		if (*lpCmdLine)
728 		{
729 			argv[parms.argc] = lpCmdLine;
730 			parms.argc++;
731 
732 			while (*lpCmdLine && ((*lpCmdLine > 32) && (*lpCmdLine <= 126)))
733 				lpCmdLine++;
734 
735 			if (*lpCmdLine)
736 			{
737 				*lpCmdLine = 0;
738 				lpCmdLine++;
739 			}
740 		}
741 	}
742 
743 	parms.argv = argv;
744 
745 	isDedicated = (COM_CheckParm ("-dedicated") != 0);
746 	if (isDedicated)
747 	{
748 		if (!AllocConsole ())
749 		{
750 			isDedicated = false;	/* so that we have a graphical error dialog */
751 			Sys_Error ("Couldn't create dedicated server console");
752 		}
753 		hinput = GetStdHandle (STD_INPUT_HANDLE);
754 		houtput = GetStdHandle (STD_OUTPUT_HANDLE);
755 		if (hinput  == INVALID_HANDLE_VALUE ||
756 		    houtput == INVALID_HANDLE_VALUE ||
757 		    hinput  == NULL || houtput == NULL)
758 		{
759 			isDedicated = false;	/* so that we have a graphical error dialog */
760 			Sys_Error ("Couldn't retrieve server console handles");
761 		}
762 
763 		PrintVersion();
764 	}
765 
766 	LOG_Init (&parms);
767 
768 	Sys_Printf("basedir is: %s\n", parms.basedir);
769 	Sys_Printf("userdir is: %s\n", parms.userdir);
770 
771 	COM_ValidateByteorder ();
772 
773 	if (!isDedicated)
774 		Sys_CreateInitSplash (global_hInstance);
775 
776 // take the greater of all the available memory or half the total memory,
777 // but at least 16 Mb and no more than 32 Mb, unless they explicitly
778 // request otherwise
779 	parms.memsize = lpBuffer.dwAvailPhys;
780 
781 	if (parms.memsize < MIN_MEM_ALLOC)
782 		parms.memsize = MIN_MEM_ALLOC;
783 
784 	if (parms.memsize < (int) (lpBuffer.dwTotalPhys >> 1))
785 		parms.memsize = (int) (lpBuffer.dwTotalPhys >> 1);
786 
787 	if (parms.memsize > STD_MEM_ALLOC)
788 		parms.memsize = STD_MEM_ALLOC;
789 
790 	if (isDedicated)
791 		parms.memsize = MIN_MEM_ALLOC;
792 
793 	i = COM_CheckParm ("-heapsize");
794 	if (i && i < com_argc-1)
795 	{
796 		parms.memsize = atoi (com_argv[i+1]) * 1024;
797 
798 		if ((parms.memsize > MAX_MEM_ALLOC) && !(COM_CheckParm ("-forcemem")))
799 			parms.memsize = MAX_MEM_ALLOC;
800 		else if ((parms.memsize < MIN_MEM_ALLOC) && !(COM_CheckParm ("-forcemem")))
801 			parms.memsize = MIN_MEM_ALLOC;
802 	}
803 
804 	parms.membase = malloc (parms.memsize);
805 
806 	if (!parms.membase)
807 		Sys_Error ("Insufficient memory.\n");
808 
809 	if (COM_CheckParm("-nopagein") == 0)
810 	{
811 		Sys_PageIn (parms.membase, parms.memsize);
812 	}
813 
814 	tevent = CreateEvent(NULL, FALSE, FALSE, NULL);
815 	if (!tevent)
816 		Sys_Error ("Couldn't create event");
817 
818 	Sys_Init ();
819 
820 	Host_Init();
821 
822 	oldtime = Sys_DoubleTime ();
823 
824 	/* main window message loop */
825 	while (1)
826 	{
827 	    if (isDedicated)
828 	    {
829 		newtime = Sys_DoubleTime ();
830 		time = newtime - oldtime;
831 
832 		while (time < sys_ticrate.value )
833 		{
834 			Sleep (1);
835 			newtime = Sys_DoubleTime ();
836 			time = newtime - oldtime;
837 		}
838 
839 		Host_Frame (time);
840 		oldtime = newtime;
841 	    }
842 	    else
843 	    {
844 		/* yield the CPU for a little while when paused, minimized or not focused */
845 		if ((cl.paused && (!ActiveApp && !DDActive)) || Minimized || block_drawing)
846 		{
847 			SleepUntilInput (PAUSE_SLEEP);
848 			scr_skipupdate = 1;		/* no point in bothering to draw */
849 		}
850 		else if (!ActiveApp && !DDActive)
851 		{
852 			SleepUntilInput (NOT_FOCUS_SLEEP);
853 			scr_skipupdate = 1;		/* no point in bothering to draw */
854 		}
855 
856 		newtime = Sys_DoubleTime ();
857 		time = newtime - oldtime;
858 
859 		Host_Frame (time);
860 
861 		if (time < sys_throttle.value)
862 			Sleep (1);
863 
864 		oldtime = newtime;
865 	    }
866 	}
867 
868 	return 0;
869 }
870 
871