Store the data pointer and free it on Font_UnloadFont
[divverent/darkplaces.git] / sys_shared.c
1 #include "quakedef.h"
2
3 #define SUPPORTDLL
4
5 #ifdef WIN32
6 # ifdef _WIN64
7 #  ifndef _WIN32_WINNT
8 #   define _WIN32_WINNT 0x0502
9 #  endif
10    // for SetDllDirectory
11 # endif
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 # ifdef _WIN64
137                 SetDllDirectory("bin64");
138 # endif
139                 dllhandle = LoadLibrary (dllnames[i]);
140 # ifdef _WIN64
141                 SetDllDirectory(NULL);
142 # endif
143 #else
144                 dllhandle = dlopen (dllnames[i], RTLD_LAZY | RTLD_GLOBAL);
145 #endif
146                 if (Sys_LoadLibraryFunctions(dllhandle, fcts, true, (dllnames[i+1] != NULL) || (strrchr(com_argv[0], '/'))))
147                         break;
148                 else
149                         Sys_UnloadLibrary (&dllhandle);
150         }
151
152         // see if the names can be loaded relative to the executable path
153         // (this is for Mac OSX which does not check next to the executable)
154         if (!dllhandle && strrchr(com_argv[0], '/'))
155         {
156                 char path[MAX_OSPATH];
157                 strlcpy(path, com_argv[0], sizeof(path));
158                 strrchr(path, '/')[1] = 0;
159                 for (i = 0; dllnames[i] != NULL; i++)
160                 {
161                         char temp[MAX_OSPATH];
162                         strlcpy(temp, path, sizeof(temp));
163                         strlcat(temp, dllnames[i], sizeof(temp));
164                         Con_DPrintf (" \"%s\"", temp);
165 #ifdef WIN32
166                         dllhandle = LoadLibrary (temp);
167 #else
168                         dllhandle = dlopen (temp, RTLD_LAZY | RTLD_GLOBAL);
169 #endif
170                         if (Sys_LoadLibraryFunctions(dllhandle, fcts, true, dllnames[i+1] != NULL))
171                                 break;
172                         else
173                                 Sys_UnloadLibrary (&dllhandle);
174                 }
175         }
176
177         // No DLL found
178         if (! dllhandle)
179         {
180                 Con_DPrintf(" - failed.\n");
181                 return false;
182         }
183
184         Con_DPrintf(" - loaded.\n");
185
186         *handle = dllhandle;
187         return true;
188 #else
189         return false;
190 #endif
191 }
192
193 void Sys_UnloadLibrary (dllhandle_t* handle)
194 {
195 #ifdef SUPPORTDLL
196         if (handle == NULL || *handle == NULL)
197                 return;
198
199 #ifdef WIN32
200         FreeLibrary (*handle);
201 #else
202         dlclose (*handle);
203 #endif
204
205         *handle = NULL;
206 #endif
207 }
208
209 void* Sys_GetProcAddress (dllhandle_t handle, const char* name)
210 {
211 #ifdef SUPPORTDLL
212 #ifdef WIN32
213         return (void *)GetProcAddress (handle, name);
214 #else
215         return (void *)dlsym (handle, name);
216 #endif
217 #else
218         return NULL;
219 #endif
220 }
221
222 #ifdef WIN32
223 # define HAVE_TIMEGETTIME 1
224 # define HAVE_QUERYPERFORMANCECOUNTER 1
225 # define HAVE_Sleep 1
226 #endif
227
228 #if defined(CLOCK_MONOTONIC) || defined(CLOCK_HIRES)
229 # define HAVE_CLOCKGETTIME 1
230 #endif
231
232 #ifndef WIN32
233 // FIXME improve this check, manpage hints to DST_NONE
234 # define HAVE_GETTIMEOFDAY 1
235 #endif
236
237 #ifndef WIN32
238 // on Win32, select() cannot be used with all three FD list args being NULL according to MSDN
239 // (so much for POSIX...)
240 # ifdef FD_SET
241 #  define HAVE_SELECT 1
242 # endif
243 #endif
244
245 #ifndef WIN32
246 // FIXME improve this check
247 # define HAVE_USLEEP 1
248 #endif
249
250 // this one is referenced elsewhere
251 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."};
252
253 // these are not
254 static cvar_t sys_debugsleep = {0, "sys_debugsleep", "0", "write requested and attained sleep times to standard output, to be used with gnuplot"};
255 static cvar_t sys_usesdlgetticks = {CVAR_SAVE, "sys_usesdlgetticks", "0", "use SDL_GetTicks() timer (less accurate, for debugging)"};
256 static cvar_t sys_usesdldelay = {CVAR_SAVE, "sys_usesdldelay", "0", "use SDL_Delay() (less accurate, for debugging)"};
257 #if HAVE_QUERYPERFORMANCECOUNTER
258 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)"};
259 #endif
260 #if HAVE_CLOCKGETTIME
261 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)"};
262 #endif
263
264 static unsigned long benchmark_time;
265
266 void Sys_Init_Commands (void)
267 {
268         Cvar_RegisterVariable(&sys_debugsleep);
269         Cvar_RegisterVariable(&sys_usenoclockbutbenchmark);
270 #if HAVE_TIMEGETTIME || HAVE_QUERYPERFORMANCECOUNTER || HAVE_CLOCKGETTIME || HAVE_GETTIMEOFDAY
271         if(sys_supportsdlgetticks)
272         {
273                 Cvar_RegisterVariable(&sys_usesdlgetticks);
274                 Cvar_RegisterVariable(&sys_usesdldelay);
275         }
276 #endif
277 #if HAVE_QUERYPERFORMANCECOUNTER
278         Cvar_RegisterVariable(&sys_usequeryperformancecounter);
279 #endif
280 #if HAVE_CLOCKGETTIME
281         Cvar_RegisterVariable(&sys_useclockgettime);
282 #endif
283 }
284
285 double Sys_DoubleTime(void)
286 {
287         static int first = true;
288         static double oldtime = 0.0, curtime = 0.0;
289         double newtime;
290         if(sys_usenoclockbutbenchmark.integer)
291         {
292                 benchmark_time += 1;
293                 return ((double) benchmark_time) / 1e6;
294         }
295
296         // first all the OPTIONAL timers
297
298 #if HAVE_QUERYPERFORMANCECOUNTER
299         else if (sys_usequeryperformancecounter.integer)
300         {
301                 // LordHavoc: note to people modifying this code, DWORD is specifically defined as an unsigned 32bit number, therefore the 65536.0 * 65536.0 is fine.
302                 // QueryPerformanceCounter
303                 // platform:
304                 // Windows 95/98/ME/NT/2000/XP
305                 // features:
306                 // very accurate (CPU cycles)
307                 // known issues:
308                 // does not necessarily match realtime too well (tends to get faster and faster in win98)
309                 // wraps around occasionally on some platforms (depends on CPU speed and probably other unknown factors)
310                 double timescale;
311                 LARGE_INTEGER PerformanceFreq;
312                 LARGE_INTEGER PerformanceCount;
313
314                 if (!QueryPerformanceFrequency (&PerformanceFreq))
315                 {
316                         Con_Printf ("No hardware timer available\n");
317                         // fall back to timeGetTime
318                         Cvar_SetValueQuick(&sys_usequeryperformancecounter, false);
319                         return Sys_DoubleTime();
320                 }
321                 QueryPerformanceCounter (&PerformanceCount);
322
323                 #ifdef __BORLANDC__
324                 timescale = 1.0 / ((double) PerformanceFreq.u.LowPart + (double) PerformanceFreq.u.HighPart * 65536.0 * 65536.0);
325                 newtime = ((double) PerformanceCount.u.LowPart + (double) PerformanceCount.u.HighPart * 65536.0 * 65536.0) * timescale;
326                 #else
327                 timescale = 1.0 / ((double) PerformanceFreq.LowPart + (double) PerformanceFreq.HighPart * 65536.0 * 65536.0);
328                 newtime = ((double) PerformanceCount.LowPart + (double) PerformanceCount.HighPart * 65536.0 * 65536.0) * timescale;
329                 #endif
330         }
331 #endif
332
333 #if HAVE_CLOCKGETTIME
334         else if (sys_useclockgettime.integer)
335         {
336                 struct timespec ts;
337 #  ifdef CLOCK_MONOTONIC
338                 // linux
339                 clock_gettime(CLOCK_MONOTONIC, &ts);
340 #  else
341                 // sunos
342                 clock_gettime(CLOCK_HIGHRES, &ts);
343 #  endif
344                 newtime = (double) ts.tv_sec + ts.tv_nsec / 1000000000.0;
345         }
346 #endif
347
348         // now all the FALLBACK timers
349         else if(sys_supportsdlgetticks && sys_usesdlgetticks.integer)
350         {
351                 newtime = (double) Sys_SDL_GetTicks() / 1000.0;
352         }
353 #if HAVE_GETTIMEOFDAY
354         else
355         {
356                 struct timeval tp;
357                 gettimeofday(&tp, NULL);
358                 newtime = (double) tp.tv_sec + tp.tv_usec / 1000000.0;
359         }
360 #elif HAVE_TIMEGETTIME
361         else
362         {
363                 static int firsttimegettime = true;
364                 // timeGetTime
365                 // platform:
366                 // Windows 95/98/ME/NT/2000/XP
367                 // features:
368                 // reasonable accuracy (millisecond)
369                 // issues:
370                 // wraps around every 47 days or so (but this is non-fatal to us, odd times are rejected, only causes a one frame stutter)
371
372                 // make sure the timer is high precision, otherwise different versions of windows have varying accuracy
373                 if (firsttimegettime)
374                 {
375                         timeBeginPeriod (1);
376                         firsttimegettime = false;
377                 }
378
379                 newtime = (double) timeGetTime () / 1000.0;
380         }
381 #else
382         // fallback for using the SDL timer if no other timer is available
383         else
384         {
385                 newtime = (double) Sys_SDL_GetTicks() / 1000.0;
386                 // this calls Sys_Error() if not linking against SDL
387         }
388 #endif
389
390         if (first)
391         {
392                 first = false;
393                 oldtime = newtime;
394         }
395
396         if (newtime < oldtime)
397         {
398                 // warn if it's significant
399                 if (newtime - oldtime < -0.01)
400                         Con_Printf("Sys_DoubleTime: time stepped backwards (went from %f to %f, difference %f)\n", oldtime, newtime, newtime - oldtime);
401         }
402         else if (newtime > oldtime + 1800)
403         {
404                 Con_Printf("Sys_DoubleTime: time stepped forward (went from %f to %f, difference %f)\n", oldtime, newtime, newtime - oldtime);
405         }
406         else
407                 curtime += newtime - oldtime;
408         oldtime = newtime;
409
410         return curtime;
411 }
412
413 void Sys_Sleep(int microseconds)
414 {
415         double t = 0;
416         if(sys_usenoclockbutbenchmark.integer)
417         {
418                 benchmark_time += microseconds;
419                 return;
420         }
421         if(sys_debugsleep.integer)
422         {
423                 t = Sys_DoubleTime();
424         }
425         if(sys_supportsdlgetticks && sys_usesdldelay.integer)
426         {
427                 Sys_SDL_Delay(microseconds / 1000);
428         }
429 #if HAVE_SELECT
430         else
431         {
432                 struct timeval tv;
433                 tv.tv_sec = microseconds / 1000000;
434                 tv.tv_usec = microseconds % 1000000;
435                 select(0, NULL, NULL, NULL, &tv);
436         }
437 #elif HAVE_USLEEP
438         else
439         {
440                 usleep(microseconds);
441         }
442 #elif HAVE_Sleep
443         else
444         {
445                 Sleep(microseconds / 1000);
446         }
447 #else
448         else
449         {
450                 Sys_SDL_Delay(microseconds / 1000);
451         }
452 #endif
453         if(sys_debugsleep.integer)
454         {
455                 t = Sys_DoubleTime() - t;
456                 printf("%d %d # debugsleep\n", microseconds, (unsigned int)(t * 1000000));
457         }
458 }
459
460 const char *Sys_FindInPATH(const char *name, char namesep, const char *PATH, char pathsep, char *buf, size_t bufsize)
461 {
462         const char *p = PATH;
463         const char *q;
464         if(p && name)
465         {
466                 while((q = strchr(p, ':')))
467                 {
468                         dpsnprintf(buf, bufsize, "%.*s%c%s", (int)(q-p), p, namesep, name);
469                         if(FS_SysFileExists(buf))
470                                 return buf;
471                         p = q + 1;
472                 }
473                 if(!q) // none found - try the last item
474                 {
475                         dpsnprintf(buf, bufsize, "%s%c%s", p, namesep, name);
476                         if(FS_SysFileExists(buf))
477                                 return buf;
478                 }
479         }
480         return name;
481 }
482
483 const char *Sys_FindExecutableName(void)
484 {
485 #if defined(WIN32)
486         return com_argv[0];
487 #else
488         static char exenamebuf[MAX_OSPATH+1];
489         ssize_t n = -1;
490 #if defined(__FreeBSD__)
491         n = readlink("/proc/curproc/file", exenamebuf, sizeof(exenamebuf)-1);
492 #elif defined(__linux__)
493         n = readlink("/proc/self/exe", exenamebuf, sizeof(exenamebuf)-1);
494 #endif
495         if(n > 0 && (size_t)(n) < sizeof(exenamebuf))
496         {
497                 exenamebuf[n] = 0;
498                 return exenamebuf;
499         }
500         if(strchr(com_argv[0], '/'))
501                 return com_argv[0]; // possibly a relative path
502         else
503                 return Sys_FindInPATH(com_argv[0], '/', getenv("PATH"), ':', exenamebuf, sizeof(exenamebuf));
504 #endif
505 }
506
507 void Sys_ProvideSelfFD(void)
508 {
509         if(com_selffd != -1)
510                 return;
511         com_selffd = FS_SysOpenFD(Sys_FindExecutableName(), "rb", false);
512 }