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