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