1 /*
2 ===========================================================================
3 Copyright (C) 2005 - 2015, ioquake3 contributors
4 Copyright (C) 2013 - 2015, OpenJK contributors
5 
6 This file is part of the OpenJK source code.
7 
8 OpenJK is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License version 2 as
10 published by the Free Software Foundation.
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.  See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, see <http://www.gnu.org/licenses/>.
19 ===========================================================================
20 */
21 
22 #include "qcommon/q_version.h"
23 #include "sys_local.h"
24 #include <direct.h>
25 #include <io.h>
26 #include <shlobj.h>
27 #include <windows.h>
28 
29 #define MEM_THRESHOLD (128*1024*1024)
30 
31 // Used to determine where to store user-specific files
32 static char homePath[ MAX_OSPATH ] = { 0 };
33 
34 static UINT timerResolution = 0;
35 
36 /*
37 ==============
38 Sys_Basename
39 ==============
40 */
Sys_Basename(char * path)41 const char *Sys_Basename( char *path )
42 {
43 	static char base[ MAX_OSPATH ] = { 0 };
44 	int length;
45 
46 	length = strlen( path ) - 1;
47 
48 	// Skip trailing slashes
49 	while( length > 0 && path[ length ] == '\\' )
50 		length--;
51 
52 	while( length > 0 && path[ length - 1 ] != '\\' )
53 		length--;
54 
55 	Q_strncpyz( base, &path[ length ], sizeof( base ) );
56 
57 	length = strlen( base ) - 1;
58 
59 	// Strip trailing slashes
60 	while( length > 0 && base[ length ] == '\\' )
61     base[ length-- ] = '\0';
62 
63 	return base;
64 }
65 
66 /*
67 ==============
68 Sys_Dirname
69 ==============
70 */
Sys_Dirname(char * path)71 const char *Sys_Dirname( char *path )
72 {
73 	static char dir[ MAX_OSPATH ] = { 0 };
74 	int length;
75 
76 	Q_strncpyz( dir, path, sizeof( dir ) );
77 	length = strlen( dir ) - 1;
78 
79 	while( length > 0 && dir[ length ] != '\\' )
80 		length--;
81 
82 	dir[ length ] = '\0';
83 
84 	return dir;
85 }
86 
87 /*
88 ================
89 Sys_Milliseconds
90 ================
91 */
Sys_Milliseconds(bool baseTime)92 int Sys_Milliseconds (bool baseTime)
93 {
94 	static int sys_timeBase = timeGetTime();
95 	int			sys_curtime;
96 
97 	sys_curtime = timeGetTime();
98 	if(!baseTime)
99 	{
100 		sys_curtime -= sys_timeBase;
101 	}
102 
103 	return sys_curtime;
104 }
105 
Sys_Milliseconds2(void)106 int Sys_Milliseconds2( void )
107 {
108 	return Sys_Milliseconds(false);
109 }
110 
111 /*
112 ================
113 Sys_RandomBytes
114 ================
115 */
Sys_RandomBytes(byte * string,int len)116 bool Sys_RandomBytes( byte *string, int len )
117 {
118 	HCRYPTPROV  prov;
119 
120 	if( !CryptAcquireContext( &prov, NULL, NULL,
121 		PROV_RSA_FULL, CRYPT_VERIFYCONTEXT ) )  {
122 
123 		return false;
124 	}
125 
126 	if( !CryptGenRandom( prov, len, (BYTE *)string ) )  {
127 		CryptReleaseContext( prov, 0 );
128 		return false;
129 	}
130 	CryptReleaseContext( prov, 0 );
131 	return true;
132 }
133 
134 /*
135 ==================
136 Sys_GetCurrentUser
137 ==================
138 */
Sys_GetCurrentUser(void)139 char *Sys_GetCurrentUser( void )
140 {
141 	static char s_userName[1024];
142 	DWORD size = sizeof( s_userName );
143 
144 	if ( !GetUserName( s_userName, &size ) )
145 		strcpy( s_userName, "player" );
146 
147 	if ( !s_userName[0] )
148 	{
149 		strcpy( s_userName, "player" );
150 	}
151 
152 	return s_userName;
153 }
154 
155 /*
156 * Builds the path for the user's game directory
157 */
Sys_DefaultHomePath(void)158 char *Sys_DefaultHomePath( void )
159 {
160 #if defined(BUILD_PORTABLE)
161 	Com_Printf( "Portable install requested, skipping homepath support\n" );
162 	return NULL;
163 #else
164 	if ( !homePath[0] )
165 	{
166 		TCHAR homeDirectory[MAX_PATH];
167 
168 		if( !SUCCEEDED( SHGetFolderPath( NULL, CSIDL_PERSONAL, NULL, 0, homeDirectory ) ) )
169 		{
170 			Com_Printf( "Unable to determine your home directory.\n" );
171 			return NULL;
172 		}
173 
174 		Com_sprintf( homePath, sizeof( homePath ), "%s%cMy Games%c", homeDirectory, PATH_SEP, PATH_SEP );
175 		if ( com_homepath && com_homepath->string[0] )
176 			Q_strcat( homePath, sizeof( homePath ), com_homepath->string );
177 		else
178 			Q_strcat( homePath, sizeof( homePath ), HOMEPATH_NAME_WIN );
179 	}
180 
181 	return homePath;
182 #endif
183 }
184 
GetErrorString(DWORD error)185 static const char *GetErrorString( DWORD error ) {
186 	static char buf[MAX_STRING_CHARS];
187 	buf[0] = '\0';
188 
189 	if ( error ) {
190 		LPVOID lpMsgBuf;
191 		DWORD bufLen = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
192 			NULL, error, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), (LPTSTR)&lpMsgBuf, 0, NULL );
193 		if ( bufLen ) {
194 			LPCSTR lpMsgStr = (LPCSTR)lpMsgBuf;
195 			Q_strncpyz( buf, lpMsgStr, Q_min( (size_t)(lpMsgStr + bufLen), sizeof(buf) ) );
196 			LocalFree( lpMsgBuf );
197 		}
198 	}
199 	return buf;
200 }
201 
Sys_SetProcessorAffinity(void)202 void Sys_SetProcessorAffinity( void ) {
203 	DWORD_PTR processMask, processAffinityMask, systemAffinityMask;
204 	HANDLE handle = GetCurrentProcess();
205 
206 	if ( !GetProcessAffinityMask( handle, &processAffinityMask, &systemAffinityMask ) )
207 		return;
208 
209 	if ( sscanf( com_affinity->string, "%X", &processMask ) != 1 )
210 		processMask = 1; // set to first core only
211 
212 	if ( !processMask )
213 		processMask = systemAffinityMask; // use all the cores available to the system
214 
215 	if ( processMask == processAffinityMask )
216 		return; // no change
217 
218 	if ( !SetProcessAffinityMask( handle, processMask ) )
219 		Com_DPrintf( "Setting affinity mask failed (%s)\n", GetErrorString( GetLastError() ) );
220 }
221 
222 /*
223 ==================
224 Sys_LowPhysicalMemory()
225 ==================
226 */
227 
Sys_LowPhysicalMemory(void)228 qboolean Sys_LowPhysicalMemory(void) {
229 	static MEMORYSTATUSEX stat;
230 	static qboolean bAsked = qfalse;
231 	static cvar_t* sys_lowmem = Cvar_Get( "sys_lowmem", "0", 0 );
232 
233 	if (!bAsked)	// just in case it takes a little time for GlobalMemoryStatusEx() to gather stats on
234 	{				//	stuff we don't care about such as virtual mem etc.
235 		bAsked = qtrue;
236 
237 		stat.dwLength = sizeof (stat);
238 		GlobalMemoryStatusEx (&stat);
239 	}
240 	if (sys_lowmem->integer)
241 	{
242 		return qtrue;
243 	}
244 	return (stat.ullTotalPhys <= MEM_THRESHOLD) ? qtrue : qfalse;
245 }
246 
247 /*
248 ==============
249 Sys_Mkdir
250 ==============
251 */
Sys_Mkdir(const char * path)252 qboolean Sys_Mkdir( const char *path ) {
253 	if( !CreateDirectory( path, NULL ) )
254 	{
255 		if( GetLastError( ) != ERROR_ALREADY_EXISTS )
256 			return qfalse;
257 	}
258 	return qtrue;
259 }
260 
261 /*
262 ==============
263 Sys_Cwd
264 ==============
265 */
Sys_Cwd(void)266 char *Sys_Cwd( void ) {
267 	static char cwd[MAX_OSPATH];
268 
269 	_getcwd( cwd, sizeof( cwd ) - 1 );
270 	cwd[MAX_OSPATH-1] = 0;
271 
272 	return cwd;
273 }
274 
275 /* Resolves path names and determines if they are the same */
276 /* For use with full OS paths not quake paths */
277 /* Returns true if resulting paths are valid and the same, otherwise false */
Sys_PathCmp(const char * path1,const char * path2)278 bool Sys_PathCmp( const char *path1, const char *path2 ) {
279 	char *r1, *r2;
280 
281 	r1 = _fullpath(NULL, path1, MAX_OSPATH);
282 	r2 = _fullpath(NULL, path2, MAX_OSPATH);
283 
284 	if(r1 && r2 && !Q_stricmp(r1, r2))
285 	{
286 		free(r1);
287 		free(r2);
288 		return true;
289 	}
290 
291 	free(r1);
292 	free(r2);
293 	return false;
294 }
295 
296 /*
297 ==============================================================
298 
299 DIRECTORY SCANNING
300 
301 ==============================================================
302 */
303 
304 #define	MAX_FOUND_FILES	0x1000
305 
Sys_ListFilteredFiles(const char * basedir,char * subdirs,char * filter,char ** psList,int * numfiles)306 void Sys_ListFilteredFiles( const char *basedir, char *subdirs, char *filter, char **psList, int *numfiles ) {
307 	char		search[MAX_OSPATH], newsubdirs[MAX_OSPATH];
308 	char		filename[MAX_OSPATH];
309 	intptr_t	findhandle;
310 	struct _finddata_t findinfo;
311 
312 	if ( *numfiles >= MAX_FOUND_FILES - 1 ) {
313 		return;
314 	}
315 
316 	if (strlen(subdirs)) {
317 		Com_sprintf( search, sizeof(search), "%s\\%s\\*", basedir, subdirs );
318 	}
319 	else {
320 		Com_sprintf( search, sizeof(search), "%s\\*", basedir );
321 	}
322 
323 	findhandle = _findfirst (search, &findinfo);
324 	if (findhandle == -1) {
325 		return;
326 	}
327 
328 	do {
329 		if (findinfo.attrib & _A_SUBDIR) {
330 			if (Q_stricmp(findinfo.name, ".") && Q_stricmp(findinfo.name, "..")) {
331 				if (strlen(subdirs)) {
332 					Com_sprintf( newsubdirs, sizeof(newsubdirs), "%s\\%s", subdirs, findinfo.name);
333 				}
334 				else {
335 					Com_sprintf( newsubdirs, sizeof(newsubdirs), "%s", findinfo.name);
336 				}
337 				Sys_ListFilteredFiles( basedir, newsubdirs, filter, psList, numfiles );
338 			}
339 		}
340 		if ( *numfiles >= MAX_FOUND_FILES - 1 ) {
341 			break;
342 		}
343 		Com_sprintf( filename, sizeof(filename), "%s\\%s", subdirs, findinfo.name );
344 		if (!Com_FilterPath( filter, filename, qfalse ))
345 			continue;
346 		psList[ *numfiles ] = CopyString( filename );
347 		(*numfiles)++;
348 	} while ( _findnext (findhandle, &findinfo) != -1 );
349 
350 	_findclose (findhandle);
351 }
352 
strgtr(const char * s0,const char * s1)353 static qboolean strgtr(const char *s0, const char *s1) {
354 	int l0, l1, i;
355 
356 	l0 = strlen(s0);
357 	l1 = strlen(s1);
358 
359 	if (l1<l0) {
360 		l0 = l1;
361 	}
362 
363 	for(i=0;i<l0;i++) {
364 		if (s1[i] > s0[i]) {
365 			return qtrue;
366 		}
367 		if (s1[i] < s0[i]) {
368 			return qfalse;
369 		}
370 	}
371 	return qfalse;
372 }
373 
Sys_ListFiles(const char * directory,const char * extension,char * filter,int * numfiles,qboolean wantsubs)374 char **Sys_ListFiles( const char *directory, const char *extension, char *filter, int *numfiles, qboolean wantsubs ) {
375 	char		search[MAX_OSPATH];
376 	int			nfiles;
377 	char		**listCopy;
378 	char		*list[MAX_FOUND_FILES];
379 	struct _finddata_t findinfo;
380 	intptr_t	findhandle;
381 	int			flag;
382 	int			i;
383 	int			extLen;
384 
385 	if (filter) {
386 
387 		nfiles = 0;
388 		Sys_ListFilteredFiles( directory, "", filter, list, &nfiles );
389 
390 		list[ nfiles ] = 0;
391 		*numfiles = nfiles;
392 
393 		if (!nfiles)
394 			return NULL;
395 
396 		listCopy = (char **)Z_Malloc( ( nfiles + 1 ) * sizeof( *listCopy ), TAG_LISTFILES );
397 		for ( i = 0 ; i < nfiles ; i++ ) {
398 			listCopy[i] = list[i];
399 		}
400 		listCopy[i] = NULL;
401 
402 		return listCopy;
403 	}
404 
405 	if ( !extension) {
406 		extension = "";
407 	}
408 
409 	// passing a slash as extension will find directories
410 	if ( extension[0] == '/' && extension[1] == 0 ) {
411 		extension = "";
412 		flag = 0;
413 	} else {
414 		flag = _A_SUBDIR;
415 	}
416 
417 	extLen = strlen( extension );
418 
419 	Com_sprintf( search, sizeof(search), "%s\\*%s", directory, extension );
420 
421 	// search
422 	nfiles = 0;
423 
424 	findhandle = _findfirst (search, &findinfo);
425 	if (findhandle == -1) {
426 		*numfiles = 0;
427 		return NULL;
428 	}
429 
430 	do {
431 		if ( (!wantsubs && flag ^ ( findinfo.attrib & _A_SUBDIR )) || (wantsubs && findinfo.attrib & _A_SUBDIR) ) {
432 			if (*extension) {
433 				if ( strlen( findinfo.name ) < extLen ||
434 					Q_stricmp(
435 						findinfo.name + strlen( findinfo.name ) - extLen,
436 						extension ) ) {
437 					continue; // didn't match
438 				}
439 			}
440 			if ( nfiles == MAX_FOUND_FILES - 1 ) {
441 				break;
442 			}
443 			list[ nfiles ] = CopyString( findinfo.name );
444 			nfiles++;
445 		}
446 	} while ( _findnext (findhandle, &findinfo) != -1 );
447 
448 	list[ nfiles ] = 0;
449 
450 	_findclose (findhandle);
451 
452 	// return a copy of the list
453 	*numfiles = nfiles;
454 
455 	if ( !nfiles ) {
456 		return NULL;
457 	}
458 
459 	listCopy = (char **)Z_Malloc( ( nfiles + 1 ) * sizeof( *listCopy ), TAG_LISTFILES );
460 	for ( i = 0 ; i < nfiles ; i++ ) {
461 		listCopy[i] = list[i];
462 	}
463 	listCopy[i] = NULL;
464 
465 	do {
466 		flag = 0;
467 		for(i=1; i<nfiles; i++) {
468 			if (strgtr(listCopy[i-1], listCopy[i])) {
469 				char *temp = listCopy[i];
470 				listCopy[i] = listCopy[i-1];
471 				listCopy[i-1] = temp;
472 				flag = 1;
473 			}
474 		}
475 	} while(flag);
476 
477 	return listCopy;
478 }
479 
Sys_FreeFileList(char ** psList)480 void	Sys_FreeFileList( char **psList ) {
481 	int		i;
482 
483 	if ( !psList ) {
484 		return;
485 	}
486 
487 	for ( i = 0 ; psList[i] ; i++ ) {
488 		Z_Free( psList[i] );
489 	}
490 
491 	Z_Free( psList );
492 }
493 
494 /*
495 ========================================================================
496 
497 LOAD/UNLOAD DLL
498 
499 ========================================================================
500 */
501 //make sure the dll can be opened by the file system, then write the
502 //file back out again so it can be loaded is a library. If the read
503 //fails then the dll is probably not in the pk3 and we are running
504 //a pure server -rww
Sys_UnpackDLL(const char * name)505 UnpackDLLResult Sys_UnpackDLL(const char *name)
506 {
507 	UnpackDLLResult result = {};
508 	void *data;
509 	long len = FS_ReadFile(name, &data);
510 
511 	if (len >= 1)
512 	{
513 		if (FS_FileIsInPAK(name, NULL) == 1)
514 		{
515 			char *tempFileName;
516 			if ( FS_WriteToTemporaryFile(data, len, &tempFileName) )
517 			{
518 				result.tempDLLPath = tempFileName;
519 				result.succeeded = true;
520 			}
521 		}
522 	}
523 
524 	FS_FreeFile(data);
525 
526 	return result;
527 }
528 
Sys_DLLNeedsUnpacking()529 bool Sys_DLLNeedsUnpacking()
530 {
531 #if defined(_JK2EXE)
532 	return false;
533 #else
534 	return Cvar_VariableIntegerValue("sv_pure") != 0;
535 #endif
536 }
537 
538 /*
539 ================
540 Sys_PlatformInit
541 
542 Platform-specific initialization
543 ================
544 */
Sys_PlatformInit(void)545 void Sys_PlatformInit( void ) {
546 	TIMECAPS ptc;
547 	if ( timeGetDevCaps( &ptc, sizeof( ptc ) ) == MMSYSERR_NOERROR )
548 	{
549 		timerResolution = ptc.wPeriodMin;
550 
551 		if ( timerResolution > 1 )
552 		{
553 			Com_Printf( "Warning: Minimum supported timer resolution is %ums "
554 				"on this system, recommended resolution 1ms\n", timerResolution );
555 		}
556 
557 		timeBeginPeriod( timerResolution );
558 	}
559 	else
560 		timerResolution = 0;
561 }
562 
563 /*
564 ================
565 Sys_PlatformExit
566 
567 Platform-specific exit code
568 ================
569 */
Sys_PlatformExit(void)570 void Sys_PlatformExit( void )
571 {
572 	if ( timerResolution )
573 		timeEndPeriod( timerResolution );
574 }
575 
Sys_Sleep(int msec)576 void Sys_Sleep( int msec )
577 {
578 	if ( msec == 0 )
579 		return;
580 
581 #ifdef DEDICATED
582 	if ( msec < 0 )
583 		WaitForSingleObject( GetStdHandle( STD_INPUT_HANDLE ), INFINITE );
584 	else
585 		WaitForSingleObject( GetStdHandle( STD_INPUT_HANDLE ), msec );
586 #else
587 	// Client Sys_Sleep doesn't support waiting on stdin
588 	if ( msec < 0 )
589 		return;
590 
591 	Sleep( msec );
592 #endif
593 }
594