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