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