1 /*
2 Copyright (C) 1996-2001 Id Software, Inc.
3 Copyright (C) 2002-2005 John Fitzgibbons and others
4 Copyright (C) 2007-2008 Kristian Duske
5 Copyright (C) 2010-2014 QuakeSpasm developers
6 
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 as published by the Free Software Foundation; either version 2
10 of the License, or (at your option) any later version.
11 
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15 
16 See the GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
21 
22 */
23 
24 #ifndef WIN32_LEAN_AND_MEAN
25 #define WIN32_LEAN_AND_MEAN
26 #endif
27 #include <windows.h>
28 #include <mmsystem.h>
29 
30 #include "quakedef.h"
31 
32 #include <sys/types.h>
33 #include <errno.h>
34 #include <io.h>
35 #include <direct.h>
36 
37 #if defined(SDL_FRAMEWORK) || defined(NO_SDL_CONFIG)
38 #include <SDL2/SDL.h>
39 #else
40 #include "SDL.h"
41 #endif
42 
43 
44 qboolean		isDedicated;
45 
46 static HANDLE		hinput, houtput;
47 
48 #define	MAX_HANDLES		32	/* johnfitz -- was 10 */
49 static FILE		*sys_handles[MAX_HANDLES];
50 
51 static double counter_freq;
52 
findhandle(void)53 static int findhandle (void)
54 {
55 	int i;
56 
57 	for (i = 1; i < MAX_HANDLES; i++)
58 	{
59 		if (!sys_handles[i])
60 			return i;
61 	}
62 	Sys_Error ("out of handles");
63 	return -1;
64 }
65 
Sys_filelength(FILE * f)66 long Sys_filelength (FILE *f)
67 {
68 	long		pos, end;
69 
70 	pos = ftell (f);
71 	fseek (f, 0, SEEK_END);
72 	end = ftell (f);
73 	fseek (f, pos, SEEK_SET);
74 
75 	return end;
76 }
77 
Sys_FileOpenRead(const char * path,int * hndl)78 int Sys_FileOpenRead (const char *path, int *hndl)
79 {
80 	FILE	*f;
81 	int	i, retval;
82 
83 	i = findhandle ();
84 	f = fopen(path, "rb");
85 
86 	if (!f)
87 	{
88 		*hndl = -1;
89 		retval = -1;
90 	}
91 	else
92 	{
93 		sys_handles[i] = f;
94 		*hndl = i;
95 		retval = Sys_filelength(f);
96 	}
97 
98 	return retval;
99 }
100 
Sys_FileOpenWrite(const char * path)101 int Sys_FileOpenWrite (const char *path)
102 {
103 	FILE	*f;
104 	int		i;
105 
106 	i = findhandle ();
107 	f = fopen(path, "wb");
108 
109 	if (!f)
110 		Sys_Error ("Error opening %s: %s", path, strerror(errno));
111 
112 	sys_handles[i] = f;
113 	return i;
114 }
115 
Sys_FileClose(int handle)116 void Sys_FileClose (int handle)
117 {
118 	fclose (sys_handles[handle]);
119 	sys_handles[handle] = NULL;
120 }
121 
Sys_FileSeek(int handle,int position)122 void Sys_FileSeek (int handle, int position)
123 {
124 	fseek (sys_handles[handle], position, SEEK_SET);
125 }
126 
Sys_FileRead(int handle,void * dest,int count)127 int Sys_FileRead (int handle, void *dest, int count)
128 {
129 	return fread (dest, 1, count, sys_handles[handle]);
130 }
131 
Sys_FileWrite(int handle,const void * data,int count)132 int Sys_FileWrite (int handle, const void *data, int count)
133 {
134 	return fwrite (data, 1, count, sys_handles[handle]);
135 }
136 
Sys_FileTime(const char * path)137 int Sys_FileTime (const char *path)
138 {
139 	FILE	*f;
140 
141 	f = fopen(path, "rb");
142 
143 	if (f)
144 	{
145 		fclose(f);
146 		return 1;
147 	}
148 
149 	return -1;
150 }
151 
152 static char	cwd[1024];
153 
Sys_GetBasedir(char * argv0,char * dst,size_t dstsize)154 static void Sys_GetBasedir (char *argv0, char *dst, size_t dstsize)
155 {
156 	char *tmp;
157 	size_t rc;
158 
159 	rc = GetCurrentDirectory(dstsize, dst);
160 	if (rc == 0 || rc > dstsize)
161 		Sys_Error ("Couldn't determine current directory");
162 
163 	tmp = dst;
164 	while (*tmp != 0)
165 		tmp++;
166 	while (*tmp == 0 && tmp != dst)
167 	{
168 		--tmp;
169 		if (tmp != dst && (*tmp == '/' || *tmp == '\\'))
170 			*tmp = 0;
171 	}
172 }
173 
174 typedef enum { dpi_unaware = 0, dpi_system_aware = 1, dpi_monitor_aware = 2 } dpi_awareness;
175 typedef BOOL (WINAPI *SetProcessDPIAwareFunc)();
176 typedef HRESULT (WINAPI *SetProcessDPIAwarenessFunc)(dpi_awareness value);
177 
Sys_SetDPIAware(void)178 static void Sys_SetDPIAware (void)
179 {
180 	HMODULE hUser32, hShcore;
181 	SetProcessDPIAwarenessFunc setDPIAwareness;
182 	SetProcessDPIAwareFunc setDPIAware;
183 
184 	/* Neither SDL 1.2 nor SDL 2.0.3 can handle the OS scaling our window.
185 	  (e.g. https://bugzilla.libsdl.org/show_bug.cgi?id=2713)
186 	  Call SetProcessDpiAwareness/SetProcessDPIAware to opt out of scaling.
187 	*/
188 
189 	hShcore = LoadLibraryA ("Shcore.dll");
190 	hUser32 = LoadLibraryA ("user32.dll");
191 	setDPIAwareness = (SetProcessDPIAwarenessFunc) (hShcore ? GetProcAddress (hShcore, "SetProcessDpiAwareness") : NULL);
192 	setDPIAware = (SetProcessDPIAwareFunc) (hUser32 ? GetProcAddress (hUser32, "SetProcessDPIAware") : NULL);
193 
194 	if (setDPIAwareness) /* Windows 8.1+ */
195 		setDPIAwareness (dpi_monitor_aware);
196 	else if (setDPIAware) /* Windows Vista-8.0 */
197 		setDPIAware ();
198 
199 	if (hShcore)
200 		FreeLibrary (hShcore);
201 	if (hUser32)
202 		FreeLibrary (hUser32);
203 }
204 
Sys_SetTimerResolution(void)205 static void Sys_SetTimerResolution(void)
206 {
207 	/* Set OS timer resolution to 1ms.
208 	   Works around buffer underruns with directsound and SDL2, but also
209 	   will make Sleep()/SDL_Dleay() accurate to 1ms which should help framerate
210 	   stability.
211 	*/
212 	timeBeginPeriod (1);
213 }
214 
Sys_Init(void)215 void Sys_Init (void)
216 {
217 	Sys_SetTimerResolution ();
218 	Sys_SetDPIAware ();
219 
220 	memset (cwd, 0, sizeof(cwd));
221 	Sys_GetBasedir(NULL, cwd, sizeof(cwd));
222 	host_parms->basedir = cwd;
223 
224 	/* userdirs not really necessary for windows guys.
225 	 * can be done if necessary, though... */
226 	host_parms->userdir = host_parms->basedir; /* code elsewhere relies on this ! */
227 
228 	SYSTEM_INFO info;
229 	GetSystemInfo(&info);
230 	host_parms->numcpus = info.dwNumberOfProcessors;
231 	if (host_parms->numcpus < 1)
232 	{
233 		host_parms->numcpus = 1;
234 	}
235 	Sys_Printf("Detected %d CPUs.\n", host_parms->numcpus);
236 
237 	if (isDedicated)
238 	{
239 		if (!AllocConsole ())
240 		{
241 			isDedicated = false;	/* so that we have a graphical error dialog */
242 			Sys_Error ("Couldn't create dedicated server console");
243 		}
244 
245 		hinput = GetStdHandle (STD_INPUT_HANDLE);
246 		houtput = GetStdHandle (STD_OUTPUT_HANDLE);
247 	}
248 
249 	counter_freq = (double)SDL_GetPerformanceFrequency();
250 }
251 
Sys_mkdir(const char * path)252 void Sys_mkdir (const char *path)
253 {
254 	if (CreateDirectory(path, NULL) != 0)
255 		return;
256 	if (GetLastError() != ERROR_ALREADY_EXISTS)
257 		Sys_Error("Unable to create directory %s", path);
258 }
259 
260 static const char errortxt1[] = "\nERROR-OUT BEGIN\n\n";
261 static const char errortxt2[] = "\nQUAKE ERROR: ";
262 
Sys_Error(const char * error,...)263 void Sys_Error (const char *error, ...)
264 {
265 	va_list		argptr;
266 	char		text[1024];
267 	DWORD		dummy;
268 
269 	host_parms->errstate++;
270 
271 	va_start (argptr, error);
272 	q_vsnprintf (text, sizeof(text), error, argptr);
273 	va_end (argptr);
274 
275 	PR_SwitchQCVM(NULL);
276 
277 	if (isDedicated)
278 		WriteFile (houtput, errortxt1, strlen(errortxt1), &dummy, NULL);
279 	/* SDL will put these into its own stderr log,
280 	   so print to stderr even in graphical mode. */
281 	fputs (errortxt1, stderr);
282 	Host_Shutdown ();
283 	fputs (errortxt2, stderr);
284 	fputs (text, stderr);
285 	fputs ("\n\n", stderr);
286 	if (!isDedicated)
287 		PL_ErrorDialog(text);
288 	else
289 	{
290 		WriteFile (houtput, errortxt2, strlen(errortxt2), &dummy, NULL);
291 		WriteFile (houtput, text,      strlen(text),      &dummy, NULL);
292 		WriteFile (houtput, "\r\n",    2,		  &dummy, NULL);
293 		SDL_Delay (3000);	/* show the console 3 more seconds */
294 	}
295 
296 #ifdef _DEBUG
297 	__debugbreak();
298 #endif
299 
300 	exit (1);
301 }
302 
Sys_Printf(const char * fmt,...)303 void Sys_Printf (const char *fmt, ...)
304 {
305 	va_list		argptr;
306 	char		text[1024];
307 	DWORD		dummy;
308 
309 	va_start (argptr,fmt);
310 	q_vsnprintf (text, sizeof(text), fmt, argptr);
311 	va_end (argptr);
312 
313 	if (isDedicated)
314 	{
315 		WriteFile(houtput, text, strlen(text), &dummy, NULL);
316 	}
317 	else
318 	{
319 	/* SDL will put these into its own stdout log,
320 	   so print to stdout even in graphical mode. */
321 		fputs (text, stdout);
322 		OutputDebugStringA(text);
323 	}
324 }
325 
Sys_Quit(void)326 void Sys_Quit (void)
327 {
328 	Host_Shutdown();
329 
330 	if (isDedicated)
331 		FreeConsole ();
332 
333 	exit (0);
334 }
335 
Sys_DoubleTime(void)336 double Sys_DoubleTime (void)
337 {
338 	return (double)SDL_GetPerformanceCounter() / counter_freq;
339 }
340 
Sys_ConsoleInput(void)341 const char *Sys_ConsoleInput (void)
342 {
343 	static char	con_text[256];
344 	static int	textlen;
345 	INPUT_RECORD	recs[1024];
346 	int		ch;
347 	DWORD		dummy, numread, numevents;
348 
349 	for ( ;; )
350 	{
351 		if (GetNumberOfConsoleInputEvents(hinput, &numevents) == 0)
352 			Sys_Error ("Error getting # of console events");
353 
354 		if (! numevents)
355 			break;
356 
357 		if (ReadConsoleInput(hinput, recs, 1, &numread) == 0)
358 			Sys_Error ("Error reading console input");
359 
360 		if (numread != 1)
361 			Sys_Error ("Couldn't read console input");
362 
363 		if (recs[0].EventType == KEY_EVENT)
364 		{
365 		    if (recs[0].Event.KeyEvent.bKeyDown == FALSE)
366 		    {
367 			ch = recs[0].Event.KeyEvent.uChar.AsciiChar;
368 
369 			switch (ch)
370 			{
371 			case '\r':
372 				WriteFile(houtput, "\r\n", 2, &dummy, NULL);
373 
374 				if (textlen != 0)
375 				{
376 					con_text[textlen] = 0;
377 					textlen = 0;
378 					return con_text;
379 				}
380 
381 				break;
382 
383 			case '\b':
384 				WriteFile(houtput, "\b \b", 3, &dummy, NULL);
385 				if (textlen != 0)
386 					textlen--;
387 
388 				break;
389 
390 			default:
391 				if (ch >= ' ')
392 				{
393 					WriteFile(houtput, &ch, 1, &dummy, NULL);
394 					con_text[textlen] = ch;
395 					textlen = (textlen + 1) & 0xff;
396 				}
397 
398 				break;
399 			}
400 		    }
401 		}
402 	}
403 
404 	return NULL;
405 }
406 
Sys_Sleep(unsigned long msecs)407 void Sys_Sleep (unsigned long msecs)
408 {
409 /*	Sleep (msecs);*/
410 	SDL_Delay (msecs);
411 }
412 
Sys_SendKeyEvents(void)413 void Sys_SendKeyEvents (void)
414 {
415 	IN_Commands();		//ericw -- allow joysticks to add keys so they can be used to confirm SCR_ModalMessage
416 	IN_SendKeyEvents();
417 }
418 
419