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