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