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