]> git.xonotic.org Git - xonotic/darkplaces.git/blob - sys_shared.c
0bb69c2576d2cffc77006a1743cb9e831d46cac9
[xonotic/darkplaces.git] / sys_shared.c
1 #ifdef WIN32
2 # ifndef DONT_USE_SETDLLDIRECTORY
3 #  define _WIN32_WINNT 0x0502
4 # endif
5 #endif
6
7 #include "quakedef.h"
8 #include "taskqueue.h"
9 #include "thread.h"
10 #include "libcurl.h"
11
12 #define SUPPORTDLL
13
14 #ifdef WIN32
15 # include <windows.h>
16 # include <mmsystem.h> // timeGetTime
17 # include <time.h> // localtime
18 #ifdef _MSC_VER
19 #pragma comment(lib, "winmm.lib")
20 #endif
21 #else
22 # ifdef __FreeBSD__
23 #  include <sys/sysctl.h>
24 # endif
25 # include <unistd.h>
26 # include <fcntl.h>
27 # include <sys/time.h>
28 # include <time.h>
29 # ifdef SUPPORTDLL
30 #  include <dlfcn.h>
31 # endif
32 #endif
33
34 #include <signal.h>
35
36 static char sys_timestring[128];
37 char *Sys_TimeString(const char *timeformat)
38 {
39         time_t mytime = time(NULL);
40 #if _MSC_VER >= 1400
41         struct tm mytm;
42         localtime_s(&mytm, &mytime);
43         strftime(sys_timestring, sizeof(sys_timestring), timeformat, &mytm);
44 #else
45         strftime(sys_timestring, sizeof(sys_timestring), timeformat, localtime(&mytime));
46 #endif
47         return sys_timestring;
48 }
49
50
51 void Sys_Quit (int returnvalue)
52 {
53         // Unlock mutexes because the quit command may jump directly here, causing a deadlock
54         if ((cmd_local)->cbuf->lock)
55                 Cbuf_Unlock((cmd_local)->cbuf);
56         SV_UnlockThreadMutex();
57         TaskQueue_Frame(true);
58
59         if (Sys_CheckParm("-profilegameonly"))
60                 Sys_AllowProfiling(false);
61         host.state = host_shutdown;
62         Host_Shutdown();
63         exit(returnvalue);
64 }
65
66 #ifdef __cplusplus
67 extern "C"
68 #endif
69 void Sys_AllowProfiling(qbool enable)
70 {
71 #ifdef __ANDROID__
72 #ifdef USE_PROFILER
73         extern void monstartup(const char *libname);
74         extern void moncleanup(void);
75         if (enable)
76                 monstartup("libmain.so");
77         else
78                 moncleanup();
79 #endif
80 #elif (defined(__linux__) && (defined(__GLIBC__) || defined(__GNU_LIBRARY__))) || defined(__FreeBSD__)
81         extern int moncontrol(int);
82         moncontrol(enable);
83 #endif
84 }
85
86
87 /*
88 ===============================================================================
89
90 DLL MANAGEMENT
91
92 ===============================================================================
93 */
94
95 static qbool Sys_LoadDependencyFunctions(dllhandle_t dllhandle, const dllfunction_t *fcts, qbool complain, qbool has_next)
96 {
97         const dllfunction_t *func;
98         if(dllhandle)
99         {
100                 for (func = fcts; func && func->name != NULL; func++)
101                         if (!(*func->funcvariable = (void *) Sys_GetProcAddress (dllhandle, func->name)))
102                         {
103                                 if(complain)
104                                 {
105                                         Con_DPrintf (" - missing function \"%s\" - broken library!", func->name);
106                                         if(has_next)
107                                                 Con_DPrintf("\nContinuing with");
108                                 }
109                                 goto notfound;
110                         }
111                 return true;
112
113         notfound:
114                 for (func = fcts; func && func->name != NULL; func++)
115                         *func->funcvariable = NULL;
116         }
117         return false;
118 }
119
120 qbool Sys_LoadSelf(dllhandle_t *handle)
121 {
122         dllhandle_t dllhandle = 0;
123
124         if (handle == NULL)
125                 return false;
126 #ifdef WIN32
127         dllhandle = LoadLibrary (NULL);
128 #else
129         dllhandle = dlopen (NULL, RTLD_NOW | RTLD_GLOBAL);
130 #endif
131         *handle = dllhandle;
132         return true;
133 }
134
135 qbool Sys_LoadDependency (const char** dllnames, dllhandle_t* handle, const dllfunction_t *fcts)
136 {
137 #ifdef SUPPORTDLL
138         const dllfunction_t *func;
139         dllhandle_t dllhandle = 0;
140         unsigned int i;
141
142         if (handle == NULL)
143                 return false;
144
145 #ifndef WIN32
146 #ifdef PREFER_PRELOAD
147         dllhandle = dlopen(NULL, RTLD_LAZY | RTLD_GLOBAL);
148         if(Sys_LoadDependencyFunctions(dllhandle, fcts, false, false))
149         {
150                 Con_DPrintf ("All of %s's functions were already linked in! Not loading dynamically...\n", dllnames[0]);
151                 *handle = dllhandle;
152                 return true;
153         }
154         else
155                 Sys_FreeLibrary(&dllhandle);
156 notfound:
157 #endif
158 #endif
159
160         // Initializations
161         for (func = fcts; func && func->name != NULL; func++)
162                 *func->funcvariable = NULL;
163
164         // Try every possible name
165         Con_DPrintf ("Trying to load library...");
166         for (i = 0; dllnames[i] != NULL; i++)
167         {
168                 Con_DPrintf (" \"%s\"", dllnames[i]);
169 #ifdef WIN32
170 # ifndef DONT_USE_SETDLLDIRECTORY
171 #  ifdef _WIN64
172                 SetDllDirectory("bin64");
173 #  else
174                 SetDllDirectory("bin32");
175 #  endif
176 # endif
177 #endif
178                 if(Sys_LoadLibrary(dllnames[i], &dllhandle))
179                 {
180                         if (Sys_LoadDependencyFunctions(dllhandle, fcts, true, (dllnames[i+1] != NULL) || (strrchr(sys.argv[0], '/'))))
181                                 break;
182                         else
183                                 Sys_FreeLibrary (&dllhandle);
184                 }
185         }
186
187         // see if the names can be loaded relative to the executable path
188         // (this is for Mac OSX which does not check next to the executable)
189         if (!dllhandle && strrchr(sys.argv[0], '/'))
190         {
191                 char path[MAX_OSPATH];
192                 strlcpy(path, sys.argv[0], sizeof(path));
193                 strrchr(path, '/')[1] = 0;
194                 for (i = 0; dllnames[i] != NULL; i++)
195                 {
196                         char temp[MAX_OSPATH];
197                         strlcpy(temp, path, sizeof(temp));
198                         strlcat(temp, dllnames[i], sizeof(temp));
199                         Con_DPrintf (" \"%s\"", temp);
200
201                         if(Sys_LoadLibrary(temp, &dllhandle))
202                         {
203                                 if (Sys_LoadDependencyFunctions(dllhandle, fcts, true, (dllnames[i+1] != NULL) || (strrchr(sys.argv[0], '/'))))
204                                         break;
205                                 else
206                                         Sys_FreeLibrary (&dllhandle);
207                         }
208                 }
209         }
210
211         // No DLL found
212         if (! dllhandle)
213         {
214                 Con_DPrintf(" - failed.\n");
215                 return false;
216         }
217
218         Con_DPrintf(" - loaded.\n");
219         Con_Printf("Loaded library \"%s\"\n", dllnames[i]);
220
221         *handle = dllhandle;
222         return true;
223 #else
224         return false;
225 #endif
226 }
227
228 qbool Sys_LoadLibrary(const char *name, dllhandle_t *handle)
229 {
230         dllhandle_t dllhandle = 0;
231
232         if(handle == NULL)
233                 return false;
234
235 #ifdef SUPPORTDLL
236 # ifdef WIN32
237         dllhandle = LoadLibrary (name);
238 # else
239         dllhandle = dlopen (name, RTLD_LAZY | RTLD_GLOBAL);
240 # endif
241 #endif
242         if(!dllhandle)
243                 return false;
244
245         *handle = dllhandle;
246         return true;
247 }
248
249 void Sys_FreeLibrary (dllhandle_t* handle)
250 {
251 #ifdef SUPPORTDLL
252         if (handle == NULL || *handle == NULL)
253                 return;
254
255 #ifdef WIN32
256         FreeLibrary (*handle);
257 #else
258         dlclose (*handle);
259 #endif
260
261         *handle = NULL;
262 #endif
263 }
264
265 void* Sys_GetProcAddress (dllhandle_t handle, const char* name)
266 {
267 #ifdef SUPPORTDLL
268 #ifdef WIN32
269         return (void *)GetProcAddress (handle, name);
270 #else
271         return (void *)dlsym (handle, name);
272 #endif
273 #else
274         return NULL;
275 #endif
276 }
277
278 #ifdef WIN32
279 # define HAVE_TIMEGETTIME 1
280 # define HAVE_QUERYPERFORMANCECOUNTER 1
281 # define HAVE_Sleep 1
282 #endif
283
284 #ifndef WIN32
285 #if defined(CLOCK_MONOTONIC) || defined(CLOCK_HIRES)
286 # define HAVE_CLOCKGETTIME 1
287 #endif
288 // FIXME improve this check, manpage hints to DST_NONE
289 # define HAVE_GETTIMEOFDAY 1
290 #endif
291
292 #ifdef FD_SET
293 # define HAVE_SELECT 1
294 #endif
295
296 #ifndef WIN32
297 // FIXME improve this check
298 # define HAVE_USLEEP 1
299 #endif
300
301 // these are referenced elsewhere
302 cvar_t sys_usenoclockbutbenchmark = {CF_SHARED, "sys_usenoclockbutbenchmark", "0", "don't use ANY real timing, and simulate a clock (for benchmarking); the game then runs as fast as possible. Run a QC mod with bots that does some stuff, then does a quit at the end, to benchmark a server. NEVER do this on a public server."};
303 cvar_t sys_libdir = {CF_READONLY | CF_SHARED, "sys_libdir", "", "Default engine library directory"};
304
305 // these are not
306 static cvar_t sys_debugsleep = {CF_SHARED, "sys_debugsleep", "0", "write requested and attained sleep times to standard output, to be used with gnuplot"};
307 static cvar_t sys_usesdlgetticks = {CF_SHARED, "sys_usesdlgetticks", "0", "use SDL_GetTicks() timer (less accurate, for debugging)"};
308 static cvar_t sys_usesdldelay = {CF_SHARED, "sys_usesdldelay", "0", "use SDL_Delay() (less accurate, for debugging)"};
309 #if HAVE_QUERYPERFORMANCECOUNTER
310 static cvar_t sys_usequeryperformancecounter = {CF_SHARED | CF_ARCHIVE, "sys_usequeryperformancecounter", "0", "use windows QueryPerformanceCounter timer (which has issues on multicore/multiprocessor machines and processors which are designed to conserve power) for timing rather than timeGetTime function (which has issues on some motherboards)"};
311 #endif
312 #if HAVE_CLOCKGETTIME
313 static cvar_t sys_useclockgettime = {CF_SHARED | CF_ARCHIVE, "sys_useclockgettime", "1", "use POSIX clock_gettime function (not adjusted by NTP on some older Linux kernels) for timing rather than gettimeofday (which has issues if the system time is stepped by ntpdate, or apparently on some Xen installations)"};
314 #endif
315
316 static double benchmark_time; // actually always contains an integer amount of milliseconds, will eventually "overflow"
317
318 /*
319 ================
320 Sys_CheckParm
321
322 Returns the position (1 to argc-1) in the program's argument list
323 where the given parameter apears, or 0 if not present
324 ================
325 */
326 int Sys_CheckParm (const char *parm)
327 {
328         int i;
329
330         for (i=1 ; i<sys.argc ; i++)
331         {
332                 if (!sys.argv[i])
333                         continue;               // NEXTSTEP sometimes clears appkit vars.
334                 if (!strcmp (parm,sys.argv[i]))
335                         return i;
336         }
337
338         return 0;
339 }
340
341 void Sys_Init_Commands (void)
342 {
343         Cvar_RegisterVariable(&sys_debugsleep);
344         Cvar_RegisterVariable(&sys_usenoclockbutbenchmark);
345         Cvar_RegisterVariable(&sys_libdir);
346 #if HAVE_TIMEGETTIME || HAVE_QUERYPERFORMANCECOUNTER || HAVE_CLOCKGETTIME || HAVE_GETTIMEOFDAY
347         if(sys_supportsdlgetticks)
348         {
349                 Cvar_RegisterVariable(&sys_usesdlgetticks);
350                 Cvar_RegisterVariable(&sys_usesdldelay);
351         }
352 #endif
353 #if HAVE_QUERYPERFORMANCECOUNTER
354         Cvar_RegisterVariable(&sys_usequeryperformancecounter);
355 #endif
356 #if HAVE_CLOCKGETTIME
357         Cvar_RegisterVariable(&sys_useclockgettime);
358 #endif
359 }
360
361 double Sys_DirtyTime(void)
362 {
363         // first all the OPTIONAL timers
364
365         // benchmark timer (fake clock)
366         if(sys_usenoclockbutbenchmark.integer)
367         {
368                 double old_benchmark_time = benchmark_time;
369                 benchmark_time += 1;
370                 if(benchmark_time == old_benchmark_time)
371                         Sys_Error("sys_usenoclockbutbenchmark cannot run any longer, sorry");
372                 return benchmark_time * 0.000001;
373         }
374 #if HAVE_QUERYPERFORMANCECOUNTER
375         if (sys_usequeryperformancecounter.integer)
376         {
377                 // LadyHavoc: note to people modifying this code, DWORD is specifically defined as an unsigned 32bit number, therefore the 65536.0 * 65536.0 is fine.
378                 // QueryPerformanceCounter
379                 // platform:
380                 // Windows 95/98/ME/NT/2000/XP
381                 // features:
382                 // very accurate (CPU cycles)
383                 // known issues:
384                 // does not necessarily match realtime too well (tends to get faster and faster in win98)
385                 // wraps around occasionally on some platforms (depends on CPU speed and probably other unknown factors)
386                 double timescale;
387                 LARGE_INTEGER PerformanceFreq;
388                 LARGE_INTEGER PerformanceCount;
389
390                 if (QueryPerformanceFrequency (&PerformanceFreq))
391                 {
392                         QueryPerformanceCounter (&PerformanceCount);
393         
394                         timescale = 1.0 / ((double) PerformanceFreq.LowPart + (double) PerformanceFreq.HighPart * 65536.0 * 65536.0);
395                         return ((double) PerformanceCount.LowPart + (double) PerformanceCount.HighPart * 65536.0 * 65536.0) * timescale;
396                 }
397                 else
398                 {
399                         Con_Printf("No hardware timer available\n");
400                         // fall back to other clock sources
401                         Cvar_SetValueQuick(&sys_usequeryperformancecounter, false);
402                 }
403         }
404 #endif
405
406 #if HAVE_CLOCKGETTIME
407         if (sys_useclockgettime.integer)
408         {
409                 struct timespec ts;
410 #  ifdef CLOCK_MONOTONIC
411                 // linux
412                 clock_gettime(CLOCK_MONOTONIC, &ts);
413 #  else
414                 // sunos
415                 clock_gettime(CLOCK_HIGHRES, &ts);
416 #  endif
417                 return (double) ts.tv_sec + ts.tv_nsec / 1000000000.0;
418         }
419 #endif
420
421         // now all the FALLBACK timers
422         if(sys_supportsdlgetticks && sys_usesdlgetticks.integer)
423                 return (double) Sys_SDL_GetTicks() / 1000.0;
424 #if HAVE_GETTIMEOFDAY
425         {
426                 struct timeval tp;
427                 gettimeofday(&tp, NULL);
428                 return (double) tp.tv_sec + tp.tv_usec / 1000000.0;
429         }
430 #elif HAVE_TIMEGETTIME
431         {
432                 static int firsttimegettime = true;
433                 // timeGetTime
434                 // platform:
435                 // Windows 95/98/ME/NT/2000/XP
436                 // features:
437                 // reasonable accuracy (millisecond)
438                 // issues:
439                 // wraps around every 47 days or so (but this is non-fatal to us, odd times are rejected, only causes a one frame stutter)
440
441                 // make sure the timer is high precision, otherwise different versions of windows have varying accuracy
442                 if (firsttimegettime)
443                 {
444                         timeBeginPeriod(1);
445                         firsttimegettime = false;
446                 }
447
448                 return (double) timeGetTime() / 1000.0;
449         }
450 #else
451         // fallback for using the SDL timer if no other timer is available
452         // this calls Sys_Error() if not linking against SDL
453         return (double) Sys_SDL_GetTicks() / 1000.0;
454 #endif
455 }
456
457 extern cvar_t host_maxwait;
458 double Sys_Sleep(double time)
459 {
460         double dt;
461         uint32_t microseconds;
462
463         // convert to microseconds
464         time *= 1000000.0;
465
466         if(host_maxwait.value <= 0)
467                 time = min(time, 1000000.0);
468         else
469                 time = min(time, host_maxwait.value * 1000.0);
470
471         if (time < 1 || host.restless)
472                 return 0; // not sleeping this frame
473
474         microseconds = time; // post-validation to prevent overflow
475
476         if(sys_usenoclockbutbenchmark.integer)
477         {
478                 double old_benchmark_time = benchmark_time;
479                 benchmark_time += microseconds;
480                 if(benchmark_time == old_benchmark_time)
481                         Sys_Error("sys_usenoclockbutbenchmark cannot run any longer, sorry");
482                 return 0;
483         }
484
485         if(sys_debugsleep.integer)
486                 Sys_Printf("sys_debugsleep: requesting %u ", microseconds);
487         dt = Sys_DirtyTime();
488
489         // less important on newer libcurl so no need to disturb dedicated servers
490         if (cls.state != ca_dedicated && Curl_Select(microseconds))
491         {
492                 // a transfer is ready or we finished sleeping
493         }
494         else if(sys_supportsdlgetticks && sys_usesdldelay.integer)
495                 Sys_SDL_Delay(microseconds / 1000);
496 #if HAVE_SELECT
497         else
498         {
499                 struct timeval tv;
500                 lhnetsocket_t *s;
501                 fd_set fdreadset;
502                 int lastfd = -1;
503
504                 FD_ZERO(&fdreadset);
505                 if (cls.state == ca_dedicated && sv_checkforpacketsduringsleep.integer)
506                 {
507                         List_For_Each_Entry(s, &lhnet_socketlist.list, lhnetsocket_t, list)
508                         {
509                                 if (s->address.addresstype == LHNETADDRESSTYPE_INET4 || s->address.addresstype == LHNETADDRESSTYPE_INET6)
510                                 {
511                                         if (lastfd < s->inetsocket)
512                                                 lastfd = s->inetsocket;
513         #if defined(WIN32) && !defined(_MSC_VER)
514                                         FD_SET((int)s->inetsocket, &fdreadset);
515         #else
516                                         FD_SET((unsigned int)s->inetsocket, &fdreadset);
517         #endif
518                                 }
519                         }
520                 }
521                 tv.tv_sec = microseconds / 1000000;
522                 tv.tv_usec = microseconds % 1000000;
523                 // on Win32, select() cannot be used with all three FD list args being NULL according to MSDN
524                 // (so much for POSIX...)
525                 // bones_was_here: but a zeroed fd_set seems to be tolerated (tested on Win 7)
526                 select(lastfd + 1, &fdreadset, NULL, NULL, &tv);
527         }
528 #elif HAVE_USLEEP
529         else
530                 usleep(microseconds);
531 #elif HAVE_Sleep
532         else
533                 Sleep(microseconds / 1000);
534 #else
535         else
536                 Sys_SDL_Delay(microseconds / 1000);
537 #endif
538
539         dt = Sys_DirtyTime() - dt;
540         if(sys_debugsleep.integer)
541                 Sys_Printf(" got %u oversleep %d\n", (unsigned int)(dt * 1000000), (unsigned int)(dt * 1000000) - microseconds);
542         return (dt < 0 || dt >= 1800) ? 0 : dt;
543 }
544
545 void Sys_Printf(const char *fmt, ...)
546 {
547         va_list argptr;
548         char msg[MAX_INPUTLINE];
549
550         va_start(argptr,fmt);
551         dpvsnprintf(msg,sizeof(msg),fmt,argptr);
552         va_end(argptr);
553
554         Sys_Print(msg);
555 }
556
557 #ifndef WIN32
558 static const char *Sys_FindInPATH(const char *name, char namesep, const char *PATH, char pathsep, char *buf, size_t bufsize)
559 {
560         const char *p = PATH;
561         const char *q;
562         if(p && name)
563         {
564                 while((q = strchr(p, ':')))
565                 {
566                         dpsnprintf(buf, bufsize, "%.*s%c%s", (int)(q-p), p, namesep, name);
567                         if(FS_SysFileExists(buf))
568                                 return buf;
569                         p = q + 1;
570                 }
571                 if(!q) // none found - try the last item
572                 {
573                         dpsnprintf(buf, bufsize, "%s%c%s", p, namesep, name);
574                         if(FS_SysFileExists(buf))
575                                 return buf;
576                 }
577         }
578         return name;
579 }
580 #endif
581
582 static const char *Sys_FindExecutableName(void)
583 {
584 #if defined(WIN32)
585         return sys.argv[0];
586 #else
587         static char exenamebuf[MAX_OSPATH+1];
588         ssize_t n = -1;
589 #if defined(__FreeBSD__)
590         int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
591         size_t exenamebuflen = sizeof(exenamebuf)-1;
592         if (sysctl(mib, 4, exenamebuf, &exenamebuflen, NULL, 0) == 0)
593         {
594                 n = exenamebuflen;
595         }
596 #elif defined(__linux__)
597         n = readlink("/proc/self/exe", exenamebuf, sizeof(exenamebuf)-1);
598 #endif
599         if(n > 0 && (size_t)(n) < sizeof(exenamebuf))
600         {
601                 exenamebuf[n] = 0;
602                 return exenamebuf;
603         }
604         if(strchr(sys.argv[0], '/'))
605                 return sys.argv[0]; // possibly a relative path
606         else
607                 return Sys_FindInPATH(sys.argv[0], '/', getenv("PATH"), ':', exenamebuf, sizeof(exenamebuf));
608 #endif
609 }
610
611 void Sys_ProvideSelfFD(void)
612 {
613         if(sys.selffd != -1)
614                 return;
615         sys.selffd = FS_SysOpenFD(Sys_FindExecutableName(), "rb", false);
616 }
617
618 // for x86 cpus only...  (x64 has SSE2_PRESENT)
619 #if defined(SSE_POSSIBLE) && !defined(SSE2_PRESENT)
620 // code from SDL, shortened as we can expect CPUID to work
621 static int CPUID_Features(void)
622 {
623         int features = 0;
624 # if (defined(__GNUC__) || defined(__clang__) || defined(__TINYC__)) && defined(__i386__)
625         __asm__ (
626 "        movl    %%ebx,%%edi\n"
627 "        xorl    %%eax,%%eax                                           \n"
628 "        incl    %%eax                                                 \n"
629 "        cpuid                       # Get family/model/stepping/features\n"
630 "        movl    %%edx,%0                                              \n"
631 "        movl    %%edi,%%ebx\n"
632         : "=m" (features)
633         :
634         : "%eax", "%ecx", "%edx", "%edi"
635         );
636 # elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
637         __asm {
638         xor     eax, eax
639         inc     eax
640         cpuid                       ; Get family/model/stepping/features
641         mov     features, edx
642         }
643 # else
644 #  error SSE_POSSIBLE set but no CPUID implementation
645 # endif
646         return features;
647 }
648 #endif
649
650 #ifdef SSE_POSSIBLE
651 qbool Sys_HaveSSE(void)
652 {
653         // COMMANDLINEOPTION: SSE: -nosse disables SSE support and detection
654         if(Sys_CheckParm("-nosse"))
655                 return false;
656 #ifdef SSE_PRESENT
657         return true;
658 #else
659         // COMMANDLINEOPTION: SSE: -forcesse enables SSE support and disables detection
660         if(Sys_CheckParm("-forcesse") || Sys_CheckParm("-forcesse2"))
661                 return true;
662         if(CPUID_Features() & (1 << 25))
663                 return true;
664         return false;
665 #endif
666 }
667
668 qbool Sys_HaveSSE2(void)
669 {
670         // COMMANDLINEOPTION: SSE2: -nosse2 disables SSE2 support and detection
671         if(Sys_CheckParm("-nosse") || Sys_CheckParm("-nosse2"))
672                 return false;
673 #ifdef SSE2_PRESENT
674         return true;
675 #else
676         // COMMANDLINEOPTION: SSE2: -forcesse2 enables SSE2 support and disables detection
677         if(Sys_CheckParm("-forcesse2"))
678                 return true;
679         if((CPUID_Features() & (3 << 25)) == (3 << 25)) // SSE is 1<<25, SSE2 is 1<<26
680                 return true;
681         return false;
682 #endif
683 }
684 #endif
685
686 /// called to set process priority for dedicated servers
687 #if defined(__linux__)
688 #include <sys/resource.h>
689 #include <errno.h>
690
691 void Sys_InitProcessNice (void)
692 {
693         struct rlimit lim;
694         sys.nicepossible = false;
695         if(Sys_CheckParm("-nonice"))
696                 return;
697         errno = 0;
698         sys.nicelevel = getpriority(PRIO_PROCESS, 0);
699         if(errno)
700         {
701                 Con_Printf("Kernel does not support reading process priority - cannot use niceness\n");
702                 return;
703         }
704         if(getrlimit(RLIMIT_NICE, &lim))
705         {
706                 Con_Printf("Kernel does not support lowering nice level again - cannot use niceness\n");
707                 return;
708         }
709         if(lim.rlim_cur != RLIM_INFINITY && sys.nicelevel < (int) (20 - lim.rlim_cur))
710         {
711                 Con_Printf("Current nice level is below the soft limit - cannot use niceness\n");
712                 return;
713         }
714         sys.nicepossible = true;
715         sys.isnice = false;
716 }
717 void Sys_MakeProcessNice (void)
718 {
719         if(!sys.nicepossible)
720                 return;
721         if(sys.isnice)
722                 return;
723         Con_DPrintf("Process is becoming 'nice'...\n");
724         if(setpriority(PRIO_PROCESS, 0, 19))
725                 Con_Printf(CON_ERROR "Failed to raise nice level to %d\n", 19);
726         sys.isnice = true;
727 }
728 void Sys_MakeProcessMean (void)
729 {
730         if(!sys.nicepossible)
731                 return;
732         if(!sys.isnice)
733                 return;
734         Con_DPrintf("Process is becoming 'mean'...\n");
735         if(setpriority(PRIO_PROCESS, 0, sys.nicelevel))
736                 Con_Printf(CON_ERROR "Failed to lower nice level to %d\n", sys.nicelevel);
737         sys.isnice = false;
738 }
739 #else
740 void Sys_InitProcessNice (void)
741 {
742 }
743 void Sys_MakeProcessNice (void)
744 {
745 }
746 void Sys_MakeProcessMean (void)
747 {
748 }
749 #endif
750
751 int main (int argc, char **argv)
752 {
753         signal(SIGFPE, SIG_IGN);
754
755         sys.argc = argc;
756         sys.argv = (const char **)argv;
757
758         // COMMANDLINEOPTION: -noterminal disables console output on stdout
759         if(Sys_CheckParm("-noterminal"))
760                 sys.outfd = -1;
761         // COMMANDLINEOPTION: -stderr moves console output to stderr
762         else if(Sys_CheckParm("-stderr"))
763                 sys.outfd = 2;
764         else
765                 sys.outfd = 1;
766
767         sys.selffd = -1;
768         Sys_ProvideSelfFD(); // may call Con_Printf() so must be after sys.outfd is set
769
770 #ifndef WIN32
771         fcntl(0, F_SETFL, fcntl (0, F_GETFL, 0) | O_NONBLOCK);
772 #endif
773
774 #ifdef __ANDROID__
775         Sys_AllowProfiling(true);
776 #endif
777
778         Host_Main();
779
780         Sys_Quit(0);
781
782         return 0;
783 }