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