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