Merge pull request #204 from jhasse/android-byte-order
[openal-soft.git] / Alc / helpers.c
blob53d85a86d660a14f2163173e20223488d0b62c82
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
42 #ifdef HAVE_PROC_PIDPATH
43 #include <libproc.h>
44 #endif
46 #ifdef __FreeBSD__
47 #include <sys/types.h>
48 #include <sys/sysctl.h>
49 #endif
51 #ifndef AL_NO_UID_DEFS
52 #if defined(HAVE_GUIDDEF_H) || defined(HAVE_INITGUID_H)
53 #define INITGUID
54 #include <windows.h>
55 #ifdef HAVE_GUIDDEF_H
56 #include <guiddef.h>
57 #else
58 #include <initguid.h>
59 #endif
61 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80,0x00, 0x00,0xaa,0x00,0x38,0x9b,0x71);
62 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80,0x00, 0x00,0xaa,0x00,0x38,0x9b,0x71);
64 DEFINE_GUID(IID_IDirectSoundNotify, 0xb0210783, 0x89cd, 0x11d0, 0xaf,0x08, 0x00,0xa0,0xc9,0x25,0xcd,0x16);
66 DEFINE_GUID(CLSID_MMDeviceEnumerator, 0xbcde0395, 0xe52f, 0x467c, 0x8e,0x3d, 0xc4,0x57,0x92,0x91,0x69,0x2e);
67 DEFINE_GUID(IID_IMMDeviceEnumerator, 0xa95664d2, 0x9614, 0x4f35, 0xa7,0x46, 0xde,0x8d,0xb6,0x36,0x17,0xe6);
68 DEFINE_GUID(IID_IAudioClient, 0x1cb9ad4c, 0xdbfa, 0x4c32, 0xb1,0x78, 0xc2,0xf5,0x68,0xa7,0x03,0xb2);
69 DEFINE_GUID(IID_IAudioRenderClient, 0xf294acfc, 0x3146, 0x4483, 0xa7,0xbf, 0xad,0xdc,0xa7,0xc2,0x60,0xe2);
70 DEFINE_GUID(IID_IAudioCaptureClient, 0xc8adbd64, 0xe71e, 0x48a0, 0xa4,0xde, 0x18,0x5c,0x39,0x5c,0xd3,0x17);
72 #ifdef HAVE_WASAPI
73 #include <wtypes.h>
74 #include <devpropdef.h>
75 #include <propkeydef.h>
76 DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80,0x20, 0x67,0xd1,0x46,0xa8,0x50,0xe0, 14);
77 DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_FormFactor, 0x1da5d803, 0xd492, 0x4edd, 0x8c,0x23, 0xe0,0xc0,0xff,0xee,0x7f,0x0e, 0);
78 DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_GUID, 0x1da5d803, 0xd492, 0x4edd, 0x8c, 0x23,0xe0, 0xc0,0xff,0xee,0x7f,0x0e, 4 );
79 #endif
80 #endif
81 #endif /* AL_NO_UID_DEFS */
83 #ifdef HAVE_DLFCN_H
84 #include <dlfcn.h>
85 #endif
86 #ifdef HAVE_INTRIN_H
87 #include <intrin.h>
88 #endif
89 #ifdef HAVE_CPUID_H
90 #include <cpuid.h>
91 #endif
92 #ifdef HAVE_SYS_SYSCONF_H
93 #include <sys/sysconf.h>
94 #endif
95 #ifdef HAVE_FLOAT_H
96 #include <float.h>
97 #endif
98 #ifdef HAVE_IEEEFP_H
99 #include <ieeefp.h>
100 #endif
102 #ifndef _WIN32
103 #include <sys/types.h>
104 #include <sys/stat.h>
105 #include <sys/mman.h>
106 #include <fcntl.h>
107 #include <unistd.h>
108 #elif defined(_WIN32_IE)
109 #include <shlobj.h>
110 #endif
112 #include "alMain.h"
113 #include "alu.h"
114 #include "cpu_caps.h"
115 #include "fpu_modes.h"
116 #include "atomic.h"
117 #include "uintmap.h"
118 #include "vector.h"
119 #include "alstring.h"
120 #include "compat.h"
121 #include "threads.h"
124 extern inline ALuint NextPowerOf2(ALuint value);
125 extern inline size_t RoundUp(size_t value, size_t r);
126 extern inline ALint fastf2i(ALfloat f);
127 extern inline int float2int(float f);
128 extern inline float fast_roundf(float f);
129 #ifndef __GNUC__
130 #if defined(HAVE_BITSCANFORWARD64_INTRINSIC)
131 extern inline int msvc64_ctz64(ALuint64 v);
132 #elif defined(HAVE_BITSCANFORWARD_INTRINSIC)
133 extern inline int msvc_ctz64(ALuint64 v);
134 #else
135 extern inline int fallback_popcnt64(ALuint64 v);
136 extern inline int fallback_ctz64(ALuint64 value);
137 #endif
138 #endif
141 #if defined(HAVE_GCC_GET_CPUID) && (defined(__i386__) || defined(__x86_64__) || \
142 defined(_M_IX86) || defined(_M_X64))
143 typedef unsigned int reg_type;
144 static inline void get_cpuid(int f, reg_type *regs)
145 { __get_cpuid(f, &regs[0], &regs[1], &regs[2], &regs[3]); }
146 #define CAN_GET_CPUID
147 #elif defined(HAVE_CPUID_INTRINSIC) && (defined(__i386__) || defined(__x86_64__) || \
148 defined(_M_IX86) || defined(_M_X64))
149 typedef int reg_type;
150 static inline void get_cpuid(int f, reg_type *regs)
151 { (__cpuid)(regs, f); }
152 #define CAN_GET_CPUID
153 #endif
155 int CPUCapFlags = 0;
157 void FillCPUCaps(int capfilter)
159 int caps = 0;
161 /* FIXME: We really should get this for all available CPUs in case different
162 * CPUs have different caps (is that possible on one machine?). */
163 #ifdef CAN_GET_CPUID
164 union {
165 reg_type regs[4];
166 char str[sizeof(reg_type[4])];
167 } cpuinf[3] = {{ { 0, 0, 0, 0 } }};
169 get_cpuid(0, cpuinf[0].regs);
170 if(cpuinf[0].regs[0] == 0)
171 ERR("Failed to get CPUID\n");
172 else
174 unsigned int maxfunc = cpuinf[0].regs[0];
175 unsigned int maxextfunc;
177 get_cpuid(0x80000000, cpuinf[0].regs);
178 maxextfunc = cpuinf[0].regs[0];
180 TRACE("Detected max CPUID function: 0x%x (ext. 0x%x)\n", maxfunc, maxextfunc);
182 TRACE("Vendor ID: \"%.4s%.4s%.4s\"\n", cpuinf[0].str+4, cpuinf[0].str+12, cpuinf[0].str+8);
183 if(maxextfunc >= 0x80000004)
185 get_cpuid(0x80000002, cpuinf[0].regs);
186 get_cpuid(0x80000003, cpuinf[1].regs);
187 get_cpuid(0x80000004, cpuinf[2].regs);
188 TRACE("Name: \"%.16s%.16s%.16s\"\n", cpuinf[0].str, cpuinf[1].str, cpuinf[2].str);
191 if(maxfunc >= 1)
193 get_cpuid(1, cpuinf[0].regs);
194 if((cpuinf[0].regs[3]&(1<<25)))
195 caps |= CPU_CAP_SSE;
196 if((caps&CPU_CAP_SSE) && (cpuinf[0].regs[3]&(1<<26)))
197 caps |= CPU_CAP_SSE2;
198 if((caps&CPU_CAP_SSE2) && (cpuinf[0].regs[2]&(1<<0)))
199 caps |= CPU_CAP_SSE3;
200 if((caps&CPU_CAP_SSE3) && (cpuinf[0].regs[2]&(1<<19)))
201 caps |= CPU_CAP_SSE4_1;
204 #else
205 /* Assume support for whatever's supported if we can't check for it */
206 #if defined(HAVE_SSE4_1)
207 #warning "Assuming SSE 4.1 run-time support!"
208 caps |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE3 | CPU_CAP_SSE4_1;
209 #elif defined(HAVE_SSE3)
210 #warning "Assuming SSE 3 run-time support!"
211 caps |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE3;
212 #elif defined(HAVE_SSE2)
213 #warning "Assuming SSE 2 run-time support!"
214 caps |= CPU_CAP_SSE | CPU_CAP_SSE2;
215 #elif defined(HAVE_SSE)
216 #warning "Assuming SSE run-time support!"
217 caps |= CPU_CAP_SSE;
218 #endif
219 #endif
220 #ifdef HAVE_NEON
221 FILE *file = fopen("/proc/cpuinfo", "rt");
222 if(!file)
223 ERR("Failed to open /proc/cpuinfo, cannot check for NEON support\n");
224 else
226 char buf[256];
227 while(fgets(buf, sizeof(buf), file) != NULL)
229 size_t len;
230 char *str;
232 if(strncmp(buf, "Features\t:", 10) != 0)
233 continue;
235 len = strlen(buf);
236 while(len > 0 && isspace(buf[len-1]))
237 buf[--len] = 0;
239 TRACE("Got features string:%s\n", buf+10);
241 str = buf;
242 while((str=strstr(str, "neon")) != NULL)
244 if(isspace(*(str-1)) && (str[4] == 0 || isspace(str[4])))
246 caps |= CPU_CAP_NEON;
247 break;
249 str++;
251 break;
254 fclose(file);
255 file = NULL;
257 #endif
259 TRACE("Extensions:%s%s%s%s%s%s\n",
260 ((capfilter&CPU_CAP_SSE) ? ((caps&CPU_CAP_SSE) ? " +SSE" : " -SSE") : ""),
261 ((capfilter&CPU_CAP_SSE2) ? ((caps&CPU_CAP_SSE2) ? " +SSE2" : " -SSE2") : ""),
262 ((capfilter&CPU_CAP_SSE3) ? ((caps&CPU_CAP_SSE3) ? " +SSE3" : " -SSE3") : ""),
263 ((capfilter&CPU_CAP_SSE4_1) ? ((caps&CPU_CAP_SSE4_1) ? " +SSE4.1" : " -SSE4.1") : ""),
264 ((capfilter&CPU_CAP_NEON) ? ((caps&CPU_CAP_NEON) ? " +NEON" : " -NEON") : ""),
265 ((!capfilter) ? " -none-" : "")
267 CPUCapFlags = caps & capfilter;
271 void SetMixerFPUMode(FPUCtl *ctl)
273 #if defined(__GNUC__) && defined(HAVE_SSE)
274 if((CPUCapFlags&CPU_CAP_SSE))
276 __asm__ __volatile__("stmxcsr %0" : "=m" (*&ctl->sse_state));
277 unsigned int sseState = ctl->sse_state;
278 sseState |= 0x8000; /* set flush-to-zero */
279 if((CPUCapFlags&CPU_CAP_SSE2))
280 sseState |= 0x0040; /* set denormals-are-zero */
281 __asm__ __volatile__("ldmxcsr %0" : : "m" (*&sseState));
284 #elif defined(HAVE___CONTROL87_2)
286 __control87_2(0, 0, &ctl->state, &ctl->sse_state);
287 _control87(_DN_FLUSH, _MCW_DN);
289 #elif defined(HAVE__CONTROLFP)
291 ctl->state = _controlfp(0, 0);
292 _controlfp(_DN_FLUSH, _MCW_DN);
293 #endif
296 void RestoreFPUMode(const FPUCtl *ctl)
298 #if defined(__GNUC__) && defined(HAVE_SSE)
299 if((CPUCapFlags&CPU_CAP_SSE))
300 __asm__ __volatile__("ldmxcsr %0" : : "m" (*&ctl->sse_state));
302 #elif defined(HAVE___CONTROL87_2)
304 int mode;
305 __control87_2(ctl->state, _MCW_DN, &mode, NULL);
306 __control87_2(ctl->sse_state, _MCW_DN, NULL, &mode);
308 #elif defined(HAVE__CONTROLFP)
310 _controlfp(ctl->state, _MCW_DN);
311 #endif
315 static int StringSortCompare(const void *str1, const void *str2)
317 return alstr_cmp(*(const_al_string*)str1, *(const_al_string*)str2);
320 #ifdef _WIN32
322 static WCHAR *strrchrW(WCHAR *str, WCHAR ch)
324 WCHAR *ret = NULL;
325 while(*str)
327 if(*str == ch)
328 ret = str;
329 ++str;
331 return ret;
334 void GetProcBinary(al_string *path, al_string *fname)
336 WCHAR *pathname, *sep;
337 DWORD pathlen;
338 DWORD len;
340 pathlen = 256;
341 pathname = malloc(pathlen * sizeof(pathname[0]));
342 while(pathlen > 0 && (len=GetModuleFileNameW(NULL, pathname, pathlen)) == pathlen)
344 free(pathname);
345 pathlen <<= 1;
346 pathname = malloc(pathlen * sizeof(pathname[0]));
348 if(len == 0)
350 free(pathname);
351 ERR("Failed to get process name: error %lu\n", GetLastError());
352 return;
355 pathname[len] = 0;
356 if((sep=strrchrW(pathname, '\\')) != NULL)
358 WCHAR *sep2 = strrchrW(sep+1, '/');
359 if(sep2) sep = sep2;
361 else
362 sep = strrchrW(pathname, '/');
364 if(sep)
366 if(path) alstr_copy_wrange(path, pathname, sep);
367 if(fname) alstr_copy_wcstr(fname, sep+1);
369 else
371 if(path) alstr_clear(path);
372 if(fname) alstr_copy_wcstr(fname, pathname);
374 free(pathname);
376 if(path && fname)
377 TRACE("Got: %s, %s\n", alstr_get_cstr(*path), alstr_get_cstr(*fname));
378 else if(path) TRACE("Got path: %s\n", alstr_get_cstr(*path));
379 else if(fname) TRACE("Got filename: %s\n", alstr_get_cstr(*fname));
383 static WCHAR *FromUTF8(const char *str)
385 WCHAR *out = NULL;
386 int len;
388 if((len=MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0)) > 0)
390 out = calloc(sizeof(WCHAR), len);
391 MultiByteToWideChar(CP_UTF8, 0, str, -1, out, len);
393 return out;
397 void *LoadLib(const char *name)
399 HANDLE hdl = NULL;
400 WCHAR *wname;
402 wname = FromUTF8(name);
403 if(!wname)
404 ERR("Failed to convert UTF-8 filename: \"%s\"\n", name);
405 else
407 hdl = LoadLibraryW(wname);
408 free(wname);
410 return hdl;
412 void CloseLib(void *handle)
413 { FreeLibrary((HANDLE)handle); }
414 void *GetSymbol(void *handle, const char *name)
416 void *ret;
418 ret = (void*)GetProcAddress((HANDLE)handle, name);
419 if(ret == NULL)
420 ERR("Failed to load %s\n", name);
421 return ret;
424 WCHAR *strdupW(const WCHAR *str)
426 const WCHAR *n;
427 WCHAR *ret;
428 size_t len;
430 n = str;
431 while(*n) n++;
432 len = n - str;
434 ret = calloc(sizeof(WCHAR), len+1);
435 if(ret != NULL)
436 memcpy(ret, str, sizeof(WCHAR)*len);
437 return ret;
440 FILE *al_fopen(const char *fname, const char *mode)
442 WCHAR *wname=NULL, *wmode=NULL;
443 FILE *file = NULL;
445 wname = FromUTF8(fname);
446 wmode = FromUTF8(mode);
447 if(!wname)
448 ERR("Failed to convert UTF-8 filename: \"%s\"\n", fname);
449 else if(!wmode)
450 ERR("Failed to convert UTF-8 mode: \"%s\"\n", mode);
451 else
452 file = _wfopen(wname, wmode);
454 free(wname);
455 free(wmode);
457 return file;
461 void al_print(const char *type, const char *func, const char *fmt, ...)
463 char str[1024];
464 WCHAR *wstr;
465 va_list ap;
467 va_start(ap, fmt);
468 vsnprintf(str, sizeof(str), fmt, ap);
469 va_end(ap);
471 str[sizeof(str)-1] = 0;
472 wstr = FromUTF8(str);
473 if(!wstr)
474 fprintf(LogFile, "AL lib: %s %s: <UTF-8 error> %s", type, func, str);
475 else
477 fprintf(LogFile, "AL lib: %s %s: %ls", type, func, wstr);
478 free(wstr);
479 wstr = NULL;
481 fflush(LogFile);
485 static inline int is_slash(int c)
486 { return (c == '\\' || c == '/'); }
488 static void DirectorySearch(const char *path, const char *ext, vector_al_string *results)
490 al_string pathstr = AL_STRING_INIT_STATIC();
491 WIN32_FIND_DATAW fdata;
492 WCHAR *wpath;
493 HANDLE hdl;
495 alstr_copy_cstr(&pathstr, path);
496 alstr_append_cstr(&pathstr, "\\*");
497 alstr_append_cstr(&pathstr, ext);
499 TRACE("Searching %s\n", alstr_get_cstr(pathstr));
501 wpath = FromUTF8(alstr_get_cstr(pathstr));
503 hdl = FindFirstFileW(wpath, &fdata);
504 if(hdl != INVALID_HANDLE_VALUE)
506 size_t base = VECTOR_SIZE(*results);
507 do {
508 al_string str = AL_STRING_INIT_STATIC();
509 alstr_copy_cstr(&str, path);
510 alstr_append_char(&str, '\\');
511 alstr_append_wcstr(&str, fdata.cFileName);
512 TRACE("Got result %s\n", alstr_get_cstr(str));
513 VECTOR_PUSH_BACK(*results, str);
514 } while(FindNextFileW(hdl, &fdata));
515 FindClose(hdl);
517 if(VECTOR_SIZE(*results) > base)
518 qsort(VECTOR_BEGIN(*results)+base, VECTOR_SIZE(*results)-base,
519 sizeof(VECTOR_FRONT(*results)), StringSortCompare);
522 free(wpath);
523 alstr_reset(&pathstr);
526 vector_al_string SearchDataFiles(const char *ext, const char *subdir)
528 static const int ids[2] = { CSIDL_APPDATA, CSIDL_COMMON_APPDATA };
529 static RefCount search_lock;
530 vector_al_string results = VECTOR_INIT_STATIC();
531 size_t i;
533 while(ATOMIC_EXCHANGE_SEQ(&search_lock, 1) == 1)
534 althrd_yield();
536 /* If the path is absolute, use it directly. */
537 if(isalpha(subdir[0]) && subdir[1] == ':' && is_slash(subdir[2]))
539 al_string path = AL_STRING_INIT_STATIC();
540 alstr_copy_cstr(&path, subdir);
541 #define FIX_SLASH(i) do { if(*(i) == '/') *(i) = '\\'; } while(0)
542 VECTOR_FOR_EACH(char, path, FIX_SLASH);
543 #undef FIX_SLASH
545 DirectorySearch(alstr_get_cstr(path), ext, &results);
547 alstr_reset(&path);
549 else if(subdir[0] == '\\' && subdir[1] == '\\' && subdir[2] == '?' && subdir[3] == '\\')
550 DirectorySearch(subdir, ext, &results);
551 else
553 al_string path = AL_STRING_INIT_STATIC();
554 WCHAR *cwdbuf;
556 /* Search the app-local directory. */
557 if((cwdbuf=_wgetenv(L"ALSOFT_LOCAL_PATH")) && *cwdbuf != '\0')
559 alstr_copy_wcstr(&path, cwdbuf);
560 if(is_slash(VECTOR_BACK(path)))
562 VECTOR_POP_BACK(path);
563 *VECTOR_END(path) = 0;
566 else if(!(cwdbuf=_wgetcwd(NULL, 0)))
567 alstr_copy_cstr(&path, ".");
568 else
570 alstr_copy_wcstr(&path, cwdbuf);
571 if(is_slash(VECTOR_BACK(path)))
573 VECTOR_POP_BACK(path);
574 *VECTOR_END(path) = 0;
576 free(cwdbuf);
578 #define FIX_SLASH(i) do { if(*(i) == '/') *(i) = '\\'; } while(0)
579 VECTOR_FOR_EACH(char, path, FIX_SLASH);
580 #undef FIX_SLASH
581 DirectorySearch(alstr_get_cstr(path), ext, &results);
583 /* Search the local and global data dirs. */
584 for(i = 0;i < COUNTOF(ids);i++)
586 WCHAR buffer[MAX_PATH];
587 if(SHGetSpecialFolderPathW(NULL, buffer, ids[i], FALSE) != FALSE)
589 alstr_copy_wcstr(&path, buffer);
590 if(!is_slash(VECTOR_BACK(path)))
591 alstr_append_char(&path, '\\');
592 alstr_append_cstr(&path, subdir);
593 #define FIX_SLASH(i) do { if(*(i) == '/') *(i) = '\\'; } while(0)
594 VECTOR_FOR_EACH(char, path, FIX_SLASH);
595 #undef FIX_SLASH
597 DirectorySearch(alstr_get_cstr(path), ext, &results);
601 alstr_reset(&path);
604 ATOMIC_STORE_SEQ(&search_lock, 0);
606 return results;
610 struct FileMapping MapFileToMem(const char *fname)
612 struct FileMapping ret = { NULL, NULL, NULL, 0 };
613 MEMORY_BASIC_INFORMATION meminfo;
614 HANDLE file, fmap;
615 WCHAR *wname;
616 void *ptr;
618 wname = FromUTF8(fname);
620 file = CreateFileW(wname, GENERIC_READ, FILE_SHARE_READ, NULL,
621 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
622 if(file == INVALID_HANDLE_VALUE)
624 ERR("Failed to open %s: %lu\n", fname, GetLastError());
625 free(wname);
626 return ret;
628 free(wname);
629 wname = NULL;
631 fmap = CreateFileMappingW(file, NULL, PAGE_READONLY, 0, 0, NULL);
632 if(!fmap)
634 ERR("Failed to create map for %s: %lu\n", fname, GetLastError());
635 CloseHandle(file);
636 return ret;
639 ptr = MapViewOfFile(fmap, FILE_MAP_READ, 0, 0, 0);
640 if(!ptr)
642 ERR("Failed to map %s: %lu\n", fname, GetLastError());
643 CloseHandle(fmap);
644 CloseHandle(file);
645 return ret;
648 if(VirtualQuery(ptr, &meminfo, sizeof(meminfo)) != sizeof(meminfo))
650 ERR("Failed to get map size for %s: %lu\n", fname, GetLastError());
651 UnmapViewOfFile(ptr);
652 CloseHandle(fmap);
653 CloseHandle(file);
654 return ret;
657 ret.file = file;
658 ret.fmap = fmap;
659 ret.ptr = ptr;
660 ret.len = meminfo.RegionSize;
661 return ret;
664 void UnmapFileMem(const struct FileMapping *mapping)
666 UnmapViewOfFile(mapping->ptr);
667 CloseHandle(mapping->fmap);
668 CloseHandle(mapping->file);
671 #else
673 void GetProcBinary(al_string *path, al_string *fname)
675 char *pathname = NULL;
676 size_t pathlen;
678 #ifdef __FreeBSD__
679 int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
680 if(sysctl(mib, 4, NULL, &pathlen, NULL, 0) == -1)
681 WARN("Failed to sysctl kern.proc.pathname: %s\n", strerror(errno));
682 else
684 pathname = malloc(pathlen + 1);
685 sysctl(mib, 4, (void*)pathname, &pathlen, NULL, 0);
686 pathname[pathlen] = 0;
688 #endif
689 #ifdef HAVE_PROC_PIDPATH
690 if(!pathname)
692 const pid_t pid = getpid();
693 char procpath[PROC_PIDPATHINFO_MAXSIZE];
694 int ret;
696 ret = proc_pidpath(pid, procpath, sizeof(procpath));
697 if(ret < 1)
699 WARN("proc_pidpath(%d, ...) failed: %s\n", pid, strerror(errno));
700 free(pathname);
701 pathname = NULL;
703 else
705 pathlen = strlen(procpath);
706 pathname = strdup(procpath);
709 #endif
710 if(!pathname)
712 const char *selfname;
713 ssize_t len;
715 pathlen = 256;
716 pathname = malloc(pathlen);
718 selfname = "/proc/self/exe";
719 len = readlink(selfname, pathname, pathlen);
720 if(len == -1 && errno == ENOENT)
722 selfname = "/proc/self/file";
723 len = readlink(selfname, pathname, pathlen);
725 if(len == -1 && errno == ENOENT)
727 selfname = "/proc/curproc/exe";
728 len = readlink(selfname, pathname, pathlen);
730 if(len == -1 && errno == ENOENT)
732 selfname = "/proc/curproc/file";
733 len = readlink(selfname, pathname, pathlen);
736 while(len > 0 && (size_t)len == pathlen)
738 free(pathname);
739 pathlen <<= 1;
740 pathname = malloc(pathlen);
741 len = readlink(selfname, pathname, pathlen);
743 if(len <= 0)
745 free(pathname);
746 WARN("Failed to readlink %s: %s\n", selfname, strerror(errno));
747 return;
750 pathname[len] = 0;
753 char *sep = strrchr(pathname, '/');
754 if(sep)
756 if(path) alstr_copy_range(path, pathname, sep);
757 if(fname) alstr_copy_cstr(fname, sep+1);
759 else
761 if(path) alstr_clear(path);
762 if(fname) alstr_copy_cstr(fname, pathname);
764 free(pathname);
766 if(path && fname)
767 TRACE("Got: %s, %s\n", alstr_get_cstr(*path), alstr_get_cstr(*fname));
768 else if(path) TRACE("Got path: %s\n", alstr_get_cstr(*path));
769 else if(fname) TRACE("Got filename: %s\n", alstr_get_cstr(*fname));
773 #ifdef HAVE_DLFCN_H
775 void *LoadLib(const char *name)
777 const char *err;
778 void *handle;
780 dlerror();
781 handle = dlopen(name, RTLD_NOW);
782 if((err=dlerror()) != NULL)
783 handle = NULL;
784 return handle;
786 void CloseLib(void *handle)
787 { dlclose(handle); }
788 void *GetSymbol(void *handle, const char *name)
790 const char *err;
791 void *sym;
793 dlerror();
794 sym = dlsym(handle, name);
795 if((err=dlerror()) != NULL)
797 WARN("Failed to load %s: %s\n", name, err);
798 sym = NULL;
800 return sym;
803 #endif /* HAVE_DLFCN_H */
805 void al_print(const char *type, const char *func, const char *fmt, ...)
807 va_list ap;
809 va_start(ap, fmt);
810 fprintf(LogFile, "AL lib: %s %s: ", type, func);
811 vfprintf(LogFile, fmt, ap);
812 va_end(ap);
814 fflush(LogFile);
818 static void DirectorySearch(const char *path, const char *ext, vector_al_string *results)
820 size_t extlen = strlen(ext);
821 DIR *dir;
823 TRACE("Searching %s for *%s\n", path, ext);
824 dir = opendir(path);
825 if(dir != NULL)
827 size_t base = VECTOR_SIZE(*results);
828 struct dirent *dirent;
829 while((dirent=readdir(dir)) != NULL)
831 al_string str;
832 size_t len;
833 if(strcmp(dirent->d_name, ".") == 0 || strcmp(dirent->d_name, "..") == 0)
834 continue;
836 len = strlen(dirent->d_name);
837 if(!(len > extlen))
838 continue;
839 if(strcasecmp(dirent->d_name+len-extlen, ext) != 0)
840 continue;
842 AL_STRING_INIT(str);
843 alstr_copy_cstr(&str, path);
844 if(VECTOR_BACK(str) != '/')
845 alstr_append_char(&str, '/');
846 alstr_append_cstr(&str, dirent->d_name);
847 TRACE("Got result %s\n", alstr_get_cstr(str));
848 VECTOR_PUSH_BACK(*results, str);
850 closedir(dir);
852 if(VECTOR_SIZE(*results) > base)
853 qsort(VECTOR_BEGIN(*results)+base, VECTOR_SIZE(*results)-base,
854 sizeof(VECTOR_FRONT(*results)), StringSortCompare);
858 vector_al_string SearchDataFiles(const char *ext, const char *subdir)
860 static RefCount search_lock;
861 vector_al_string results = VECTOR_INIT_STATIC();
863 while(ATOMIC_EXCHANGE_SEQ(&search_lock, 1) == 1)
864 althrd_yield();
866 if(subdir[0] == '/')
867 DirectorySearch(subdir, ext, &results);
868 else
870 al_string path = AL_STRING_INIT_STATIC();
871 const char *str, *next;
873 /* Search the app-local directory. */
874 if((str=getenv("ALSOFT_LOCAL_PATH")) && *str != '\0')
875 DirectorySearch(str, ext, &results);
876 else
878 size_t cwdlen = 256;
879 char *cwdbuf = malloc(cwdlen);
880 while(!getcwd(cwdbuf, cwdlen))
882 free(cwdbuf);
883 cwdbuf = NULL;
884 if(errno != ERANGE)
885 break;
886 cwdlen <<= 1;
887 cwdbuf = malloc(cwdlen);
889 if(!cwdbuf)
890 DirectorySearch(".", ext, &results);
891 else
893 DirectorySearch(cwdbuf, ext, &results);
894 free(cwdbuf);
895 cwdbuf = NULL;
899 // Search local data dir
900 if((str=getenv("XDG_DATA_HOME")) != NULL && str[0] != '\0')
902 alstr_copy_cstr(&path, str);
903 if(VECTOR_BACK(path) != '/')
904 alstr_append_char(&path, '/');
905 alstr_append_cstr(&path, subdir);
906 DirectorySearch(alstr_get_cstr(path), ext, &results);
908 else if((str=getenv("HOME")) != NULL && str[0] != '\0')
910 alstr_copy_cstr(&path, str);
911 if(VECTOR_BACK(path) == '/')
913 VECTOR_POP_BACK(path);
914 *VECTOR_END(path) = 0;
916 alstr_append_cstr(&path, "/.local/share/");
917 alstr_append_cstr(&path, subdir);
918 DirectorySearch(alstr_get_cstr(path), ext, &results);
921 // Search global data dirs
922 if((str=getenv("XDG_DATA_DIRS")) == NULL || str[0] == '\0')
923 str = "/usr/local/share/:/usr/share/";
925 next = str;
926 while((str=next) != NULL && str[0] != '\0')
928 next = strchr(str, ':');
929 if(!next)
930 alstr_copy_cstr(&path, str);
931 else
933 alstr_copy_range(&path, str, next);
934 ++next;
936 if(!alstr_empty(path))
938 if(VECTOR_BACK(path) != '/')
939 alstr_append_char(&path, '/');
940 alstr_append_cstr(&path, subdir);
942 DirectorySearch(alstr_get_cstr(path), ext, &results);
946 alstr_reset(&path);
949 ATOMIC_STORE_SEQ(&search_lock, 0);
951 return results;
955 struct FileMapping MapFileToMem(const char *fname)
957 struct FileMapping ret = { -1, NULL, 0 };
958 struct stat sbuf;
959 void *ptr;
960 int fd;
962 fd = open(fname, O_RDONLY, 0);
963 if(fd == -1)
965 ERR("Failed to open %s: (%d) %s\n", fname, errno, strerror(errno));
966 return ret;
968 if(fstat(fd, &sbuf) == -1)
970 ERR("Failed to stat %s: (%d) %s\n", fname, errno, strerror(errno));
971 close(fd);
972 return ret;
975 ptr = mmap(NULL, sbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
976 if(ptr == MAP_FAILED)
978 ERR("Failed to map %s: (%d) %s\n", fname, errno, strerror(errno));
979 close(fd);
980 return ret;
983 ret.fd = fd;
984 ret.ptr = ptr;
985 ret.len = sbuf.st_size;
986 return ret;
989 void UnmapFileMem(const struct FileMapping *mapping)
991 munmap(mapping->ptr, mapping->len);
992 close(mapping->fd);
995 #endif
998 void SetRTPriority(void)
1000 ALboolean failed = AL_FALSE;
1002 #ifdef _WIN32
1003 if(RTPrioLevel > 0)
1004 failed = !SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
1005 #elif defined(HAVE_PTHREAD_SETSCHEDPARAM) && !defined(__OpenBSD__)
1006 if(RTPrioLevel > 0)
1008 struct sched_param param;
1009 /* Use the minimum real-time priority possible for now (on Linux this
1010 * should be 1 for SCHED_RR) */
1011 param.sched_priority = sched_get_priority_min(SCHED_RR);
1012 failed = !!pthread_setschedparam(pthread_self(), SCHED_RR, &param);
1014 #else
1015 /* Real-time priority not available */
1016 failed = (RTPrioLevel>0);
1017 #endif
1018 if(failed)
1019 ERR("Failed to set priority level for thread\n");
1023 extern inline void alstr_reset(al_string *str);
1024 extern inline size_t alstr_length(const_al_string str);
1025 extern inline ALboolean alstr_empty(const_al_string str);
1026 extern inline const al_string_char_type *alstr_get_cstr(const_al_string str);
1028 void alstr_clear(al_string *str)
1030 if(!alstr_empty(*str))
1032 /* Reserve one more character than the total size of the string. This
1033 * is to ensure we have space to add a null terminator in the string
1034 * data so it can be used as a C-style string.
1036 VECTOR_RESIZE(*str, 0, 1);
1037 VECTOR_ELEM(*str, 0) = 0;
1041 static inline int alstr_compare(const al_string_char_type *str1, size_t str1len,
1042 const al_string_char_type *str2, size_t str2len)
1044 size_t complen = (str1len < str2len) ? str1len : str2len;
1045 int ret = memcmp(str1, str2, complen);
1046 if(ret == 0)
1048 if(str1len > str2len) return 1;
1049 if(str1len < str2len) return -1;
1051 return ret;
1053 int alstr_cmp(const_al_string str1, const_al_string str2)
1055 return alstr_compare(&VECTOR_FRONT(str1), alstr_length(str1),
1056 &VECTOR_FRONT(str2), alstr_length(str2));
1058 int alstr_cmp_cstr(const_al_string str1, const al_string_char_type *str2)
1060 return alstr_compare(&VECTOR_FRONT(str1), alstr_length(str1),
1061 str2, strlen(str2));
1064 void alstr_copy(al_string *str, const_al_string from)
1066 size_t len = alstr_length(from);
1067 size_t i;
1069 VECTOR_RESIZE(*str, len, len+1);
1070 for(i = 0;i < len;i++)
1071 VECTOR_ELEM(*str, i) = VECTOR_ELEM(from, i);
1072 VECTOR_ELEM(*str, i) = 0;
1075 void alstr_copy_cstr(al_string *str, const al_string_char_type *from)
1077 size_t len = strlen(from);
1078 size_t i;
1080 VECTOR_RESIZE(*str, len, len+1);
1081 for(i = 0;i < len;i++)
1082 VECTOR_ELEM(*str, i) = from[i];
1083 VECTOR_ELEM(*str, i) = 0;
1086 void alstr_copy_range(al_string *str, const al_string_char_type *from, const al_string_char_type *to)
1088 size_t len = to - from;
1089 size_t i;
1091 VECTOR_RESIZE(*str, len, len+1);
1092 for(i = 0;i < len;i++)
1093 VECTOR_ELEM(*str, i) = from[i];
1094 VECTOR_ELEM(*str, i) = 0;
1097 void alstr_append_char(al_string *str, const al_string_char_type c)
1099 size_t len = alstr_length(*str);
1100 VECTOR_RESIZE(*str, len, len+2);
1101 VECTOR_PUSH_BACK(*str, c);
1102 VECTOR_ELEM(*str, len+1) = 0;
1105 void alstr_append_cstr(al_string *str, const al_string_char_type *from)
1107 size_t len = strlen(from);
1108 if(len != 0)
1110 size_t base = alstr_length(*str);
1111 size_t i;
1113 VECTOR_RESIZE(*str, base+len, base+len+1);
1114 for(i = 0;i < len;i++)
1115 VECTOR_ELEM(*str, base+i) = from[i];
1116 VECTOR_ELEM(*str, base+i) = 0;
1120 void alstr_append_range(al_string *str, const al_string_char_type *from, const al_string_char_type *to)
1122 size_t len = to - from;
1123 if(len != 0)
1125 size_t base = alstr_length(*str);
1126 size_t i;
1128 VECTOR_RESIZE(*str, base+len, base+len+1);
1129 for(i = 0;i < len;i++)
1130 VECTOR_ELEM(*str, base+i) = from[i];
1131 VECTOR_ELEM(*str, base+i) = 0;
1135 #ifdef _WIN32
1136 void alstr_copy_wcstr(al_string *str, const wchar_t *from)
1138 int len;
1139 if((len=WideCharToMultiByte(CP_UTF8, 0, from, -1, NULL, 0, NULL, NULL)) > 0)
1141 VECTOR_RESIZE(*str, len-1, len);
1142 WideCharToMultiByte(CP_UTF8, 0, from, -1, &VECTOR_FRONT(*str), len, NULL, NULL);
1143 VECTOR_ELEM(*str, len-1) = 0;
1147 void alstr_append_wcstr(al_string *str, const wchar_t *from)
1149 int len;
1150 if((len=WideCharToMultiByte(CP_UTF8, 0, from, -1, NULL, 0, NULL, NULL)) > 0)
1152 size_t base = alstr_length(*str);
1153 VECTOR_RESIZE(*str, base+len-1, base+len);
1154 WideCharToMultiByte(CP_UTF8, 0, from, -1, &VECTOR_ELEM(*str, base), len, NULL, NULL);
1155 VECTOR_ELEM(*str, base+len-1) = 0;
1159 void alstr_copy_wrange(al_string *str, const wchar_t *from, const wchar_t *to)
1161 int len;
1162 if((len=WideCharToMultiByte(CP_UTF8, 0, from, (int)(to-from), NULL, 0, NULL, NULL)) > 0)
1164 VECTOR_RESIZE(*str, len, len+1);
1165 WideCharToMultiByte(CP_UTF8, 0, from, (int)(to-from), &VECTOR_FRONT(*str), len+1, NULL, NULL);
1166 VECTOR_ELEM(*str, len) = 0;
1170 void alstr_append_wrange(al_string *str, const wchar_t *from, const wchar_t *to)
1172 int len;
1173 if((len=WideCharToMultiByte(CP_UTF8, 0, from, (int)(to-from), NULL, 0, NULL, NULL)) > 0)
1175 size_t base = alstr_length(*str);
1176 VECTOR_RESIZE(*str, base+len, base+len+1);
1177 WideCharToMultiByte(CP_UTF8, 0, from, (int)(to-from), &VECTOR_ELEM(*str, base), len+1, NULL, NULL);
1178 VECTOR_ELEM(*str, base+len) = 0;
1181 #endif