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