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