]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - sys_shared.c
APPEND forced args, not PREPEND, as earlier arguments win in most DP options.
[xonotic/darkplaces.git] / sys_shared.c
1 #include "quakedef.h"
2
3 #define SUPPORTDLL
4
5 #ifdef WIN32
6 # include <windows.h>
7 # include <mmsystem.h> // timeGetTime
8 # include <time.h> // localtime
9 #else
10 # include <unistd.h>
11 # include <fcntl.h>
12 # include <sys/time.h>
13 # include <time.h>
14 # ifdef SUPPORTDLL
15 #  include <dlfcn.h>
16 # endif
17 #endif
18
19 static char sys_timestring[128];
20 char *Sys_TimeString(const char *timeformat)
21 {
22         time_t mytime = time(NULL);
23 #if _MSC_VER >= 1400
24         struct tm mytm;
25         localtime_s(&mytm, &mytime);
26         strftime(sys_timestring, sizeof(sys_timestring), timeformat, &mytm);
27 #else
28         strftime(sys_timestring, sizeof(sys_timestring), timeformat, localtime(&mytime));
29 #endif
30         return sys_timestring;
31 }
32
33
34 extern qboolean host_shuttingdown;
35 void Sys_Quit (int returnvalue)
36 {
37         if (COM_CheckParm("-profilegameonly"))
38                 Sys_AllowProfiling(false);
39         host_shuttingdown = true;
40         Host_Shutdown();
41         exit(returnvalue);
42 }
43
44 #if defined(__linux__) || defined(__FreeBSD__)
45 #ifdef __cplusplus
46 extern "C"
47 #endif
48 int moncontrol(int);
49 #endif
50
51 void Sys_AllowProfiling(qboolean enable)
52 {
53 #if defined(__linux__) || defined(__FreeBSD__)
54         moncontrol(enable);
55 #endif
56 }
57
58
59 /*
60 ===============================================================================
61
62 DLL MANAGEMENT
63
64 ===============================================================================
65 */
66
67 qboolean Sys_LoadLibrary (const char** dllnames, dllhandle_t* handle, const dllfunction_t *fcts)
68 {
69 #ifdef SUPPORTDLL
70         const dllfunction_t *func;
71         dllhandle_t dllhandle = 0;
72         unsigned int i;
73
74         if (handle == NULL)
75                 return false;
76
77 #ifndef WIN32
78 #ifdef PREFER_PRELOAD
79         dllhandle = dlopen(NULL, RTLD_LAZY | RTLD_GLOBAL);
80         if(dllhandle)
81         {
82                 for (func = fcts; func && func->name != NULL; func++)
83                         if (!(*func->funcvariable = (void *) Sys_GetProcAddress (dllhandle, func->name)))
84                         {
85                                 dlclose(dllhandle);
86                                 goto notfound;
87                         }
88                 Con_DPrintf ("All of %s's functions were already linked in! Not loading dynamically...\n", dllnames[0]);
89                 *handle = dllhandle;
90                 return true;
91         }
92 notfound:
93 #endif
94 #endif
95
96         // Initializations
97         for (func = fcts; func && func->name != NULL; func++)
98                 *func->funcvariable = NULL;
99
100         // Try every possible name
101         Con_DPrintf ("Trying to load library...");
102         for (i = 0; dllnames[i] != NULL; i++)
103         {
104                 Con_DPrintf (" \"%s\"", dllnames[i]);
105 #ifdef WIN32
106                 dllhandle = LoadLibrary (dllnames[i]);
107 #else
108                 dllhandle = dlopen (dllnames[i], RTLD_LAZY | RTLD_GLOBAL);
109 #endif
110                 if (dllhandle)
111                         break;
112         }
113
114         // see if the names can be loaded relative to the executable path
115         // (this is for Mac OSX which does not check next to the executable)
116         if (!dllhandle && strrchr(com_argv[0], '/'))
117         {
118                 char path[MAX_OSPATH];
119                 strlcpy(path, com_argv[0], sizeof(path));
120                 strrchr(path, '/')[1] = 0;
121                 for (i = 0; dllnames[i] != NULL; i++)
122                 {
123                         char temp[MAX_OSPATH];
124                         strlcpy(temp, path, sizeof(temp));
125                         strlcat(temp, dllnames[i], sizeof(temp));
126                         Con_DPrintf (" \"%s\"", temp);
127 #ifdef WIN32
128                         dllhandle = LoadLibrary (temp);
129 #else
130                         dllhandle = dlopen (temp, RTLD_LAZY | RTLD_GLOBAL);
131 #endif
132                         if (dllhandle)
133                                 break;
134                 }
135         }
136
137         // No DLL found
138         if (! dllhandle)
139         {
140                 Con_DPrintf(" - failed.\n");
141                 return false;
142         }
143
144         Con_DPrintf(" - loaded.\n");
145
146         // Get the function adresses
147         for (func = fcts; func && func->name != NULL; func++)
148                 if (!(*func->funcvariable = (void *) Sys_GetProcAddress (dllhandle, func->name)))
149                 {
150                         Con_DPrintf ("Missing function \"%s\" - broken library!\n", func->name);
151                         Sys_UnloadLibrary (&dllhandle);
152                         return false;
153                 }
154
155         *handle = dllhandle;
156         return true;
157 #else
158         return false;
159 #endif
160 }
161
162 void Sys_UnloadLibrary (dllhandle_t* handle)
163 {
164 #ifdef SUPPORTDLL
165         if (handle == NULL || *handle == NULL)
166                 return;
167
168 #ifdef WIN32
169         FreeLibrary (*handle);
170 #else
171         dlclose (*handle);
172 #endif
173
174         *handle = NULL;
175 #endif
176 }
177
178 void* Sys_GetProcAddress (dllhandle_t handle, const char* name)
179 {
180 #ifdef SUPPORTDLL
181 #ifdef WIN32
182         return (void *)GetProcAddress (handle, name);
183 #else
184         return (void *)dlsym (handle, name);
185 #endif
186 #else
187         return NULL;
188 #endif
189 }
190
191 #ifdef WIN32
192 # define HAVE_TIMEGETTIME 1
193 # define HAVE_QUERYPERFORMANCECOUNTER 1
194 # define HAVE_Sleep 1
195 #endif
196
197 #if defined(CLOCK_MONOTONIC) || defined(CLOCK_HIRES)
198 # define HAVE_CLOCKGETTIME 1
199 #endif
200
201 #ifndef WIN32
202 // FIXME improve this check, manpage hints to DST_NONE
203 # define HAVE_GETTIMEOFDAY 1
204 #endif
205
206 #ifdef FD_SET
207 # define HAVE_SELECT 1
208 #endif
209
210 #ifndef WIN32
211 // FIXME improve this check
212 # define HAVE_USLEEP 1
213 #endif
214
215 // this one is referenced elsewhere
216 cvar_t sys_usenoclockbutbenchmark = {CVAR_SAVE, "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."};
217
218 // these are not
219 static cvar_t sys_debugsleep = {0, "sys_debugsleep", "0", "write requested and attained sleep times to standard output, to be used with gnuplot"};
220 static cvar_t sys_usesdlgetticks = {CVAR_SAVE, "sys_usesdlgetticks", "0", "use SDL_GetTicks() timer (less accurate, for debugging)"};
221 static cvar_t sys_usesdldelay = {CVAR_SAVE, "sys_usesdldelay", "0", "use SDL_Delay() (less accurate, for debugging)"};
222 #if HAVE_QUERYPERFORMANCECOUNTER
223 static cvar_t sys_usequeryperformancecounter = {CVAR_SAVE, "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)"};
224 #endif
225 #if HAVE_CLOCKGETTIME
226 static cvar_t sys_useclockgettime = {CVAR_SAVE, "sys_useclockgettime", "0", "use POSIX clock_gettime function (which has issues if the system clock speed is far off, as it can't get fixed by NTP) for timing rather than gettimeofday (which has issues if the system time is stepped by ntpdate, or apparently on some Xen installations)"};
227 #endif
228
229 static unsigned long benchmark_time;
230
231 void Sys_Init_Commands (void)
232 {
233         Cvar_RegisterVariable(&sys_debugsleep);
234         Cvar_RegisterVariable(&sys_usenoclockbutbenchmark);
235 #if HAVE_TIMEGETTIME || HAVE_QUERYPERFORMANCECOUNTER || HAVE_CLOCKGETTIME || HAVE_GETTIMEOFDAY
236         if(sys_supportsdlgetticks)
237         {
238                 Cvar_RegisterVariable(&sys_usesdlgetticks);
239                 Cvar_RegisterVariable(&sys_usesdldelay);
240         }
241 #endif
242 #if HAVE_QUERYPERFORMANCECOUNTER
243         Cvar_RegisterVariable(&sys_usequeryperformancecounter);
244 #endif
245 #if HAVE_CLOCKGETTIME
246         Cvar_RegisterVariable(&sys_useclockgettime);
247 #endif
248 }
249
250 double Sys_DoubleTime(void)
251 {
252         static int first = true;
253         static double oldtime = 0.0, curtime = 0.0;
254         double newtime;
255         if(sys_usenoclockbutbenchmark.integer)
256         {
257                 benchmark_time += 1;
258                 return ((double) benchmark_time) / 1e6;
259         }
260
261         // first all the OPTIONAL timers
262
263 #if HAVE_QUERYPERFORMANCECOUNTER
264         else if (sys_usequeryperformancecounter.integer)
265         {
266                 // LordHavoc: note to people modifying this code, DWORD is specifically defined as an unsigned 32bit number, therefore the 65536.0 * 65536.0 is fine.
267                 // QueryPerformanceCounter
268                 // platform:
269                 // Windows 95/98/ME/NT/2000/XP
270                 // features:
271                 // very accurate (CPU cycles)
272                 // known issues:
273                 // does not necessarily match realtime too well (tends to get faster and faster in win98)
274                 // wraps around occasionally on some platforms (depends on CPU speed and probably other unknown factors)
275                 double timescale;
276                 LARGE_INTEGER PerformanceFreq;
277                 LARGE_INTEGER PerformanceCount;
278
279                 if (!QueryPerformanceFrequency (&PerformanceFreq))
280                 {
281                         Con_Printf ("No hardware timer available\n");
282                         // fall back to timeGetTime
283                         Cvar_SetValueQuick(&sys_usequeryperformancecounter, false);
284                         return Sys_DoubleTime();
285                 }
286                 QueryPerformanceCounter (&PerformanceCount);
287
288                 #ifdef __BORLANDC__
289                 timescale = 1.0 / ((double) PerformanceFreq.u.LowPart + (double) PerformanceFreq.u.HighPart * 65536.0 * 65536.0);
290                 newtime = ((double) PerformanceCount.u.LowPart + (double) PerformanceCount.u.HighPart * 65536.0 * 65536.0) * timescale;
291                 #else
292                 timescale = 1.0 / ((double) PerformanceFreq.LowPart + (double) PerformanceFreq.HighPart * 65536.0 * 65536.0);
293                 newtime = ((double) PerformanceCount.LowPart + (double) PerformanceCount.HighPart * 65536.0 * 65536.0) * timescale;
294                 #endif
295         }
296 #endif
297
298 #if HAVE_CLOCKGETTIME
299         else if (sys_useclockgettime.integer)
300         {
301                 struct timespec ts;
302 #  ifdef CLOCK_MONOTONIC
303                 // linux
304                 clock_gettime(CLOCK_MONOTONIC, &ts);
305 #  else
306                 // sunos
307                 clock_gettime(CLOCK_HIGHRES, &ts);
308 #  endif
309                 newtime = (double) ts.tv_sec + ts.tv_nsec / 1000000000.0;
310         }
311 #endif
312
313         // now all the FALLBACK timers
314         else if(sys_supportsdlgetticks && sys_usesdlgetticks.integer)
315         {
316                 newtime = (double) Sys_SDL_GetTicks() / 1000.0;
317         }
318 #if HAVE_GETTIMEOFDAY
319         else
320         {
321                 struct timeval tp;
322                 gettimeofday(&tp, NULL);
323                 newtime = (double) tp.tv_sec + tp.tv_usec / 1000000.0;
324         }
325 #elif HAVE_TIMEGETTIME
326         else
327         {
328                 static int firsttimegettime = true;
329                 // timeGetTime
330                 // platform:
331                 // Windows 95/98/ME/NT/2000/XP
332                 // features:
333                 // reasonable accuracy (millisecond)
334                 // issues:
335                 // wraps around every 47 days or so (but this is non-fatal to us, odd times are rejected, only causes a one frame stutter)
336
337                 // make sure the timer is high precision, otherwise different versions of windows have varying accuracy
338                 if (firsttimegettime)
339                 {
340                         timeBeginPeriod (1);
341                         firsttimegettime = false;
342                 }
343
344                 newtime = (double) timeGetTime () / 1000.0;
345         }
346 #else
347         // fallback for using the SDL timer if no other timer is available
348         else
349         {
350                 newtime = (double) Sys_SDL_GetTicks() / 1000.0;
351                 // this calls Sys_Error() if not linking against SDL
352         }
353 #endif
354
355         if (first)
356         {
357                 first = false;
358                 oldtime = newtime;
359         }
360
361         if (newtime < oldtime)
362         {
363                 // warn if it's significant
364                 if (newtime - oldtime < -0.01)
365                         Con_Printf("Sys_DoubleTime: time stepped backwards (went from %f to %f, difference %f)\n", oldtime, newtime, newtime - oldtime);
366         }
367         else if (newtime > oldtime + 1800)
368         {
369                 Con_Printf("Sys_DoubleTime: time stepped forward (went from %f to %f, difference %f)\n", oldtime, newtime, newtime - oldtime);
370         }
371         else
372                 curtime += newtime - oldtime;
373         oldtime = newtime;
374
375         return curtime;
376 }
377
378 void Sys_Sleep(int microseconds)
379 {
380         double t = 0;
381         if(sys_usenoclockbutbenchmark.integer)
382         {
383                 benchmark_time += microseconds;
384                 return;
385         }
386         if(sys_debugsleep.integer)
387         {
388                 t = Sys_DoubleTime();
389         }
390         if(sys_supportsdlgetticks && sys_usesdldelay.integer)
391         {
392                 Sys_SDL_Delay(microseconds / 1000);
393         }
394 #if HAVE_SELECT
395         else
396         {
397                 struct timeval tv;
398                 tv.tv_sec = microseconds / 1000000;
399                 tv.tv_usec = microseconds % 1000000;
400                 select(0, NULL, NULL, NULL, &tv);
401         }
402 #elif HAVE_USLEEP
403         else
404         {
405                 usleep(microseconds);
406         }
407 #elif HAVE_Sleep
408         else
409         {
410                 Sleep(microseconds / 1000);
411         }
412 #else
413         else
414         {
415                 Sys_SDL_Delay(microseconds / 1000);
416         }
417 #endif
418         if(sys_debugsleep.integer)
419         {
420                 t = Sys_DoubleTime() - t;
421                 printf("%d %d # debugsleep\n", microseconds, (unsigned int)(t * 1000000));
422         }
423 }
424
425 const char *Sys_FindInPATH(const char *name, char namesep, const char *PATH, char pathsep, char *buf, size_t bufsize)
426 {
427         const char *p = PATH;
428         const char *q;
429         if(p && name)
430         {
431                 while((q = strchr(p, ':')))
432                 {
433                         dpsnprintf(buf, bufsize, "%.*s%c%s", (int)(q-p), p, namesep, name);
434                         if(FS_SysFileExists(buf))
435                                 return buf;
436                         p = q + 1;
437                 }
438                 if(!q) // none found - try the last item
439                 {
440                         dpsnprintf(buf, bufsize, "%s%c%s", p, namesep, name);
441                         if(FS_SysFileExists(buf))
442                                 return buf;
443                 }
444         }
445         return name;
446 }
447
448 const char *Sys_FindExecutableName(void)
449 {
450 #if defined(WIN32)
451         return com_argv[0];
452 #else
453         static char exenamebuf[MAX_OSPATH+1];
454         ssize_t n = -1;
455 #if defined(__FreeBSD__)
456         n = readlink("/proc/curproc/file", exenamebuf, sizeof(exenamebuf)-1);
457 #elif defined(__linux__)
458         n = readlink("/proc/self/exe", exenamebuf, sizeof(exenamebuf)-1);
459 #endif
460         if(n > 0 && (size_t)(n) < sizeof(exenamebuf))
461         {
462                 exenamebuf[n] = 0;
463                 return exenamebuf;
464         }
465         if(strchr(com_argv[0], '/'))
466                 return com_argv[0]; // possibly a relative path
467         else
468                 return Sys_FindInPATH(com_argv[0], '/', getenv("PATH"), ':', exenamebuf, sizeof(exenamebuf));
469 #endif
470 }
471
472 void Sys_ProvideSelfFD(void)
473 {
474         if(com_selffd != -1)
475                 return;
476         com_selffd = FS_SysOpenFD(Sys_FindExecutableName(), "rb", false);
477 }