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