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