]> git.xonotic.org Git - xonotic/darkplaces.git/blob - sys_shared.c
deduplicate Sys_Error()
[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
619 void Sys_Error (const char *error, ...)
620 {
621         va_list argptr;
622         char string[MAX_INPUTLINE];
623
624 // change stdin to non blocking
625 #ifndef WIN32
626         fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~O_NONBLOCK);
627 #endif
628
629         va_start (argptr,error);
630         dpvsnprintf (string, sizeof (string), error, argptr);
631         va_end (argptr);
632
633         Con_Printf(CON_ERROR "Engine Error: %s\n", string);
634
635         // don't want a dead window left blocking the OS UI or the crash dialog
636         Host_Shutdown();
637
638         Sys_SDL_Dialog("Engine Error", string);
639
640         exit (1);
641 }
642
643 #ifndef WIN32
644 static const char *Sys_FindInPATH(const char *name, char namesep, const char *PATH, char pathsep, char *buf, size_t bufsize)
645 {
646         const char *p = PATH;
647         const char *q;
648         if(p && name)
649         {
650                 while((q = strchr(p, ':')))
651                 {
652                         dpsnprintf(buf, bufsize, "%.*s%c%s", (int)(q-p), p, namesep, name);
653                         if(FS_SysFileExists(buf))
654                                 return buf;
655                         p = q + 1;
656                 }
657                 if(!q) // none found - try the last item
658                 {
659                         dpsnprintf(buf, bufsize, "%s%c%s", p, namesep, name);
660                         if(FS_SysFileExists(buf))
661                                 return buf;
662                 }
663         }
664         return name;
665 }
666 #endif
667
668 static const char *Sys_FindExecutableName(void)
669 {
670 #if defined(WIN32)
671         return sys.argv[0];
672 #else
673         static char exenamebuf[MAX_OSPATH+1];
674         ssize_t n = -1;
675 #if defined(__FreeBSD__)
676         int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
677         size_t exenamebuflen = sizeof(exenamebuf)-1;
678         if (sysctl(mib, 4, exenamebuf, &exenamebuflen, NULL, 0) == 0)
679         {
680                 n = exenamebuflen;
681         }
682 #elif defined(__linux__)
683         n = readlink("/proc/self/exe", exenamebuf, sizeof(exenamebuf)-1);
684 #endif
685         if(n > 0 && (size_t)(n) < sizeof(exenamebuf))
686         {
687                 exenamebuf[n] = 0;
688                 return exenamebuf;
689         }
690         if(strchr(sys.argv[0], '/'))
691                 return sys.argv[0]; // possibly a relative path
692         else
693                 return Sys_FindInPATH(sys.argv[0], '/', getenv("PATH"), ':', exenamebuf, sizeof(exenamebuf));
694 #endif
695 }
696
697 void Sys_ProvideSelfFD(void)
698 {
699         if(sys.selffd != -1)
700                 return;
701         sys.selffd = FS_SysOpenFD(Sys_FindExecutableName(), "rb", false);
702 }
703
704 // for x86 cpus only...  (x64 has SSE2_PRESENT)
705 #if defined(SSE_POSSIBLE) && !defined(SSE2_PRESENT)
706 // code from SDL, shortened as we can expect CPUID to work
707 static int CPUID_Features(void)
708 {
709         int features = 0;
710 # if (defined(__GNUC__) || defined(__clang__) || defined(__TINYC__)) && defined(__i386__)
711         __asm__ (
712 "        movl    %%ebx,%%edi\n"
713 "        xorl    %%eax,%%eax                                           \n"
714 "        incl    %%eax                                                 \n"
715 "        cpuid                       # Get family/model/stepping/features\n"
716 "        movl    %%edx,%0                                              \n"
717 "        movl    %%edi,%%ebx\n"
718         : "=m" (features)
719         :
720         : "%eax", "%ecx", "%edx", "%edi"
721         );
722 # elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
723         __asm {
724         xor     eax, eax
725         inc     eax
726         cpuid                       ; Get family/model/stepping/features
727         mov     features, edx
728         }
729 # else
730 #  error SSE_POSSIBLE set but no CPUID implementation
731 # endif
732         return features;
733 }
734 #endif
735
736 #ifdef SSE_POSSIBLE
737 qbool Sys_HaveSSE(void)
738 {
739         // COMMANDLINEOPTION: SSE: -nosse disables SSE support and detection
740         if(Sys_CheckParm("-nosse"))
741                 return false;
742 #ifdef SSE_PRESENT
743         return true;
744 #else
745         // COMMANDLINEOPTION: SSE: -forcesse enables SSE support and disables detection
746         if(Sys_CheckParm("-forcesse") || Sys_CheckParm("-forcesse2"))
747                 return true;
748         if(CPUID_Features() & (1 << 25))
749                 return true;
750         return false;
751 #endif
752 }
753
754 qbool Sys_HaveSSE2(void)
755 {
756         // COMMANDLINEOPTION: SSE2: -nosse2 disables SSE2 support and detection
757         if(Sys_CheckParm("-nosse") || Sys_CheckParm("-nosse2"))
758                 return false;
759 #ifdef SSE2_PRESENT
760         return true;
761 #else
762         // COMMANDLINEOPTION: SSE2: -forcesse2 enables SSE2 support and disables detection
763         if(Sys_CheckParm("-forcesse2"))
764                 return true;
765         if((CPUID_Features() & (3 << 25)) == (3 << 25)) // SSE is 1<<25, SSE2 is 1<<26
766                 return true;
767         return false;
768 #endif
769 }
770 #endif
771
772 /// called to set process priority for dedicated servers
773 #if defined(__linux__)
774 #include <sys/resource.h>
775 #include <errno.h>
776
777 void Sys_InitProcessNice (void)
778 {
779         struct rlimit lim;
780         sys.nicepossible = false;
781         if(Sys_CheckParm("-nonice"))
782                 return;
783         errno = 0;
784         sys.nicelevel = getpriority(PRIO_PROCESS, 0);
785         if(errno)
786         {
787                 Con_Printf("Kernel does not support reading process priority - cannot use niceness\n");
788                 return;
789         }
790         if(getrlimit(RLIMIT_NICE, &lim))
791         {
792                 Con_Printf("Kernel does not support lowering nice level again - cannot use niceness\n");
793                 return;
794         }
795         if(lim.rlim_cur != RLIM_INFINITY && sys.nicelevel < (int) (20 - lim.rlim_cur))
796         {
797                 Con_Printf("Current nice level is below the soft limit - cannot use niceness\n");
798                 return;
799         }
800         sys.nicepossible = true;
801         sys.isnice = false;
802 }
803 void Sys_MakeProcessNice (void)
804 {
805         if(!sys.nicepossible)
806                 return;
807         if(sys.isnice)
808                 return;
809         Con_DPrintf("Process is becoming 'nice'...\n");
810         if(setpriority(PRIO_PROCESS, 0, 19))
811                 Con_Printf(CON_ERROR "Failed to raise nice level to %d\n", 19);
812         sys.isnice = true;
813 }
814 void Sys_MakeProcessMean (void)
815 {
816         if(!sys.nicepossible)
817                 return;
818         if(!sys.isnice)
819                 return;
820         Con_DPrintf("Process is becoming 'mean'...\n");
821         if(setpriority(PRIO_PROCESS, 0, sys.nicelevel))
822                 Con_Printf(CON_ERROR "Failed to lower nice level to %d\n", sys.nicelevel);
823         sys.isnice = false;
824 }
825 #else
826 void Sys_InitProcessNice (void)
827 {
828 }
829 void Sys_MakeProcessNice (void)
830 {
831 }
832 void Sys_MakeProcessMean (void)
833 {
834 }
835 #endif
836
837 int main (int argc, char **argv)
838 {
839         signal(SIGFPE, SIG_IGN);
840
841         sys.argc = argc;
842         sys.argv = (const char **)argv;
843
844         // COMMANDLINEOPTION: -noterminal disables console output on stdout
845         if(Sys_CheckParm("-noterminal"))
846                 sys.outfd = -1;
847         // COMMANDLINEOPTION: -stderr moves console output to stderr
848         else if(Sys_CheckParm("-stderr"))
849                 sys.outfd = 2;
850         else
851                 sys.outfd = 1;
852
853         sys.selffd = -1;
854         Sys_ProvideSelfFD(); // may call Con_Printf() so must be after sys.outfd is set
855
856 #ifndef WIN32
857         fcntl(0, F_SETFL, fcntl (0, F_GETFL, 0) | O_NONBLOCK);
858 #endif
859
860 #ifdef __ANDROID__
861         Sys_AllowProfiling(true);
862 #endif
863
864         Host_Main();
865
866         Sys_Quit(0);
867
868         return 0;
869 }