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