Use separate macros for atomics that don't take a memory order
[openal-soft.git] / Alc / helpers.c
blobef0c8e88d48d48d8f5fdd951f6f01272fb37dcb2
1 /**
2 * OpenAL cross platform audio library
3 * Copyright (C) 2011 by authors.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
21 #ifdef _WIN32
22 #ifdef __MINGW32__
23 #define _WIN32_IE 0x501
24 #else
25 #define _WIN32_IE 0x400
26 #endif
27 #endif
29 #include "config.h"
31 #include <stdlib.h>
32 #include <time.h>
33 #include <errno.h>
34 #include <stdarg.h>
35 #include <ctype.h>
36 #ifdef HAVE_MALLOC_H
37 #include <malloc.h>
38 #endif
39 #ifdef HAVE_DIRENT_H
40 #include <dirent.h>
41 #endif
43 #ifndef AL_NO_UID_DEFS
44 #if defined(HAVE_GUIDDEF_H) || defined(HAVE_INITGUID_H)
45 #define INITGUID
46 #include <windows.h>
47 #ifdef HAVE_GUIDDEF_H
48 #include <guiddef.h>
49 #else
50 #include <initguid.h>
51 #endif
53 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80,0x00, 0x00,0xaa,0x00,0x38,0x9b,0x71);
54 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80,0x00, 0x00,0xaa,0x00,0x38,0x9b,0x71);
56 DEFINE_GUID(IID_IDirectSoundNotify, 0xb0210783, 0x89cd, 0x11d0, 0xaf,0x08, 0x00,0xa0,0xc9,0x25,0xcd,0x16);
58 DEFINE_GUID(CLSID_MMDeviceEnumerator, 0xbcde0395, 0xe52f, 0x467c, 0x8e,0x3d, 0xc4,0x57,0x92,0x91,0x69,0x2e);
59 DEFINE_GUID(IID_IMMDeviceEnumerator, 0xa95664d2, 0x9614, 0x4f35, 0xa7,0x46, 0xde,0x8d,0xb6,0x36,0x17,0xe6);
60 DEFINE_GUID(IID_IAudioClient, 0x1cb9ad4c, 0xdbfa, 0x4c32, 0xb1,0x78, 0xc2,0xf5,0x68,0xa7,0x03,0xb2);
61 DEFINE_GUID(IID_IAudioRenderClient, 0xf294acfc, 0x3146, 0x4483, 0xa7,0xbf, 0xad,0xdc,0xa7,0xc2,0x60,0xe2);
62 DEFINE_GUID(IID_IAudioCaptureClient, 0xc8adbd64, 0xe71e, 0x48a0, 0xa4,0xde, 0x18,0x5c,0x39,0x5c,0xd3,0x17);
64 #ifdef HAVE_MMDEVAPI
65 #include <wtypes.h>
66 #include <devpropdef.h>
67 #include <propkeydef.h>
68 DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80,0x20, 0x67,0xd1,0x46,0xa8,0x50,0xe0, 14);
69 DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_FormFactor, 0x1da5d803, 0xd492, 0x4edd, 0x8c,0x23, 0xe0,0xc0,0xff,0xee,0x7f,0x0e, 0);
70 DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_GUID, 0x1da5d803, 0xd492, 0x4edd, 0x8c, 0x23,0xe0, 0xc0,0xff,0xee,0x7f,0x0e, 4 );
71 #endif
72 #endif
73 #endif /* AL_NO_UID_DEFS */
75 #ifdef HAVE_DLFCN_H
76 #include <dlfcn.h>
77 #endif
78 #ifdef HAVE_INTRIN_H
79 #include <intrin.h>
80 #endif
81 #ifdef HAVE_CPUID_H
82 #include <cpuid.h>
83 #endif
84 #ifdef HAVE_SYS_SYSCONF_H
85 #include <sys/sysconf.h>
86 #endif
87 #ifdef HAVE_FLOAT_H
88 #include <float.h>
89 #endif
90 #ifdef HAVE_IEEEFP_H
91 #include <ieeefp.h>
92 #endif
94 #ifndef _WIN32
95 #include <sys/types.h>
96 #include <sys/stat.h>
97 #include <sys/mman.h>
98 #include <fcntl.h>
99 #include <unistd.h>
100 #elif defined(_WIN32_IE)
101 #include <shlobj.h>
102 #endif
104 #include "alMain.h"
105 #include "alu.h"
106 #include "atomic.h"
107 #include "uintmap.h"
108 #include "vector.h"
109 #include "alstring.h"
110 #include "compat.h"
111 #include "threads.h"
114 extern inline ALuint NextPowerOf2(ALuint value);
115 extern inline ALint fastf2i(ALfloat f);
116 extern inline ALuint fastf2u(ALfloat f);
119 ALuint CPUCapFlags = 0;
122 void FillCPUCaps(ALuint capfilter)
124 ALuint caps = 0;
126 /* FIXME: We really should get this for all available CPUs in case different
127 * CPUs have different caps (is that possible on one machine?). */
128 #if defined(HAVE_GCC_GET_CPUID) && (defined(__i386__) || defined(__x86_64__) || \
129 defined(_M_IX86) || defined(_M_X64))
130 union {
131 unsigned int regs[4];
132 char str[sizeof(unsigned int[4])];
133 } cpuinf[3];
135 if(!__get_cpuid(0, &cpuinf[0].regs[0], &cpuinf[0].regs[1], &cpuinf[0].regs[2], &cpuinf[0].regs[3]))
136 ERR("Failed to get CPUID\n");
137 else
139 unsigned int maxfunc = cpuinf[0].regs[0];
140 unsigned int maxextfunc = 0;
142 if(__get_cpuid(0x80000000, &cpuinf[0].regs[0], &cpuinf[0].regs[1], &cpuinf[0].regs[2], &cpuinf[0].regs[3]))
143 maxextfunc = cpuinf[0].regs[0];
144 TRACE("Detected max CPUID function: 0x%x (ext. 0x%x)\n", maxfunc, maxextfunc);
146 TRACE("Vendor ID: \"%.4s%.4s%.4s\"\n", cpuinf[0].str+4, cpuinf[0].str+12, cpuinf[0].str+8);
147 if(maxextfunc >= 0x80000004 &&
148 __get_cpuid(0x80000002, &cpuinf[0].regs[0], &cpuinf[0].regs[1], &cpuinf[0].regs[2], &cpuinf[0].regs[3]) &&
149 __get_cpuid(0x80000003, &cpuinf[1].regs[0], &cpuinf[1].regs[1], &cpuinf[1].regs[2], &cpuinf[1].regs[3]) &&
150 __get_cpuid(0x80000004, &cpuinf[2].regs[0], &cpuinf[2].regs[1], &cpuinf[2].regs[2], &cpuinf[2].regs[3]))
151 TRACE("Name: \"%.16s%.16s%.16s\"\n", cpuinf[0].str, cpuinf[1].str, cpuinf[2].str);
153 if(maxfunc >= 1 &&
154 __get_cpuid(1, &cpuinf[0].regs[0], &cpuinf[0].regs[1], &cpuinf[0].regs[2], &cpuinf[0].regs[3]))
156 if((cpuinf[0].regs[3]&(1<<25)))
158 caps |= CPU_CAP_SSE;
159 if((cpuinf[0].regs[3]&(1<<26)))
161 caps |= CPU_CAP_SSE2;
162 if((cpuinf[0].regs[2]&(1<<0)))
164 caps |= CPU_CAP_SSE3;
165 if((cpuinf[0].regs[2]&(1<<19)))
166 caps |= CPU_CAP_SSE4_1;
172 #elif defined(HAVE_CPUID_INTRINSIC) && (defined(__i386__) || defined(__x86_64__) || \
173 defined(_M_IX86) || defined(_M_X64))
174 union {
175 int regs[4];
176 char str[sizeof(int[4])];
177 } cpuinf[3];
179 (__cpuid)(cpuinf[0].regs, 0);
180 if(cpuinf[0].regs[0] == 0)
181 ERR("Failed to get CPUID\n");
182 else
184 unsigned int maxfunc = cpuinf[0].regs[0];
185 unsigned int maxextfunc;
187 (__cpuid)(cpuinf[0].regs, 0x80000000);
188 maxextfunc = cpuinf[0].regs[0];
190 TRACE("Detected max CPUID function: 0x%x (ext. 0x%x)\n", maxfunc, maxextfunc);
192 TRACE("Vendor ID: \"%.4s%.4s%.4s\"\n", cpuinf[0].str+4, cpuinf[0].str+12, cpuinf[0].str+8);
193 if(maxextfunc >= 0x80000004)
195 (__cpuid)(cpuinf[0].regs, 0x80000002);
196 (__cpuid)(cpuinf[1].regs, 0x80000003);
197 (__cpuid)(cpuinf[2].regs, 0x80000004);
198 TRACE("Name: \"%.16s%.16s%.16s\"\n", cpuinf[0].str, cpuinf[1].str, cpuinf[2].str);
201 if(maxfunc >= 1)
203 (__cpuid)(cpuinf[0].regs, 1);
204 if((cpuinf[0].regs[3]&(1<<25)))
206 caps |= CPU_CAP_SSE;
207 if((cpuinf[0].regs[3]&(1<<26)))
209 caps |= CPU_CAP_SSE2;
210 if((cpuinf[0].regs[2]&(1<<0)))
212 caps |= CPU_CAP_SSE3;
213 if((cpuinf[0].regs[2]&(1<<19)))
214 caps |= CPU_CAP_SSE4_1;
220 #else
221 /* Assume support for whatever's supported if we can't check for it */
222 #if defined(HAVE_SSE4_1)
223 #warning "Assuming SSE 4.1 run-time support!"
224 caps |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE3 | CPU_CAP_SSE4_1;
225 #elif defined(HAVE_SSE3)
226 #warning "Assuming SSE 3 run-time support!"
227 caps |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE3;
228 #elif defined(HAVE_SSE2)
229 #warning "Assuming SSE 2 run-time support!"
230 caps |= CPU_CAP_SSE | CPU_CAP_SSE2;
231 #elif defined(HAVE_SSE)
232 #warning "Assuming SSE run-time support!"
233 caps |= CPU_CAP_SSE;
234 #endif
235 #endif
236 #ifdef HAVE_NEON
237 FILE *file = fopen("/proc/cpuinfo", "rt");
238 if(!file)
239 ERR("Failed to open /proc/cpuinfo, cannot check for NEON support\n");
240 else
242 char buf[256];
243 while(fgets(buf, sizeof(buf), file) != NULL)
245 char *str;
247 if(strncmp(buf, "Features\t:", 10) != 0)
248 continue;
250 TRACE("Got features string:%s\n", buf+10);
252 str = buf;
253 while((str=strstr(str, "neon")) != NULL)
255 if(isspace(*(str-1)) && (str[4] == 0 || isspace(str[4])))
257 caps |= CPU_CAP_NEON;
258 break;
260 str++;
262 break;
265 fclose(file);
266 file = NULL;
268 #endif
270 TRACE("Extensions:%s%s%s%s%s%s\n",
271 ((capfilter&CPU_CAP_SSE) ? ((caps&CPU_CAP_SSE) ? " +SSE" : " -SSE") : ""),
272 ((capfilter&CPU_CAP_SSE2) ? ((caps&CPU_CAP_SSE2) ? " +SSE2" : " -SSE2") : ""),
273 ((capfilter&CPU_CAP_SSE3) ? ((caps&CPU_CAP_SSE3) ? " +SSE3" : " -SSE3") : ""),
274 ((capfilter&CPU_CAP_SSE4_1) ? ((caps&CPU_CAP_SSE4_1) ? " +SSE4.1" : " -SSE4.1") : ""),
275 ((capfilter&CPU_CAP_NEON) ? ((caps&CPU_CAP_NEON) ? " +Neon" : " -Neon") : ""),
276 ((!capfilter) ? " -none-" : "")
278 CPUCapFlags = caps & capfilter;
282 void SetMixerFPUMode(FPUCtl *ctl)
284 #ifdef HAVE_FENV_H
285 fegetenv(STATIC_CAST(fenv_t, ctl));
286 #if defined(__GNUC__) && defined(HAVE_SSE)
287 /* FIXME: Some fegetenv implementations can get the SSE environment too?
288 * How to tell when it does? */
289 if((CPUCapFlags&CPU_CAP_SSE))
290 __asm__ __volatile__("stmxcsr %0" : "=m" (*&ctl->sse_state));
291 #endif
293 #ifdef FE_TOWARDZERO
294 fesetround(FE_TOWARDZERO);
295 #endif
296 #if defined(__GNUC__) && defined(HAVE_SSE)
297 if((CPUCapFlags&CPU_CAP_SSE))
299 int sseState = ctl->sse_state;
300 sseState |= 0x6000; /* set round-to-zero */
301 sseState |= 0x8000; /* set flush-to-zero */
302 if((CPUCapFlags&CPU_CAP_SSE2))
303 sseState |= 0x0040; /* set denormals-are-zero */
304 __asm__ __volatile__("ldmxcsr %0" : : "m" (*&sseState));
306 #endif
308 #elif defined(HAVE___CONTROL87_2)
310 int mode;
311 __control87_2(0, 0, &ctl->state, NULL);
312 __control87_2(_RC_CHOP, _MCW_RC, &mode, NULL);
313 #ifdef HAVE_SSE
314 if((CPUCapFlags&CPU_CAP_SSE))
316 __control87_2(0, 0, NULL, &ctl->sse_state);
317 __control87_2(_RC_CHOP|_DN_FLUSH, _MCW_RC|_MCW_DN, NULL, &mode);
319 #endif
321 #elif defined(HAVE__CONTROLFP)
323 ctl->state = _controlfp(0, 0);
324 (void)_controlfp(_RC_CHOP, _MCW_RC);
325 #endif
328 void RestoreFPUMode(const FPUCtl *ctl)
330 #ifdef HAVE_FENV_H
331 fesetenv(STATIC_CAST(fenv_t, ctl));
332 #if defined(__GNUC__) && defined(HAVE_SSE)
333 if((CPUCapFlags&CPU_CAP_SSE))
334 __asm__ __volatile__("ldmxcsr %0" : : "m" (*&ctl->sse_state));
335 #endif
337 #elif defined(HAVE___CONTROL87_2)
339 int mode;
340 __control87_2(ctl->state, _MCW_RC, &mode, NULL);
341 #ifdef HAVE_SSE
342 if((CPUCapFlags&CPU_CAP_SSE))
343 __control87_2(ctl->sse_state, _MCW_RC|_MCW_DN, NULL, &mode);
344 #endif
346 #elif defined(HAVE__CONTROLFP)
348 _controlfp(ctl->state, _MCW_RC);
349 #endif
353 static int StringSortCompare(const void *str1, const void *str2)
355 return al_string_cmp(*(const_al_string*)str1, *(const_al_string*)str2);
358 #ifdef _WIN32
360 static WCHAR *strrchrW(WCHAR *str, WCHAR ch)
362 WCHAR *ret = NULL;
363 while(*str)
365 if(*str == ch)
366 ret = str;
367 ++str;
369 return ret;
372 al_string GetProcPath(void)
374 al_string ret = AL_STRING_INIT_STATIC();
375 WCHAR *pathname, *sep;
376 DWORD pathlen;
377 DWORD len;
379 pathlen = 256;
380 pathname = malloc(pathlen * sizeof(pathname[0]));
381 while(pathlen > 0 && (len=GetModuleFileNameW(NULL, pathname, pathlen)) == pathlen)
383 free(pathname);
384 pathlen <<= 1;
385 pathname = malloc(pathlen * sizeof(pathname[0]));
387 if(len == 0)
389 free(pathname);
390 ERR("Failed to get process name: error %lu\n", GetLastError());
391 return ret;
394 pathname[len] = 0;
395 if((sep = strrchrW(pathname, '\\')))
397 WCHAR *sep2 = strrchrW(pathname, '/');
398 if(sep2) *sep2 = 0;
399 else *sep = 0;
401 else if((sep = strrchrW(pathname, '/')))
402 *sep = 0;
403 al_string_copy_wcstr(&ret, pathname);
404 free(pathname);
406 TRACE("Got: %s\n", al_string_get_cstr(ret));
407 return ret;
411 static WCHAR *FromUTF8(const char *str)
413 WCHAR *out = NULL;
414 int len;
416 if((len=MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0)) > 0)
418 out = calloc(sizeof(WCHAR), len);
419 MultiByteToWideChar(CP_UTF8, 0, str, -1, out, len);
421 return out;
425 void *LoadLib(const char *name)
427 HANDLE hdl = NULL;
428 WCHAR *wname;
430 wname = FromUTF8(name);
431 if(!wname)
432 ERR("Failed to convert UTF-8 filename: \"%s\"\n", name);
433 else
435 hdl = LoadLibraryW(wname);
436 free(wname);
438 return hdl;
440 void CloseLib(void *handle)
441 { FreeLibrary((HANDLE)handle); }
442 void *GetSymbol(void *handle, const char *name)
444 void *ret;
446 ret = (void*)GetProcAddress((HANDLE)handle, name);
447 if(ret == NULL)
448 ERR("Failed to load %s\n", name);
449 return ret;
452 WCHAR *strdupW(const WCHAR *str)
454 const WCHAR *n;
455 WCHAR *ret;
456 size_t len;
458 n = str;
459 while(*n) n++;
460 len = n - str;
462 ret = calloc(sizeof(WCHAR), len+1);
463 if(ret != NULL)
464 memcpy(ret, str, sizeof(WCHAR)*len);
465 return ret;
468 FILE *al_fopen(const char *fname, const char *mode)
470 WCHAR *wname=NULL, *wmode=NULL;
471 FILE *file = NULL;
473 wname = FromUTF8(fname);
474 wmode = FromUTF8(mode);
475 if(!wname)
476 ERR("Failed to convert UTF-8 filename: \"%s\"\n", fname);
477 else if(!wmode)
478 ERR("Failed to convert UTF-8 mode: \"%s\"\n", mode);
479 else
480 file = _wfopen(wname, wmode);
482 free(wname);
483 free(wmode);
485 return file;
489 void al_print(const char *type, const char *func, const char *fmt, ...)
491 char str[1024];
492 WCHAR *wstr;
493 va_list ap;
495 va_start(ap, fmt);
496 vsnprintf(str, sizeof(str), fmt, ap);
497 va_end(ap);
499 str[sizeof(str)-1] = 0;
500 wstr = FromUTF8(str);
501 if(!wstr)
502 fprintf(LogFile, "AL lib: %s %s: <UTF-8 error> %s", type, func, str);
503 else
505 fprintf(LogFile, "AL lib: %s %s: %ls", type, func, wstr);
506 free(wstr);
507 wstr = NULL;
509 fflush(LogFile);
513 static inline int is_slash(int c)
514 { return (c == '\\' || c == '/'); }
516 static void DirectorySearch(const char *path, const char *ext, vector_al_string *results)
518 al_string pathstr = AL_STRING_INIT_STATIC();
519 WIN32_FIND_DATAW fdata;
520 WCHAR *wpath;
521 HANDLE hdl;
523 al_string_copy_cstr(&pathstr, path);
524 al_string_append_cstr(&pathstr, "\\*");
525 al_string_append_cstr(&pathstr, ext);
527 TRACE("Searching %s\n", al_string_get_cstr(pathstr));
529 wpath = FromUTF8(al_string_get_cstr(pathstr));
531 hdl = FindFirstFileW(wpath, &fdata);
532 if(hdl != INVALID_HANDLE_VALUE)
534 size_t base = VECTOR_SIZE(*results);
535 do {
536 al_string str = AL_STRING_INIT_STATIC();
537 al_string_copy_cstr(&str, path);
538 al_string_append_char(&str, '\\');
539 al_string_append_wcstr(&str, fdata.cFileName);
540 TRACE("Got result %s\n", al_string_get_cstr(str));
541 VECTOR_PUSH_BACK(*results, str);
542 } while(FindNextFileW(hdl, &fdata));
543 FindClose(hdl);
545 if(VECTOR_SIZE(*results) > base)
546 qsort(VECTOR_BEGIN(*results)+base, VECTOR_SIZE(*results)-base,
547 sizeof(VECTOR_FRONT(*results)), StringSortCompare);
550 free(wpath);
551 al_string_deinit(&pathstr);
554 vector_al_string SearchDataFiles(const char *ext, const char *subdir)
556 static const int ids[2] = { CSIDL_APPDATA, CSIDL_COMMON_APPDATA };
557 static RefCount search_lock;
558 vector_al_string results = VECTOR_INIT_STATIC();
559 size_t i;
561 while(ATOMIC_EXCHANGE_SEQ(uint, &search_lock, 1) == 1)
562 althrd_yield();
564 /* If the path is absolute, use it directly. */
565 if(isalpha(subdir[0]) && subdir[1] == ':' && is_slash(subdir[2]))
567 al_string path = AL_STRING_INIT_STATIC();
568 al_string_copy_cstr(&path, subdir);
569 #define FIX_SLASH(i) do { if(*(i) == '/') *(i) = '\\'; } while(0)
570 VECTOR_FOR_EACH(char, path, FIX_SLASH);
571 #undef FIX_SLASH
573 DirectorySearch(al_string_get_cstr(path), ext, &results);
575 al_string_deinit(&path);
577 else if(subdir[0] == '\\' && subdir[1] == '\\' && subdir[2] == '?' && subdir[3] == '\\')
578 DirectorySearch(subdir, ext, &results);
579 else
581 al_string path = AL_STRING_INIT_STATIC();
582 WCHAR *cwdbuf;
584 /* Search the app-local directory. */
585 if((cwdbuf=_wgetenv(L"ALSOFT_LOCAL_PATH")) && *cwdbuf != '\0')
587 al_string_copy_wcstr(&path, cwdbuf);
588 if(is_slash(VECTOR_BACK(path)))
590 VECTOR_POP_BACK(path);
591 *VECTOR_END(path) = 0;
594 else if(!(cwdbuf=_wgetcwd(NULL, 0)))
595 al_string_copy_cstr(&path, ".");
596 else
598 al_string_copy_wcstr(&path, cwdbuf);
599 if(is_slash(VECTOR_BACK(path)))
601 VECTOR_POP_BACK(path);
602 *VECTOR_END(path) = 0;
604 free(cwdbuf);
606 #define FIX_SLASH(i) do { if(*(i) == '/') *(i) = '\\'; } while(0)
607 VECTOR_FOR_EACH(char, path, FIX_SLASH);
608 #undef FIX_SLASH
609 DirectorySearch(al_string_get_cstr(path), ext, &results);
611 /* Search the local and global data dirs. */
612 for(i = 0;i < COUNTOF(ids);i++)
614 WCHAR buffer[PATH_MAX];
615 if(SHGetSpecialFolderPathW(NULL, buffer, ids[i], FALSE) != FALSE)
617 al_string_copy_wcstr(&path, buffer);
618 if(!is_slash(VECTOR_BACK(path)))
619 al_string_append_char(&path, '\\');
620 al_string_append_cstr(&path, subdir);
621 #define FIX_SLASH(i) do { if(*(i) == '/') *(i) = '\\'; } while(0)
622 VECTOR_FOR_EACH(char, path, FIX_SLASH);
623 #undef FIX_SLASH
625 DirectorySearch(al_string_get_cstr(path), ext, &results);
629 al_string_deinit(&path);
632 ATOMIC_STORE_SEQ(&search_lock, 0);
634 return results;
638 struct FileMapping MapFileToMem(const char *fname)
640 struct FileMapping ret = { NULL, NULL, NULL, 0 };
641 MEMORY_BASIC_INFORMATION meminfo;
642 HANDLE file, fmap;
643 WCHAR *wname;
644 void *ptr;
646 wname = FromUTF8(fname);
648 file = CreateFileW(wname, GENERIC_READ, FILE_SHARE_READ, NULL,
649 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
650 if(file == INVALID_HANDLE_VALUE)
652 ERR("Failed to open %s: %lu\n", fname, GetLastError());
653 free(wname);
654 return ret;
656 free(wname);
657 wname = NULL;
659 fmap = CreateFileMappingW(file, NULL, PAGE_READONLY, 0, 0, NULL);
660 if(!fmap)
662 ERR("Failed to create map for %s: %lu\n", fname, GetLastError());
663 CloseHandle(file);
664 return ret;
667 ptr = MapViewOfFile(fmap, FILE_MAP_READ, 0, 0, 0);
668 if(!ptr)
670 ERR("Failed to map %s: %lu\n", fname, GetLastError());
671 CloseHandle(fmap);
672 CloseHandle(file);
673 return ret;
676 if(VirtualQuery(ptr, &meminfo, sizeof(meminfo)) != sizeof(meminfo))
678 ERR("Failed to get map size for %s: %lu\n", fname, GetLastError());
679 UnmapViewOfFile(ptr);
680 CloseHandle(fmap);
681 CloseHandle(file);
682 return ret;
685 ret.file = file;
686 ret.fmap = fmap;
687 ret.ptr = ptr;
688 ret.len = meminfo.RegionSize;
689 return ret;
692 void UnmapFileMem(const struct FileMapping *mapping)
694 UnmapViewOfFile(mapping->ptr);
695 CloseHandle(mapping->fmap);
696 CloseHandle(mapping->file);
699 #else
701 al_string GetProcPath(void)
703 al_string ret = AL_STRING_INIT_STATIC();
704 const char *fname;
705 char *pathname, *sep;
706 size_t pathlen;
707 ssize_t len;
709 pathlen = 256;
710 pathname = malloc(pathlen);
712 fname = "/proc/self/exe";
713 len = readlink(fname, pathname, pathlen);
714 if(len == -1 && errno == ENOENT)
716 fname = "/proc/self/file";
717 len = readlink(fname, pathname, pathlen);
720 while(len > 0 && (size_t)len == pathlen)
722 free(pathname);
723 pathlen <<= 1;
724 pathname = malloc(pathlen);
725 len = readlink(fname, pathname, pathlen);
727 if(len <= 0)
729 free(pathname);
730 WARN("Failed to readlink %s: %s\n", fname, strerror(errno));
731 return ret;
734 pathname[len] = 0;
735 sep = strrchr(pathname, '/');
736 if(sep)
737 al_string_copy_range(&ret, pathname, sep);
738 else
739 al_string_copy_cstr(&ret, pathname);
740 free(pathname);
742 TRACE("Got: %s\n", al_string_get_cstr(ret));
743 return ret;
747 #ifdef HAVE_DLFCN_H
749 void *LoadLib(const char *name)
751 const char *err;
752 void *handle;
754 dlerror();
755 handle = dlopen(name, RTLD_NOW);
756 if((err=dlerror()) != NULL)
757 handle = NULL;
758 return handle;
760 void CloseLib(void *handle)
761 { dlclose(handle); }
762 void *GetSymbol(void *handle, const char *name)
764 const char *err;
765 void *sym;
767 dlerror();
768 sym = dlsym(handle, name);
769 if((err=dlerror()) != NULL)
771 WARN("Failed to load %s: %s\n", name, err);
772 sym = NULL;
774 return sym;
777 #endif /* HAVE_DLFCN_H */
779 void al_print(const char *type, const char *func, const char *fmt, ...)
781 va_list ap;
783 va_start(ap, fmt);
784 fprintf(LogFile, "AL lib: %s %s: ", type, func);
785 vfprintf(LogFile, fmt, ap);
786 va_end(ap);
788 fflush(LogFile);
792 static void DirectorySearch(const char *path, const char *ext, vector_al_string *results)
794 size_t extlen = strlen(ext);
795 DIR *dir;
797 TRACE("Searching %s for *%s\n", path, ext);
798 dir = opendir(path);
799 if(dir != NULL)
801 size_t base = VECTOR_SIZE(*results);
802 struct dirent *dirent;
803 while((dirent=readdir(dir)) != NULL)
805 al_string str;
806 size_t len;
807 if(strcmp(dirent->d_name, ".") == 0 || strcmp(dirent->d_name, "..") == 0)
808 continue;
810 len = strlen(dirent->d_name);
811 if(!(len > extlen))
812 continue;
813 if(strcasecmp(dirent->d_name+len-extlen, ext) != 0)
814 continue;
816 AL_STRING_INIT(str);
817 al_string_copy_cstr(&str, path);
818 if(VECTOR_BACK(str) != '/')
819 al_string_append_char(&str, '/');
820 al_string_append_cstr(&str, dirent->d_name);
821 TRACE("Got result %s\n", al_string_get_cstr(str));
822 VECTOR_PUSH_BACK(*results, str);
824 closedir(dir);
826 if(VECTOR_SIZE(*results) > base)
827 qsort(VECTOR_BEGIN(*results)+base, VECTOR_SIZE(*results)-base,
828 sizeof(VECTOR_FRONT(*results)), StringSortCompare);
832 vector_al_string SearchDataFiles(const char *ext, const char *subdir)
834 static RefCount search_lock;
835 vector_al_string results = VECTOR_INIT_STATIC();
837 while(ATOMIC_EXCHANGE_SEQ(uint, &search_lock, 1) == 1)
838 althrd_yield();
840 if(subdir[0] == '/')
841 DirectorySearch(subdir, ext, &results);
842 else
844 al_string path = AL_STRING_INIT_STATIC();
845 const char *str, *next;
846 char cwdbuf[PATH_MAX];
848 /* Search the app-local directory. */
849 if((str=getenv("ALSOFT_LOCAL_PATH")) && *str != '\0')
850 DirectorySearch(str, ext, &results);
851 else if(getcwd(cwdbuf, sizeof(cwdbuf)))
852 DirectorySearch(cwdbuf, ext, &results);
853 else
854 DirectorySearch(".", ext, &results);
856 // Search local data dir
857 if((str=getenv("XDG_DATA_HOME")) != NULL && str[0] != '\0')
859 al_string_copy_cstr(&path, str);
860 if(VECTOR_BACK(path) != '/')
861 al_string_append_char(&path, '/');
862 al_string_append_cstr(&path, subdir);
863 DirectorySearch(al_string_get_cstr(path), ext, &results);
865 else if((str=getenv("HOME")) != NULL && str[0] != '\0')
867 al_string_copy_cstr(&path, str);
868 if(VECTOR_BACK(path) == '/')
870 VECTOR_POP_BACK(path);
871 *VECTOR_END(path) = 0;
873 al_string_append_cstr(&path, "/.local/share/");
874 al_string_append_cstr(&path, subdir);
875 DirectorySearch(al_string_get_cstr(path), ext, &results);
878 // Search global data dirs
879 if((str=getenv("XDG_DATA_DIRS")) == NULL || str[0] == '\0')
880 str = "/usr/local/share/:/usr/share/";
882 next = str;
883 while((str=next) != NULL && str[0] != '\0')
885 next = strchr(str, ':');
886 if(!next)
887 al_string_copy_cstr(&path, str);
888 else
890 al_string_copy_range(&path, str, next);
891 ++next;
893 if(!al_string_empty(path))
895 if(VECTOR_BACK(path) != '/')
896 al_string_append_char(&path, '/');
897 al_string_append_cstr(&path, subdir);
899 DirectorySearch(al_string_get_cstr(path), ext, &results);
903 al_string_deinit(&path);
906 ATOMIC_STORE_SEQ(&search_lock, 0);
908 return results;
912 struct FileMapping MapFileToMem(const char *fname)
914 struct FileMapping ret = { -1, NULL, 0 };
915 struct stat sbuf;
916 void *ptr;
917 int fd;
919 fd = open(fname, O_RDONLY, 0);
920 if(fd == -1)
922 ERR("Failed to open %s: (%d) %s\n", fname, errno, strerror(errno));
923 return ret;
925 if(fstat(fd, &sbuf) == -1)
927 ERR("Failed to stat %s: (%d) %s\n", fname, errno, strerror(errno));
928 close(fd);
929 return ret;
932 ptr = mmap(NULL, sbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
933 if(ptr == MAP_FAILED)
935 ERR("Failed to map %s: (%d) %s\n", fname, errno, strerror(errno));
936 close(fd);
937 return ret;
940 ret.fd = fd;
941 ret.ptr = ptr;
942 ret.len = sbuf.st_size;
943 return ret;
946 void UnmapFileMem(const struct FileMapping *mapping)
948 munmap(mapping->ptr, mapping->len);
949 close(mapping->fd);
952 #endif
955 void SetRTPriority(void)
957 ALboolean failed = AL_FALSE;
959 #ifdef _WIN32
960 if(RTPrioLevel > 0)
961 failed = !SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
962 #elif defined(HAVE_PTHREAD_SETSCHEDPARAM) && !defined(__OpenBSD__)
963 if(RTPrioLevel > 0)
965 struct sched_param param;
966 /* Use the minimum real-time priority possible for now (on Linux this
967 * should be 1 for SCHED_RR) */
968 param.sched_priority = sched_get_priority_min(SCHED_RR);
969 failed = !!pthread_setschedparam(pthread_self(), SCHED_RR, &param);
971 #else
972 /* Real-time priority not available */
973 failed = (RTPrioLevel>0);
974 #endif
975 if(failed)
976 ERR("Failed to set priority level for thread\n");
980 extern inline void al_string_deinit(al_string *str);
981 extern inline size_t al_string_length(const_al_string str);
982 extern inline ALboolean al_string_empty(const_al_string str);
983 extern inline const al_string_char_type *al_string_get_cstr(const_al_string str);
985 void al_string_clear(al_string *str)
987 if(!al_string_empty(*str))
989 /* Reserve one more character than the total size of the string. This
990 * is to ensure we have space to add a null terminator in the string
991 * data so it can be used as a C-style string.
993 VECTOR_RESIZE(*str, 0, 1);
994 VECTOR_ELEM(*str, 0) = 0;
998 static inline int al_string_compare(const al_string_char_type *str1, size_t str1len,
999 const al_string_char_type *str2, size_t str2len)
1001 size_t complen = (str1len < str2len) ? str1len : str2len;
1002 int ret = memcmp(str1, str2, complen);
1003 if(ret == 0)
1005 if(str1len > str2len) return 1;
1006 if(str1len < str2len) return -1;
1008 return ret;
1010 int al_string_cmp(const_al_string str1, const_al_string str2)
1012 return al_string_compare(&VECTOR_FRONT(str1), al_string_length(str1),
1013 &VECTOR_FRONT(str2), al_string_length(str2));
1015 int al_string_cmp_cstr(const_al_string str1, const al_string_char_type *str2)
1017 return al_string_compare(&VECTOR_FRONT(str1), al_string_length(str1),
1018 str2, strlen(str2));
1021 void al_string_copy(al_string *str, const_al_string from)
1023 size_t len = al_string_length(from);
1024 size_t i;
1026 VECTOR_RESIZE(*str, len, len+1);
1027 for(i = 0;i < len;i++)
1028 VECTOR_ELEM(*str, i) = VECTOR_ELEM(from, i);
1029 VECTOR_ELEM(*str, i) = 0;
1032 void al_string_copy_cstr(al_string *str, const al_string_char_type *from)
1034 size_t len = strlen(from);
1035 size_t i;
1037 VECTOR_RESIZE(*str, len, len+1);
1038 for(i = 0;i < len;i++)
1039 VECTOR_ELEM(*str, i) = from[i];
1040 VECTOR_ELEM(*str, i) = 0;
1043 void al_string_copy_range(al_string *str, const al_string_char_type *from, const al_string_char_type *to)
1045 size_t len = to - from;
1046 size_t i;
1048 VECTOR_RESIZE(*str, len, len+1);
1049 for(i = 0;i < len;i++)
1050 VECTOR_ELEM(*str, i) = from[i];
1051 VECTOR_ELEM(*str, i) = 0;
1054 void al_string_append_char(al_string *str, const al_string_char_type c)
1056 size_t len = al_string_length(*str);
1057 VECTOR_RESIZE(*str, len, len+2);
1058 VECTOR_PUSH_BACK(*str, c);
1059 VECTOR_ELEM(*str, len+1) = 0;
1062 void al_string_append_cstr(al_string *str, const al_string_char_type *from)
1064 size_t len = strlen(from);
1065 if(len != 0)
1067 size_t base = al_string_length(*str);
1068 size_t i;
1070 VECTOR_RESIZE(*str, base+len, base+len+1);
1071 for(i = 0;i < len;i++)
1072 VECTOR_ELEM(*str, base+i) = from[i];
1073 VECTOR_ELEM(*str, base+i) = 0;
1077 void al_string_append_range(al_string *str, const al_string_char_type *from, const al_string_char_type *to)
1079 size_t len = to - from;
1080 if(len != 0)
1082 size_t base = al_string_length(*str);
1083 size_t i;
1085 VECTOR_RESIZE(*str, base+len, base+len+1);
1086 for(i = 0;i < len;i++)
1087 VECTOR_ELEM(*str, base+i) = from[i];
1088 VECTOR_ELEM(*str, base+i) = 0;
1092 #ifdef _WIN32
1093 void al_string_copy_wcstr(al_string *str, const wchar_t *from)
1095 int len;
1096 if((len=WideCharToMultiByte(CP_UTF8, 0, from, -1, NULL, 0, NULL, NULL)) > 0)
1098 VECTOR_RESIZE(*str, len-1, len);
1099 WideCharToMultiByte(CP_UTF8, 0, from, -1, &VECTOR_FRONT(*str), len, NULL, NULL);
1100 VECTOR_ELEM(*str, len-1) = 0;
1104 void al_string_append_wcstr(al_string *str, const wchar_t *from)
1106 int len;
1107 if((len=WideCharToMultiByte(CP_UTF8, 0, from, -1, NULL, 0, NULL, NULL)) > 0)
1109 size_t base = al_string_length(*str);
1110 VECTOR_RESIZE(*str, base+len-1, base+len);
1111 WideCharToMultiByte(CP_UTF8, 0, from, -1, &VECTOR_ELEM(*str, base), len, NULL, NULL);
1112 VECTOR_ELEM(*str, base+len-1) = 0;
1116 void al_string_append_wrange(al_string *str, const wchar_t *from, const wchar_t *to)
1118 int len;
1119 if((len=WideCharToMultiByte(CP_UTF8, 0, from, (int)(to-from), NULL, 0, NULL, NULL)) > 0)
1121 size_t base = al_string_length(*str);
1122 VECTOR_RESIZE(*str, base+len, base+len+1);
1123 WideCharToMultiByte(CP_UTF8, 0, from, (int)(to-from), &VECTOR_ELEM(*str, base), len+1, NULL, NULL);
1124 VECTOR_ELEM(*str, base+len) = 0;
1127 #endif