1/*
2Copyright (C) 1996-1997 Id Software, Inc.
3
4This program is free software; you can redistribute it and/or
5modify it under the terms of the GNU General Public License
6as published by the Free Software Foundation; either version 2
7of the License, or (at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13See the GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19*/
20// sys_win.h
21
22#include "quakedef.h"
23#include "winquake.h"
24#include "resource.h"
25#include "errno.h"
26#include "fcntl.h"
27#include <limits.h>
28
29#define MINIMUM_WIN_MEMORY	0x0c00000
30#define MAXIMUM_WIN_MEMORY	0x1000000
31
32#define PAUSE_SLEEP		50				// sleep time on pause or minimization
33#define NOT_FOCUS_SLEEP	20				// sleep time when not focus
34
35int		starttime;
36qboolean ActiveApp, Minimized;
37qboolean	WinNT;
38
39HWND	hwnd_dialog;		// startup dialog box
40
41static double		pfreq;
42static double		curtime = 0.0;
43static double		lastcurtime = 0.0;
44static int			lowshift;
45static HANDLE		hinput, houtput;
46
47HANDLE		qwclsemaphore;
48
49static HANDLE	tevent;
50
51void Sys_InitFloatTime (void);
52
53void MaskExceptions (void);
54void Sys_PopFPCW (void);
55void Sys_PushFPCW_SetHigh (void);
56
57void Sys_DebugLog(char *file, char *fmt, ...)
58{
59    va_list argptr;
60    static char data[1024];
61    int fd;
62
63    va_start(argptr, fmt);
64    vsprintf(data, fmt, argptr);
65    va_end(argptr);
66    fd = open(file, O_WRONLY | O_CREAT | O_APPEND, 0666);
67    write(fd, data, strlen(data));
68    close(fd);
69};
70
71/*
72===============================================================================
73
74FILE IO
75
76===============================================================================
77*/
78
79/*
80================
81filelength
82================
83*/
84int filelength (FILE *f)
85{
86	int		pos;
87	int		end;
88
89	pos = ftell (f);
90	fseek (f, 0, SEEK_END);
91	end = ftell (f);
92	fseek (f, pos, SEEK_SET);
93
94	return end;
95}
96
97
98int	Sys_FileTime (char *path)
99{
100	FILE	*f;
101	int		t, retval;
102
103	t = VID_ForceUnlockedAndReturnState ();
104
105	f = fopen(path, "rb");
106
107	if (f)
108	{
109		fclose(f);
110		retval = 1;
111	}
112	else
113	{
114		retval = -1;
115	}
116
117	VID_ForceLockState (t);
118	return retval;
119}
120
121void Sys_mkdir (char *path)
122{
123	_mkdir (path);
124}
125
126
127/*
128===============================================================================
129
130SYSTEM IO
131
132===============================================================================
133*/
134
135/*
136================
137Sys_MakeCodeWriteable
138================
139*/
140void Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length)
141{
142	DWORD  flOldProtect;
143
144//@@@ copy on write or just read-write?
145	if (!VirtualProtect((LPVOID)startaddr, length, PAGE_READWRITE, &flOldProtect))
146   		Sys_Error("Protection change failed\n");
147}
148
149
150/*
151================
152Sys_Init
153================
154*/
155void Sys_Init (void)
156{
157	LARGE_INTEGER	PerformanceFreq;
158	unsigned int	lowpart, highpart;
159	OSVERSIONINFO	vinfo;
160
161#ifndef SERVERONLY
162	// allocate a named semaphore on the client so the
163	// front end can tell if it is alive
164
165	// mutex will fail if semephore allready exists
166    qwclsemaphore = CreateMutex(
167        NULL,         /* Security attributes */
168        0,            /* owner       */
169        "qwcl"); /* Semaphore name      */
170	if (!qwclsemaphore)
171		Sys_Error ("QWCL is already running on this system");
172	CloseHandle (qwclsemaphore);
173
174    qwclsemaphore = CreateSemaphore(
175        NULL,         /* Security attributes */
176        0,            /* Initial count       */
177        1,            /* Maximum count       */
178        "qwcl"); /* Semaphore name      */
179#endif
180
181	MaskExceptions ();
182	Sys_SetFPCW ();
183
184#if 0
185	if (!QueryPerformanceFrequency (&PerformanceFreq))
186		Sys_Error ("No hardware timer available");
187
188// get 32 out of the 64 time bits such that we have around
189// 1 microsecond resolution
190	lowpart = (unsigned int)PerformanceFreq.LowPart;
191	highpart = (unsigned int)PerformanceFreq.HighPart;
192	lowshift = 0;
193
194	while (highpart || (lowpart > 2000000.0))
195	{
196		lowshift++;
197		lowpart >>= 1;
198		lowpart |= (highpart & 1) << 31;
199		highpart >>= 1;
200	}
201
202	pfreq = 1.0 / (double)lowpart;
203
204	Sys_InitFloatTime ();
205#endif
206
207	// make sure the timer is high precision, otherwise
208	// NT gets 18ms resolution
209	timeBeginPeriod( 1 );
210
211	vinfo.dwOSVersionInfoSize = sizeof(vinfo);
212
213	if (!GetVersionEx (&vinfo))
214		Sys_Error ("Couldn't get OS info");
215
216	if ((vinfo.dwMajorVersion < 4) ||
217		(vinfo.dwPlatformId == VER_PLATFORM_WIN32s))
218	{
219		Sys_Error ("QuakeWorld requires at least Win95 or NT 4.0");
220	}
221
222	if (vinfo.dwPlatformId == VER_PLATFORM_WIN32_NT)
223		WinNT = true;
224	else
225		WinNT = false;
226}
227
228
229void Sys_Error (char *error, ...)
230{
231	va_list		argptr;
232	char		text[1024], text2[1024];
233	DWORD		dummy;
234
235	Host_Shutdown ();
236
237	va_start (argptr, error);
238	vsprintf (text, error, argptr);
239	va_end (argptr);
240
241	MessageBox(NULL, text, "Error", 0 /* MB_OK */ );
242
243#ifndef SERVERONLY
244	CloseHandle (qwclsemaphore);
245#endif
246
247	exit (1);
248}
249
250void Sys_Printf (char *fmt, ...)
251{
252	va_list		argptr;
253	char		text[1024];
254	DWORD		dummy;
255
256	va_start (argptr,fmt);
257	vprintf (fmt, argptr);
258	va_end (argptr);
259}
260
261void Sys_Quit (void)
262{
263	VID_ForceUnlockedAndReturnState ();
264
265	Host_Shutdown();
266#ifndef SERVERONLY
267	if (tevent)
268		CloseHandle (tevent);
269
270	if (qwclsemaphore)
271		CloseHandle (qwclsemaphore);
272#endif
273
274	exit (0);
275}
276
277
278#if 0
279/*
280================
281Sys_DoubleTime
282================
283*/
284double Sys_DoubleTime (void)
285{
286	static int			sametimecount;
287	static unsigned int	oldtime;
288	static int			first = 1;
289	LARGE_INTEGER		PerformanceCount;
290	unsigned int		temp, t2;
291	double				time;
292
293	Sys_PushFPCW_SetHigh ();
294
295	QueryPerformanceCounter (&PerformanceCount);
296
297	temp = ((unsigned int)PerformanceCount.LowPart >> lowshift) |
298		   ((unsigned int)PerformanceCount.HighPart << (32 - lowshift));
299
300	if (first)
301	{
302		oldtime = temp;
303		first = 0;
304	}
305	else
306	{
307	// check for turnover or backward time
308		if ((temp <= oldtime) && ((oldtime - temp) < 0x10000000))
309		{
310			oldtime = temp;	// so we can't get stuck
311		}
312		else
313		{
314			t2 = temp - oldtime;
315
316			time = (double)t2 * pfreq;
317			oldtime = temp;
318
319			curtime += time;
320
321			if (curtime == lastcurtime)
322			{
323				sametimecount++;
324
325				if (sametimecount > 100000)
326				{
327					curtime += 1.0;
328					sametimecount = 0;
329				}
330			}
331			else
332			{
333				sametimecount = 0;
334			}
335
336			lastcurtime = curtime;
337		}
338	}
339
340	Sys_PopFPCW ();
341
342    return curtime;
343}
344
345/*
346================
347Sys_InitFloatTime
348================
349*/
350void Sys_InitFloatTime (void)
351{
352	int		j;
353
354	Sys_DoubleTime ();
355
356	j = COM_CheckParm("-starttime");
357
358	if (j)
359	{
360		curtime = (double) (Q_atof(com_argv[j+1]));
361	}
362	else
363	{
364		curtime = 0.0;
365	}
366
367	lastcurtime = curtime;
368}
369
370#endif
371
372double Sys_DoubleTime (void)
373{
374	static DWORD starttime;
375	static qboolean first = true;
376	DWORD now;
377	double t;
378
379	now = timeGetTime();
380
381	if (first) {
382		first = false;
383		starttime = now;
384		return 0.0;
385	}
386
387	if (now < starttime) // wrapped?
388		return (now / 1000.0) + (LONG_MAX - starttime / 1000.0);
389
390	if (now - starttime == 0)
391		return 0.0;
392
393	return (now - starttime) / 1000.0;
394}
395
396char *Sys_ConsoleInput (void)
397{
398	static char	text[256];
399	static int		len;
400	INPUT_RECORD	recs[1024];
401	int		count;
402	int		i, dummy;
403	int		ch, numread, numevents;
404	HANDLE	th;
405	char	*clipText, *textCopied;
406
407	for ( ;; )
408	{
409		if (!GetNumberOfConsoleInputEvents (hinput, &numevents))
410			Sys_Error ("Error getting # of console events");
411
412		if (numevents <= 0)
413			break;
414
415		if (!ReadConsoleInput(hinput, recs, 1, &numread))
416			Sys_Error ("Error reading console input");
417
418		if (numread != 1)
419			Sys_Error ("Couldn't read console input");
420
421		if (recs[0].EventType == KEY_EVENT)
422		{
423			if (!recs[0].Event.KeyEvent.bKeyDown)
424			{
425				ch = recs[0].Event.KeyEvent.uChar.AsciiChar;
426
427				switch (ch)
428				{
429					case '\r':
430						WriteFile(houtput, "\r\n", 2, &dummy, NULL);
431
432						if (len)
433						{
434							text[len] = 0;
435							len = 0;
436							return text;
437						}
438						break;
439
440					case '\b':
441						WriteFile(houtput, "\b \b", 3, &dummy, NULL);
442						if (len)
443						{
444							len--;
445							putch('\b');
446						}
447						break;
448
449					default:
450						Con_Printf("Stupid: %d\n", recs[0].Event.KeyEvent.dwControlKeyState);
451						if (((ch=='V' || ch=='v') && (recs[0].Event.KeyEvent.dwControlKeyState &
452							(LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))) || ((recs[0].Event.KeyEvent.dwControlKeyState
453							& SHIFT_PRESSED) && (recs[0].Event.KeyEvent.wVirtualKeyCode
454							==VK_INSERT))) {
455							if (OpenClipboard(NULL)) {
456								th = GetClipboardData(CF_TEXT);
457								if (th) {
458									clipText = GlobalLock(th);
459									if (clipText) {
460										textCopied = malloc(GlobalSize(th)+1);
461										strcpy(textCopied, clipText);
462/* Substitutes a NULL for every token */strtok(textCopied, "\n\r\b");
463										i = strlen(textCopied);
464										if (i+len>=256)
465											i=256-len;
466										if (i>0) {
467											textCopied[i]=0;
468											text[len]=0;
469											strcat(text, textCopied);
470											len+=dummy;
471											WriteFile(houtput, textCopied, i, &dummy, NULL);
472										}
473										free(textCopied);
474									}
475									GlobalUnlock(th);
476								}
477								CloseClipboard();
478							}
479						} else if (ch >= ' ')
480						{
481							WriteFile(houtput, &ch, 1, &dummy, NULL);
482							text[len] = ch;
483							len = (len + 1) & 0xff;
484						}
485
486						break;
487
488				}
489			}
490		}
491	}
492
493	return NULL;
494}
495
496void Sys_Sleep (void)
497{
498}
499
500
501void Sys_SendKeyEvents (void)
502{
503    MSG        msg;
504
505	while (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE))
506	{
507	// we always update if there are any event, even if we're paused
508		scr_skipupdate = 0;
509
510		if (!GetMessage (&msg, NULL, 0, 0))
511			Sys_Quit ();
512      	TranslateMessage (&msg);
513      	DispatchMessage (&msg);
514	}
515}
516
517
518
519/*
520==============================================================================
521
522 WINDOWS CRAP
523
524==============================================================================
525*/
526
527/*
528==================
529WinMain
530==================
531*/
532void SleepUntilInput (int time)
533{
534
535	MsgWaitForMultipleObjects(1, &tevent, FALSE, time, QS_ALLINPUT);
536}
537
538
539
540/*
541==================
542WinMain
543==================
544*/
545HINSTANCE	global_hInstance;
546int			global_nCmdShow;
547char		*argv[MAX_NUM_ARGVS];
548static char	*empty_string = "";
549HWND		hwnd_dialog;
550
551
552int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
553{
554    MSG				msg;
555	quakeparms_t	parms;
556	double			time, oldtime, newtime;
557	MEMORYSTATUS	lpBuffer;
558	static	char	cwd[1024];
559	int				t;
560	RECT			rect;
561
562    /* previous instances do not exist in Win32 */
563    if (hPrevInstance)
564        return 0;
565
566	global_hInstance = hInstance;
567	global_nCmdShow = nCmdShow;
568
569	lpBuffer.dwLength = sizeof(MEMORYSTATUS);
570	GlobalMemoryStatus (&lpBuffer);
571
572	if (!GetCurrentDirectory (sizeof(cwd), cwd))
573		Sys_Error ("Couldn't determine current directory");
574
575	if (cwd[Q_strlen(cwd)-1] == '/')
576		cwd[Q_strlen(cwd)-1] = 0;
577
578	parms.basedir = cwd;
579	parms.cachedir = NULL;
580
581	parms.argc = 1;
582	argv[0] = empty_string;
583
584	while (*lpCmdLine && (parms.argc < MAX_NUM_ARGVS))
585	{
586		while (*lpCmdLine && ((*lpCmdLine <= 32) || (*lpCmdLine > 126)))
587			lpCmdLine++;
588
589		if (*lpCmdLine)
590		{
591			argv[parms.argc] = lpCmdLine;
592			parms.argc++;
593
594			while (*lpCmdLine && ((*lpCmdLine > 32) && (*lpCmdLine <= 126)))
595				lpCmdLine++;
596
597			if (*lpCmdLine)
598			{
599				*lpCmdLine = 0;
600				lpCmdLine++;
601			}
602
603		}
604	}
605
606	parms.argv = argv;
607
608	COM_InitArgv (parms.argc, parms.argv);
609
610	parms.argc = com_argc;
611	parms.argv = com_argv;
612
613	hwnd_dialog = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, NULL);
614
615	if (hwnd_dialog)
616	{
617		if (GetWindowRect (hwnd_dialog, &rect))
618		{
619			if (rect.left > (rect.top * 2))
620			{
621				SetWindowPos (hwnd_dialog, 0,
622					(rect.left / 2) - ((rect.right - rect.left) / 2),
623					rect.top, 0, 0,
624					SWP_NOZORDER | SWP_NOSIZE);
625			}
626		}
627
628		ShowWindow (hwnd_dialog, SW_SHOWDEFAULT);
629		UpdateWindow (hwnd_dialog);
630		SetForegroundWindow (hwnd_dialog);
631	}
632
633// take the greater of all the available memory or half the total memory,
634// but at least 8 Mb and no more than 16 Mb, unless they explicitly
635// request otherwise
636	parms.memsize = lpBuffer.dwAvailPhys;
637
638	if (parms.memsize < MINIMUM_WIN_MEMORY)
639		parms.memsize = MINIMUM_WIN_MEMORY;
640
641	if (parms.memsize < (lpBuffer.dwTotalPhys >> 1))
642		parms.memsize = lpBuffer.dwTotalPhys >> 1;
643
644	if (parms.memsize > MAXIMUM_WIN_MEMORY)
645		parms.memsize = MAXIMUM_WIN_MEMORY;
646
647	if (COM_CheckParm ("-heapsize"))
648	{
649		t = COM_CheckParm("-heapsize") + 1;
650
651		if (t < com_argc)
652			parms.memsize = Q_atoi (com_argv[t]) * 1024;
653	}
654
655	parms.membase = malloc (parms.memsize);
656
657	if (!parms.membase)
658		Sys_Error ("Not enough memory free; check disk space\n");
659
660	tevent = CreateEvent(NULL, FALSE, FALSE, NULL);
661
662	if (!tevent)
663		Sys_Error ("Couldn't create event");
664
665	Sys_Init ();
666
667// because sound is off until we become active
668	S_BlockSound ();
669
670	Sys_Printf ("Host_Init\n");
671	Host_Init (&parms);
672
673	oldtime = Sys_DoubleTime ();
674
675    /* main window message loop */
676	while (1)
677	{
678	// yield the CPU for a little while when paused, minimized, or not the focus
679		if ((cl.paused && (!ActiveApp && !DDActive)) || Minimized || block_drawing)
680		{
681			SleepUntilInput (PAUSE_SLEEP);
682			scr_skipupdate = 1;		// no point in bothering to draw
683		}
684		else if (!ActiveApp && !DDActive)
685		{
686			SleepUntilInput (NOT_FOCUS_SLEEP);
687		}
688
689		newtime = Sys_DoubleTime ();
690		time = newtime - oldtime;
691		Host_Frame (time);
692		oldtime = newtime;
693	}
694
695    /* return success of application */
696    return TRUE;
697}
698
699