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 <sys/types.h>
26 #include <limits.h>
27 #include <windows.h>
28 #include <mmsystem.h>
29
30
31 // heapsize: minimum 8 mb, standart 16 mb, max is 32 mb.
32 // -heapsize argument will abide by these min/max settings
33 // unless the -forcemem argument is used
34 #define MIN_MEM_ALLOC 0x0800000
35 #define STD_MEM_ALLOC 0x1000000
36 #define MAX_MEM_ALLOC 0x2000000
37
38 cvar_t sys_nostdout = {"sys_nostdout", "0", CVAR_NONE};
39 int devlog; /* log the Con_DPrintf and Sys_DPrintf content when !developer.integer */
40
41 #define TIME_WRAP_VALUE (~(DWORD)0)
42 static DWORD starttime;
43 static HANDLE hinput, houtput;
44
45
46 /*
47 ===============================================================================
48
49 FILE IO
50
51 ===============================================================================
52 */
53
Sys_mkdir(const char * path,qboolean crash)54 int Sys_mkdir (const char *path, qboolean crash)
55 {
56 if (CreateDirectory(path, NULL) != 0)
57 return 0;
58 if (GetLastError() == ERROR_ALREADY_EXISTS)
59 return 0;
60 if (crash)
61 Sys_Error("Unable to create directory %s", path);
62 return -1;
63 }
64
Sys_rmdir(const char * path)65 int Sys_rmdir (const char *path)
66 {
67 if (RemoveDirectory(path) != 0)
68 return 0;
69 return -1;
70 }
71
Sys_unlink(const char * path)72 int Sys_unlink (const char *path)
73 {
74 if (DeleteFile(path) != 0)
75 return 0;
76 return -1;
77 }
78
Sys_rename(const char * oldp,const char * newp)79 int Sys_rename (const char *oldp, const char *newp)
80 {
81 if (MoveFile(oldp, newp) != 0)
82 return 0;
83 return -1;
84 }
85
Sys_filesize(const char * path)86 long Sys_filesize (const char *path)
87 {
88 HANDLE fh;
89 WIN32_FIND_DATA data;
90 long size;
91
92 fh = FindFirstFile(path, &data);
93 if (fh == INVALID_HANDLE_VALUE)
94 return -1;
95 FindClose(fh);
96 if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
97 return -1;
98 // we're not dealing with gigabytes of files.
99 // size should normally smaller than INT_MAX.
100 // size = (data.nFileSizeHigh * (MAXDWORD + 1)) + data.nFileSizeLow;
101 size = (long) data.nFileSizeLow;
102 return size;
103 }
104
105 #ifndef INVALID_FILE_ATTRIBUTES
106 #define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
107 #endif
Sys_FileType(const char * path)108 int Sys_FileType (const char *path)
109 {
110 DWORD result = GetFileAttributes(path);
111
112 if (result == INVALID_FILE_ATTRIBUTES)
113 return FS_ENT_NONE;
114 if (result & FILE_ATTRIBUTE_DIRECTORY)
115 return FS_ENT_DIRECTORY;
116
117 return FS_ENT_FILE;
118 }
119
Sys_CopyFile(const char * frompath,const char * topath)120 int Sys_CopyFile (const char *frompath, const char *topath)
121 {
122 /* 3rd param: whether to fail if 'topath' already exists */
123 if (CopyFile(frompath, topath, FALSE) != 0)
124 return 0;
125 return -1;
126 }
127
128 /*
129 =================================================
130 simplified findfirst/findnext implementation:
131 Sys_FindFirstFile and Sys_FindNextFile return
132 filenames only, not a dirent struct. this is
133 what we presently need in this engine.
134 =================================================
135 */
136 static HANDLE findhandle = INVALID_HANDLE_VALUE;
137 static WIN32_FIND_DATA finddata;
138 static char findstr[MAX_OSPATH];
139
Sys_FindFirstFile(const char * path,const char * pattern)140 const char *Sys_FindFirstFile (const char *path, const char *pattern)
141 {
142 if (findhandle != INVALID_HANDLE_VALUE)
143 Sys_Error ("Sys_FindFirst without FindClose");
144 q_snprintf (findstr, sizeof(findstr), "%s/%s", path, pattern);
145 findhandle = FindFirstFile(findstr, &finddata);
146 if (findhandle == INVALID_HANDLE_VALUE)
147 return NULL;
148 if (finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
149 return Sys_FindNextFile();
150 return finddata.cFileName;
151 }
152
Sys_FindNextFile(void)153 const char *Sys_FindNextFile (void)
154 {
155 if (findhandle == INVALID_HANDLE_VALUE)
156 return NULL;
157 while (FindNextFile(findhandle, &finddata) != 0)
158 {
159 if (finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
160 continue;
161 return finddata.cFileName;
162 }
163 return NULL;
164 }
165
Sys_FindClose(void)166 void Sys_FindClose (void)
167 {
168 if (findhandle != INVALID_HANDLE_VALUE)
169 {
170 FindClose(findhandle);
171 findhandle = INVALID_HANDLE_VALUE;
172 }
173 }
174
175
176 /*
177 ===============================================================================
178
179 SYSTEM IO
180
181 ===============================================================================
182 */
183
184 #define ERROR_PREFIX "\nFATAL ERROR: "
Sys_Error(const char * error,...)185 void Sys_Error (const char *error, ...)
186 {
187 va_list argptr;
188 char text[MAX_PRINTMSG];
189 const char text2[] = ERROR_PREFIX;
190 const char text3[] = "\n";
191 DWORD dummy;
192
193 host_parms->errstate++;
194
195 va_start (argptr, error);
196 q_vsnprintf (text, sizeof(text), error, argptr);
197 va_end (argptr);
198
199 if (sv_logfile)
200 {
201 fprintf (sv_logfile, ERROR_PREFIX "%s\n\n", text);
202 fflush (sv_logfile);
203 }
204
205 WriteFile(houtput, text2, strlen(text2), &dummy, NULL);
206 WriteFile(houtput, text, strlen(text), &dummy, NULL);
207 WriteFile(houtput, text3, strlen(text3), &dummy, NULL);
208
209 exit (1);
210 }
211
Sys_PrintTerm(const char * msgtxt)212 void Sys_PrintTerm (const char *msgtxt)
213 {
214 DWORD dummy;
215
216 if (sys_nostdout.integer)
217 return;
218
219 WriteFile(houtput, msgtxt, strlen(msgtxt), &dummy, NULL);
220 }
221
Sys_Quit(void)222 void Sys_Quit (void)
223 {
224 exit (0);
225 }
226
227
228 /*
229 ================
230 Sys_DoubleTime
231 ================
232 */
Sys_DoubleTime(void)233 double Sys_DoubleTime (void)
234 {
235 DWORD now, passed;
236
237 now = timeGetTime();
238 if (now < starttime) /* wrapped? */
239 {
240 passed = TIME_WRAP_VALUE - starttime;
241 passed += now;
242 }
243 else
244 {
245 passed = now - starttime;
246 }
247
248 return (passed == 0) ? 0.0 : (passed / 1000.0);
249 }
250
251
Sys_DateTimeString(char * buf)252 char *Sys_DateTimeString (char *buf)
253 {
254 static char strbuf[24];
255 SYSTEMTIME st;
256 int val;
257
258 if (!buf) buf = strbuf;
259
260 GetLocalTime(&st);
261
262 val = st.wMonth;
263 buf[0] = val / 10 + '0';
264 buf[1] = val % 10 + '0';
265 buf[2] = '/';
266 val = st.wDay;
267 buf[3] = val / 10 + '0';
268 buf[4] = val % 10 + '0';
269 buf[5] = '/';
270 val = st.wYear / 100;
271 buf[6] = val / 10 + '0';
272 buf[7] = val % 10 + '0';
273 val = st.wYear % 100;
274 buf[8] = val / 10 + '0';
275 buf[9] = val % 10 + '0';
276
277 buf[10] = ' ';
278
279 val = st.wHour;
280 buf[11] = val / 10 + '0';
281 buf[12] = val % 10 + '0';
282 buf[13] = ':';
283 val = st.wMinute;
284 buf[14] = val / 10 + '0';
285 buf[15] = val % 10 + '0';
286 buf[16] = ':';
287 val = st.wSecond;
288 buf[17] = val / 10 + '0';
289 buf[18] = val % 10 + '0';
290
291 buf[19] = '\0';
292
293 return buf;
294 }
295
296
Sys_ConsoleInput(void)297 const char *Sys_ConsoleInput (void)
298 {
299 static char con_text[256];
300 static int textlen;
301 INPUT_RECORD recs[1024];
302 int ch;
303 DWORD dummy, numread, numevents;
304
305 for ( ;; )
306 {
307 if (GetNumberOfConsoleInputEvents(hinput, &numevents) == 0)
308 Sys_Error ("Error getting # of console events");
309
310 if (! numevents)
311 break;
312
313 if (ReadConsoleInput(hinput, recs, 1, &numread) == 0)
314 Sys_Error ("Error reading console input");
315
316 if (numread != 1)
317 Sys_Error ("Couldn't read console input");
318
319 if (recs[0].EventType == KEY_EVENT)
320 {
321 if (recs[0].Event.KeyEvent.bKeyDown == FALSE)
322 {
323 ch = recs[0].Event.KeyEvent.uChar.AsciiChar;
324
325 switch (ch)
326 {
327 case '\r':
328 WriteFile(houtput, "\r\n", 2, &dummy, NULL);
329 if (textlen != 0)
330 {
331 con_text[textlen] = 0;
332 textlen = 0;
333 return con_text;
334 }
335
336 break;
337
338 case '\b':
339 WriteFile(houtput, "\b \b", 3, &dummy, NULL);
340 if (textlen != 0)
341 textlen--;
342
343 break;
344
345 default:
346 if (ch >= ' ')
347 {
348 WriteFile(houtput, &ch, 1, &dummy, NULL);
349 con_text[textlen] = ch;
350 textlen = (textlen + 1) & 0xff;
351 }
352
353 break;
354 }
355 }
356 }
357 }
358
359 return NULL;
360 }
361
362
Sys_GetBasedir(char * argv0,char * dst,size_t dstsize)363 static int Sys_GetBasedir (char *argv0, char *dst, size_t dstsize)
364 {
365 char *tmp;
366 size_t rc;
367
368 rc = GetCurrentDirectory(dstsize, dst);
369 if (rc == 0 || rc > dstsize)
370 return -1;
371
372 tmp = dst;
373 while (*tmp != 0)
374 tmp++;
375 while (*tmp == 0 && tmp != dst)
376 {
377 --tmp;
378 if (tmp != dst && (*tmp == '/' || *tmp == '\\'))
379 *tmp = 0;
380 }
381
382 return 0;
383 }
384
PrintVersion(void)385 static void PrintVersion (void)
386 {
387 Sys_Printf ("HexenWorld server %4.2f (%s)\n", ENGINE_VERSION, PLATFORM_STRING);
388 Sys_Printf ("Hammer of Thyrion, release %s (%s)\n", HOT_VERSION_STR, HOT_VERSION_REL_DATE);
389 }
390
391 /*
392 ===============================================================================
393
394 MAIN
395
396 ===============================================================================
397 */
398 static quakeparms_t parms;
399 static char cwd[MAX_OSPATH];
400
main(int argc,char ** argv)401 int main (int argc, char **argv)
402 {
403 int i;
404 double newtime, time, oldtime;
405
406 hinput = GetStdHandle (STD_INPUT_HANDLE);
407 houtput = GetStdHandle (STD_OUTPUT_HANDLE);
408
409 PrintVersion();
410
411 if (argc > 1)
412 {
413 for (i = 1; i < argc; i++)
414 {
415 if ( !(strcmp(argv[i], "-v")) || !(strcmp(argv[i], "-version" )) ||
416 !(strcmp(argv[i], "--version")) )
417 {
418 exit(0);
419 }
420 else if ( !(strcmp(argv[i], "-h")) || !(strcmp(argv[i], "-help" )) ||
421 !(strcmp(argv[i], "--help")) || !(strcmp(argv[i], "-?")) )
422 {
423 Sys_PrintTerm ("See the documentation for details\n");
424 exit (0);
425 }
426 }
427 }
428
429 /* initialize the host params */
430 memset (&parms, 0, sizeof(parms));
431 parms.basedir = cwd;
432 parms.userdir = cwd; /* no userdir on win32 */
433 parms.argc = argc;
434 parms.argv = argv;
435 parms.errstate = 0;
436 host_parms = &parms;
437
438 memset (cwd, 0, sizeof(cwd));
439 if (Sys_GetBasedir(argv[0], cwd, sizeof(cwd)) != 0)
440 Sys_Error ("Couldn't determine current directory");
441
442 devlog = COM_CheckParm("-devlog");
443
444 Sys_Printf("basedir is: %s\n", parms.basedir);
445 Sys_Printf("userdir is: %s\n", parms.userdir);
446
447 COM_ValidateByteorder ();
448
449 parms.memsize = STD_MEM_ALLOC;
450
451 i = COM_CheckParm ("-heapsize");
452 if (i && i < com_argc-1)
453 {
454 parms.memsize = atoi (com_argv[i+1]) * 1024;
455
456 if ((parms.memsize > MAX_MEM_ALLOC) && !(COM_CheckParm ("-forcemem")))
457 {
458 Sys_Printf ("Requested memory (%d Mb) too large, using the default maximum.\n", parms.memsize/(1024*1024));
459 Sys_Printf ("If you are sure, use the -forcemem switch.\n");
460 parms.memsize = MAX_MEM_ALLOC;
461 }
462 else if ((parms.memsize < MIN_MEM_ALLOC) && !(COM_CheckParm ("-forcemem")))
463 {
464 Sys_Printf ("Requested memory (%d Mb) too little, using the default minimum.\n", parms.memsize/(1024*1024));
465 Sys_Printf ("If you are sure, use the -forcemem switch.\n");
466 parms.memsize = MIN_MEM_ALLOC;
467 }
468 }
469
470 parms.membase = malloc (parms.memsize);
471
472 if (!parms.membase)
473 Sys_Error ("Insufficient memory.\n");
474
475 timeBeginPeriod (1); /* 1 ms timer precision */
476 starttime = timeGetTime ();
477
478 SV_Init();
479
480 // report the filesystem to the user
481 Sys_Printf("gamedir is: %s\n", FS_GetGamedir());
482 Sys_Printf("userdir is: %s\n", FS_GetUserdir());
483
484 // run one frame immediately for first heartbeat
485 SV_Frame (HX_FRAME_TIME);
486
487 //
488 // main loop
489 //
490 oldtime = Sys_DoubleTime () - HX_FRAME_TIME;
491 while (1)
492 {
493 if (NET_CheckReadTimeout(0, 10000) == -1)
494 continue;
495
496 newtime = Sys_DoubleTime ();
497 time = newtime - oldtime;
498 oldtime = newtime;
499
500 SV_Frame (time);
501 }
502
503 return 0;
504 }
505
506