diff options
author | Sven Gothel <[email protected]> | 2015-11-12 18:12:26 +0100 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2015-11-12 18:12:26 +0100 |
commit | 542c4804aafe3f3879cee56d19d7353ed49ee154 (patch) | |
tree | 9c89aeda9ec5d368a1fb016422448e6418fc87db /Alc/helpers.c | |
parent | 2e8f6a1704dfa0048dbfc2f826847a4aaea3cbe8 (diff) | |
parent | 5d039309b355c350fd087a48c4b896d31871d174 (diff) |
Merge branch 'UPSTREAM'
Diffstat (limited to 'Alc/helpers.c')
-rw-r--r-- | Alc/helpers.c | 851 |
1 files changed, 735 insertions, 116 deletions
diff --git a/Alc/helpers.c b/Alc/helpers.c index e1220fd4..7b9e58e0 100644 --- a/Alc/helpers.c +++ b/Alc/helpers.c @@ -13,8 +13,8 @@ * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * Or go to http://www.gnu.org/copyleft/lgpl.html */ @@ -35,6 +35,9 @@ #ifdef HAVE_MALLOC_H #include <malloc.h> #endif +#ifdef HAVE_DIRENT_H +#include <dirent.h> +#endif #ifndef AL_NO_UID_DEFS #if defined(HAVE_GUIDDEF_H) || defined(HAVE_INITGUID_H) @@ -55,10 +58,13 @@ DEFINE_GUID(CLSID_MMDeviceEnumerator, 0xbcde0395, 0xe52f, 0x467c, 0x8e,0x3d, 0xc DEFINE_GUID(IID_IMMDeviceEnumerator, 0xa95664d2, 0x9614, 0x4f35, 0xa7,0x46, 0xde,0x8d,0xb6,0x36,0x17,0xe6); DEFINE_GUID(IID_IAudioClient, 0x1cb9ad4c, 0xdbfa, 0x4c32, 0xb1,0x78, 0xc2,0xf5,0x68,0xa7,0x03,0xb2); DEFINE_GUID(IID_IAudioRenderClient, 0xf294acfc, 0x3146, 0x4483, 0xa7,0xbf, 0xad,0xdc,0xa7,0xc2,0x60,0xe2); +DEFINE_GUID(IID_IAudioCaptureClient, 0xc8adbd64, 0xe71e, 0x48a0, 0xa4,0xde, 0x18,0x5c,0x39,0x5c,0xd3,0x17); #ifdef HAVE_MMDEVAPI #include <devpropdef.h> +#include <propkeydef.h> DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80,0x20, 0x67,0xd1,0x46,0xa8,0x50,0xe0, 14); +DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_FormFactor, 0x1da5d803, 0xd492, 0x4edd, 0x8c,0x23, 0xe0,0xc0,0xff,0xee,0x7f,0x0e, 0); #endif #endif #endif /* AL_NO_UID_DEFS */ @@ -66,6 +72,9 @@ DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, #ifdef HAVE_DLFCN_H #include <dlfcn.h> #endif +#ifdef HAVE_INTRIN_H +#include <intrin.h> +#endif #ifdef HAVE_CPUID_H #include <cpuid.h> #endif @@ -79,7 +88,9 @@ DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, #include <ieeefp.h> #endif -#ifdef _WIN32_IE +#ifndef _WIN32 +#include <unistd.h> +#elif defined(_WIN32_IE) #include <shlobj.h> #endif @@ -107,8 +118,8 @@ void FillCPUCaps(ALuint capfilter) /* FIXME: We really should get this for all available CPUs in case different * CPUs have different caps (is that possible on one machine?). */ -#if defined(HAVE_CPUID_H) && (defined(__i386__) || defined(__x86_64__) || \ - defined(_M_IX86) || defined(_M_X64)) +#if defined(HAVE_GCC_GET_CPUID) && (defined(__i386__) || defined(__x86_64__) || \ + defined(_M_IX86) || defined(_M_X64)) union { unsigned int regs[4]; char str[sizeof(unsigned int[4])]; @@ -141,36 +152,89 @@ void FillCPUCaps(ALuint capfilter) if((cpuinf[0].regs[3]&(1<<26))) { caps |= CPU_CAP_SSE2; - if((cpuinf[0].regs[2]&(1<<19))) - caps |= CPU_CAP_SSE4_1; + if((cpuinf[0].regs[2]&(1<<0))) + { + caps |= CPU_CAP_SSE3; + if((cpuinf[0].regs[2]&(1<<19))) + caps |= CPU_CAP_SSE4_1; + } } } } } -#elif defined(HAVE_WINDOWS_H) - HMODULE k32 = GetModuleHandleA("kernel32.dll"); - BOOL (WINAPI*IsProcessorFeaturePresent)(DWORD ProcessorFeature); - IsProcessorFeaturePresent = (BOOL(WINAPI*)(DWORD))GetProcAddress(k32, "IsProcessorFeaturePresent"); - if(!IsProcessorFeaturePresent) - ERR("IsProcessorFeaturePresent not available; CPU caps not detected\n"); +#elif defined(HAVE_CPUID_INTRINSIC) && (defined(__i386__) || defined(__x86_64__) || \ + defined(_M_IX86) || defined(_M_X64)) + union { + int regs[4]; + char str[sizeof(int[4])]; + } cpuinf[3]; + + (__cpuid)(cpuinf[0].regs, 0); + if(cpuinf[0].regs[0] == 0) + ERR("Failed to get CPUID\n"); else { - if(IsProcessorFeaturePresent(PF_XMMI_INSTRUCTIONS_AVAILABLE)) + unsigned int maxfunc = cpuinf[0].regs[0]; + unsigned int maxextfunc; + + (__cpuid)(cpuinf[0].regs, 0x80000000); + maxextfunc = cpuinf[0].regs[0]; + + TRACE("Detected max CPUID function: 0x%x (ext. 0x%x)\n", maxfunc, maxextfunc); + + TRACE("Vendor ID: \"%.4s%.4s%.4s\"\n", cpuinf[0].str+4, cpuinf[0].str+12, cpuinf[0].str+8); + if(maxextfunc >= 0x80000004) { - caps |= CPU_CAP_SSE; - if(IsProcessorFeaturePresent(PF_XMMI64_INSTRUCTIONS_AVAILABLE)) - caps |= CPU_CAP_SSE2; + (__cpuid)(cpuinf[0].regs, 0x80000002); + (__cpuid)(cpuinf[1].regs, 0x80000003); + (__cpuid)(cpuinf[2].regs, 0x80000004); + TRACE("Name: \"%.16s%.16s%.16s\"\n", cpuinf[0].str, cpuinf[1].str, cpuinf[2].str); + } + + if(maxfunc >= 1) + { + (__cpuid)(cpuinf[0].regs, 1); + if((cpuinf[0].regs[3]&(1<<25))) + { + caps |= CPU_CAP_SSE; + if((cpuinf[0].regs[3]&(1<<26))) + { + caps |= CPU_CAP_SSE2; + if((cpuinf[0].regs[2]&(1<<0))) + { + caps |= CPU_CAP_SSE3; + if((cpuinf[0].regs[2]&(1<<19))) + caps |= CPU_CAP_SSE4_1; + } + } + } } } +#else + /* Assume support for whatever's supported if we can't check for it */ +#if defined(HAVE_SSE4_1) +#warning "Assuming SSE 4.1 run-time support!" + caps |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE3 | CPU_CAP_SSE4_1; +#elif defined(HAVE_SSE3) +#warning "Assuming SSE 3 run-time support!" + caps |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE3; +#elif defined(HAVE_SSE2) +#warning "Assuming SSE 2 run-time support!" + caps |= CPU_CAP_SSE | CPU_CAP_SSE2; +#elif defined(HAVE_SSE) +#warning "Assuming SSE run-time support!" + caps |= CPU_CAP_SSE; +#endif #endif #ifdef HAVE_NEON /* Assume Neon support if compiled with it */ caps |= CPU_CAP_NEON; #endif - TRACE("Extensions:%s%s%s%s%s\n", + TRACE("Extensions:%s%s%s%s%s%s\n", ((capfilter&CPU_CAP_SSE) ? ((caps&CPU_CAP_SSE) ? " +SSE" : " -SSE") : ""), ((capfilter&CPU_CAP_SSE2) ? ((caps&CPU_CAP_SSE2) ? " +SSE2" : " -SSE2") : ""), + ((capfilter&CPU_CAP_SSE3) ? ((caps&CPU_CAP_SSE3) ? " +SSE3" : " -SSE3") : ""), ((capfilter&CPU_CAP_SSE4_1) ? ((caps&CPU_CAP_SSE4_1) ? " +SSE4.1" : " -SSE4.1") : ""), ((capfilter&CPU_CAP_NEON) ? ((caps&CPU_CAP_NEON) ? " +Neon" : " -Neon") : ""), ((!capfilter) ? " -none-" : "") @@ -196,7 +260,7 @@ void *al_malloc(size_t alignment, size_t size) if(ret != NULL) { *(ret++) = 0x00; - while(((ALintptrEXT)ret&(alignment-1)) != 0) + while(((ptrdiff_t)ret&(alignment-1)) != 0) *(ret++) = 0x55; } return ret; @@ -229,17 +293,13 @@ void al_free(void *ptr) } -#if (defined(HAVE___CONTROL87_2) || defined(HAVE__CONTROLFP)) && (defined(__x86_64__) || defined(_M_X64)) -/* Win64 doesn't allow us to set the precision control. */ -#undef _MCW_PC -#define _MCW_PC 0 -#endif - void SetMixerFPUMode(FPUCtl *ctl) { #ifdef HAVE_FENV_H fegetenv(STATIC_CAST(fenv_t, ctl)); #if defined(__GNUC__) && defined(HAVE_SSE) + /* FIXME: Some fegetenv implementations can get the SSE environment too? + * How to tell when it does? */ if((CPUCapFlags&CPU_CAP_SSE)) __asm__ __volatile__("stmxcsr %0" : "=m" (*&ctl->sse_state)); #endif @@ -263,7 +323,7 @@ void SetMixerFPUMode(FPUCtl *ctl) int mode; __control87_2(0, 0, &ctl->state, NULL); - __control87_2(_RC_CHOP|_PC_24, _MCW_RC|_MCW_PC, &mode, NULL); + __control87_2(_RC_CHOP, _MCW_RC, &mode, NULL); #ifdef HAVE_SSE if((CPUCapFlags&CPU_CAP_SSE)) { @@ -275,7 +335,7 @@ void SetMixerFPUMode(FPUCtl *ctl) #elif defined(HAVE__CONTROLFP) ctl->state = _controlfp(0, 0); - (void)_controlfp(_RC_CHOP|_PC_24, _MCW_RC|_MCW_PC); + (void)_controlfp(_RC_CHOP, _MCW_RC); #endif } @@ -291,7 +351,7 @@ void RestoreFPUMode(const FPUCtl *ctl) #elif defined(HAVE___CONTROL87_2) int mode; - __control87_2(ctl->state, _MCW_RC|_MCW_PC, &mode, NULL); + __control87_2(ctl->state, _MCW_RC, &mode, NULL); #ifdef HAVE_SSE if((CPUCapFlags&CPU_CAP_SSE)) __control87_2(ctl->sse_state, _MCW_RC|_MCW_DN, NULL, &mode); @@ -299,7 +359,7 @@ void RestoreFPUMode(const FPUCtl *ctl) #elif defined(HAVE__CONTROLFP) - _controlfp(ctl->state, _MCW_RC|_MCW_PC); + _controlfp(ctl->state, _MCW_RC); #endif } @@ -383,55 +443,31 @@ FILE *al_fopen(const char *fname, const char *mode) return file; } -#else - -#ifdef HAVE_DLFCN_H - -void *LoadLib(const char *name) -{ - const char *err; - void *handle; - - dlerror(); - handle = dlopen(name, RTLD_NOW); - if((err=dlerror()) != NULL) - handle = NULL; - return handle; -} -void CloseLib(void *handle) -{ dlclose(handle); } -void *GetSymbol(void *handle, const char *name) -{ - const char *err; - void *sym; - - dlerror(); - sym = dlsym(handle, name); - if((err=dlerror()) != NULL) - { - WARN("Failed to load %s: %s\n", name, err); - sym = NULL; - } - return sym; -} - -#endif -#endif - void al_print(const char *type, const char *func, const char *fmt, ...) { + char str[1024]; + WCHAR *wstr; va_list ap; va_start(ap, fmt); - fprintf(LogFile, "AL lib: %s %s: ", type, func); - vfprintf(LogFile, fmt, ap); + vsnprintf(str, sizeof(str), fmt, ap); va_end(ap); + str[sizeof(str)-1] = 0; + wstr = FromUTF8(str); + if(!wstr) + fprintf(LogFile, "AL lib: %s %s: <UTF-8 error> %s", type, func, str); + else + { + fprintf(LogFile, "AL lib: %s %s: %ls", type, func, wstr); + free(wstr); + wstr = NULL; + } fflush(LogFile); } -#ifdef _WIN32 + static inline int is_slash(int c) { return (c == '\\' || c == '/'); } @@ -439,32 +475,46 @@ FILE *OpenDataFile(const char *fname, const char *subdir) { static const int ids[2] = { CSIDL_APPDATA, CSIDL_COMMON_APPDATA }; WCHAR *wname=NULL, *wsubdir=NULL; - int i; + FILE *f; + size_t i; - /* If the path is absolute, open it directly. */ - if(fname[0] != '\0' && fname[1] == ':' && is_slash(fname[2])) + wname = FromUTF8(fname); + if(!wname) { - FILE *f; - if((f=al_fopen(fname, "rb")) != NULL) - { - TRACE("Opened %s\n", fname); - return f; - } - WARN("Could not open %s\n", fname); + ERR("Failed to convert UTF-8 filename: \"%s\"\n", fname); return NULL; } - wname = FromUTF8(fname); + /* If the path is absolute, open it directly. */ + if(wname[0] != '\0' && wname[1] == ':' && is_slash(wname[2])) + { + f = _wfopen(wname, L"rb"); + if(f) TRACE("Opened %s\n", fname); + else WARN("Could not open %s\n", fname); + free(wname); + return f; + } + + /* Try the current directory first before the data directories. */ + if((f=_wfopen(wname, L"rb")) != NULL) + { + TRACE("Opened %s\n", fname); + free(wname); + return f; + } + wsubdir = FromUTF8(subdir); - if(!wname) - ERR("Failed to convert UTF-8 filename: \"%s\"\n", fname); - else if(!wsubdir) + if(!wsubdir) + { ERR("Failed to convert UTF-8 subdir: \"%s\"\n", subdir); - else for(i = 0;i < 2;i++) + free(wname); + return NULL; + } + + for(i = 0;i < COUNTOF(ids);i++) { WCHAR buffer[PATH_MAX]; size_t len; - FILE *f; if(SHGetSpecialFolderPathW(NULL, buffer, ids[i], FALSE) == FALSE) continue; @@ -483,17 +533,347 @@ FILE *OpenDataFile(const char *fname, const char *subdir) if((f=_wfopen(buffer, L"rb")) != NULL) { - TRACE("Opened %ls\n", buffer); - return f; + al_string filepath = AL_STRING_INIT_STATIC(); + al_string_copy_wcstr(&filepath, buffer); + TRACE("Opened %s\n", al_string_get_cstr(filepath)); + al_string_deinit(&filepath); + break; } - WARN("Could not open %ls\n", buffer); } free(wname); free(wsubdir); + if(f == NULL) + WARN("Could not open %s\\%s\n", subdir, fname); + return f; +} + + +static const WCHAR *strchrW(const WCHAR *str, WCHAR ch) +{ + for(;*str != 0;++str) + { + if(*str == ch) + return str; + } return NULL; } + +static const WCHAR *strrchrW(const WCHAR *str, WCHAR ch) +{ + const WCHAR *ret = NULL; + for(;*str != 0;++str) + { + if(*str == ch) + ret = str; + } + return ret; +} + +/* Compares the filename in the find data with the match string. The match + * string may contain the "%r" marker to signifiy a sample rate (really any + * positive integer), or "%%" to signify a single '%'. + */ +static int MatchFilter(const WCHAR *match, const WIN32_FIND_DATAW *fdata) +{ + const WCHAR *name = fdata->cFileName; + int ret = 1; + + do { + const WCHAR *p = strchrW(match, '%'); + if(!p) + ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, + match, -1, name, -1) == CSTR_EQUAL; + else + { + int len = p-match; + ret = lstrlenW(name) >= len; + if(ret) + ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, + match, len, name, len) == CSTR_EQUAL; + if(ret) + { + match += len; + name += len; + + ++p; + if(*p == 'r') + { + unsigned long l = 0; + while(*name >= '0' && *name <= '9') + { + l = l*10 + (*name-'0'); + ++name; + } + ret = l > 0; + ++p; + } + } + } + + match = p; + } while(ret && match && *match); + + return ret; +} + +static void RecurseDirectorySearch(const char *path, const WCHAR *match, vector_al_string *results) +{ + WIN32_FIND_DATAW fdata; + const WCHAR *sep, *p; + HANDLE hdl; + + if(!match[0]) + return; + + /* Find the last directory separator and the next '%' marker in the match + * string. */ + sep = strrchrW(match, '\\'); + p = strchrW(match, '%'); + + /* If there's no separator, test the files in the specified path against + * the match string, and add the results. */ + if(!sep) + { + al_string pathstr = AL_STRING_INIT_STATIC(); + WCHAR *wpath; + + TRACE("Searching %s for %ls\n", path, match); + + al_string_append_cstr(&pathstr, path); + al_string_append_cstr(&pathstr, "\\*.*"); + wpath = FromUTF8(al_string_get_cstr(pathstr)); + + hdl = FindFirstFileW(wpath, &fdata); + if(hdl != INVALID_HANDLE_VALUE) + { + do { + if(MatchFilter(match, &fdata)) + { + al_string str = AL_STRING_INIT_STATIC(); + al_string_copy_cstr(&str, path); + al_string_append_char(&str, '\\'); + al_string_append_wcstr(&str, fdata.cFileName); + TRACE("Got result %s\n", al_string_get_cstr(str)); + VECTOR_PUSH_BACK(*results, str); + } + } while(FindNextFileW(hdl, &fdata)); + FindClose(hdl); + } + + free(wpath); + al_string_deinit(&pathstr); + + return; + } + + /* If there's no '%' marker, or it's after the final separator, append the + * remaining directories to the path and recurse into it with the remaining + * filename portion. */ + if(!p || p-sep >= 0) + { + al_string npath = AL_STRING_INIT_STATIC(); + al_string_append_cstr(&npath, path); + al_string_append_char(&npath, '\\'); + al_string_append_wrange(&npath, match, sep); + + TRACE("Recursing into %s with %ls\n", al_string_get_cstr(npath), sep+1); + RecurseDirectorySearch(al_string_get_cstr(npath), sep+1, results); + + al_string_deinit(&npath); + return; + } + + /* Look for the last separator before the '%' marker, and the first + * separator after it. */ + sep = strchrW(match, '\\'); + if(sep-p >= 0) sep = NULL; + for(;;) + { + const WCHAR *next = strchrW(sep?sep+1:match, '\\'); + if(next-p < 0) + { + al_string npath = AL_STRING_INIT_STATIC(); + WCHAR *nwpath, *nwmatch; + + /* Append up to the last directory before the one with a '%'. */ + al_string_copy_cstr(&npath, path); + if(sep) + { + al_string_append_char(&npath, '\\'); + al_string_append_wrange(&npath, match, sep); + } + al_string_append_cstr(&npath, "\\*.*"); + nwpath = FromUTF8(al_string_get_cstr(npath)); + + /* Take the directory name containing a '%' as a new string to + * match against. */ + if(!sep) + { + nwmatch = calloc(2, next-match+1); + memcpy(nwmatch, match, (next-match)*2); + } + else + { + nwmatch = calloc(2, next-(sep+1)+1); + memcpy(nwmatch, sep+1, (next-(sep+1))*2); + } + + /* For each matching directory name, recurse into it with the + * remaining string. */ + TRACE("Searching %s for %ls\n", al_string_get_cstr(npath), nwmatch); + hdl = FindFirstFileW(nwpath, &fdata); + if(hdl != INVALID_HANDLE_VALUE) + { + do { + if(MatchFilter(nwmatch, &fdata)) + { + al_string ndir = AL_STRING_INIT_STATIC(); + al_string_copy(&ndir, npath); + al_string_append_char(&ndir, '\\'); + al_string_append_wcstr(&ndir, fdata.cFileName); + TRACE("Recursing %s with %ls\n", al_string_get_cstr(ndir), next+1); + RecurseDirectorySearch(al_string_get_cstr(ndir), next+1, results); + al_string_deinit(&ndir); + } + } while(FindNextFileW(hdl, &fdata)); + FindClose(hdl); + } + + free(nwmatch); + free(nwpath); + al_string_deinit(&npath); + break; + } + sep = next; + } +} + +vector_al_string SearchDataFiles(const char *match, const char *subdir) +{ + static const int ids[2] = { CSIDL_APPDATA, CSIDL_COMMON_APPDATA }; + static RefCount search_lock; + vector_al_string results = VECTOR_INIT_STATIC(); + WCHAR *wmatch; + size_t i; + + while(ATOMIC_EXCHANGE(uint, &search_lock, 1) == 1) + althrd_yield(); + + wmatch = FromUTF8(match); + if(!wmatch) + { + ERR("Failed to convert UTF-8 filename: \"%s\"\n", match); + return results; + } + for(i = 0;wmatch[i];++i) + { + if(wmatch[i] == '/') + wmatch[i] = '\\'; + } + + /* If the path is absolute, use it directly. */ + if(isalpha(wmatch[0]) && wmatch[1] == ':' && is_slash(wmatch[2])) + { + char drv[3] = { (char)wmatch[0], ':', 0 }; + RecurseDirectorySearch(drv, wmatch+3, &results); + } + else if(wmatch[0] == '\\' && wmatch[1] == '\\' && wmatch[2] == '?' && wmatch[3] == '\\') + RecurseDirectorySearch("\\\\?", wmatch+4, &results); + else + { + al_string path = AL_STRING_INIT_STATIC(); + WCHAR *cwdbuf; + + /* Search the CWD. */ + if(!(cwdbuf=_wgetcwd(NULL, 0))) + al_string_copy_cstr(&path, "."); + else + { + al_string_copy_wcstr(&path, cwdbuf); + if(is_slash(VECTOR_BACK(path))) + { + VECTOR_POP_BACK(path); + *VECTOR_ITER_END(path) = 0; + } + free(cwdbuf); + } + RecurseDirectorySearch(al_string_get_cstr(path), wmatch, &results); + + /* Search the local and global data dirs. */ + for(i = 0;i < COUNTOF(ids);i++) + { + WCHAR buffer[PATH_MAX]; + if(SHGetSpecialFolderPathW(NULL, buffer, ids[i], FALSE) != FALSE) + { + al_string_copy_wcstr(&path, buffer); + if(!is_slash(VECTOR_BACK(path))) + al_string_append_char(&path, '\\'); + al_string_append_cstr(&path, subdir); +#define FIX_SLASH(i) do { if(*(i) == '/') *(i) = '\\'; } while(0) + VECTOR_FOR_EACH(char, path, FIX_SLASH); +#undef FIX_SLASH + + RecurseDirectorySearch(al_string_get_cstr(path), wmatch, &results); + } + } + + al_string_deinit(&path); + } + + free(wmatch); + ATOMIC_STORE(&search_lock, 0); + + return results; +} + #else + +#ifdef HAVE_DLFCN_H + +void *LoadLib(const char *name) +{ + const char *err; + void *handle; + + dlerror(); + handle = dlopen(name, RTLD_NOW); + if((err=dlerror()) != NULL) + handle = NULL; + return handle; +} +void CloseLib(void *handle) +{ dlclose(handle); } +void *GetSymbol(void *handle, const char *name) +{ + const char *err; + void *sym; + + dlerror(); + sym = dlsym(handle, name); + if((err=dlerror()) != NULL) + { + WARN("Failed to load %s: %s\n", name, err); + sym = NULL; + } + return sym; +} + +#endif /* HAVE_DLFCN_H */ + +void al_print(const char *type, const char *func, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + fprintf(LogFile, "AL lib: %s %s: ", type, func); + vfprintf(LogFile, fmt, ap); + va_end(ap); + + fflush(LogFile); +} + + FILE *OpenDataFile(const char *fname, const char *subdir) { char buffer[PATH_MAX] = ""; @@ -511,6 +891,12 @@ FILE *OpenDataFile(const char *fname, const char *subdir) return NULL; } + if((f=al_fopen(fname, "rb")) != NULL) + { + TRACE("Opened %s\n", fname); + return f; + } + if((str=getenv("XDG_DATA_HOME")) != NULL && str[0] != '\0') snprintf(buffer, sizeof(buffer), "%s/%s/%s", str, subdir, fname); else if((str=getenv("HOME")) != NULL && str[0] != '\0') @@ -522,7 +908,6 @@ FILE *OpenDataFile(const char *fname, const char *subdir) TRACE("Opened %s\n", buffer); return f; } - WARN("Could not open %s\n", buffer); } if((str=getenv("XDG_DATA_DIRS")) == NULL || str[0] == '\0') @@ -553,11 +938,221 @@ FILE *OpenDataFile(const char *fname, const char *subdir) TRACE("Opened %s\n", buffer); return f; } - WARN("Could not open %s\n", buffer); } + WARN("Could not open %s/%s\n", subdir, fname); return NULL; } + + +static const char *MatchString; +static int MatchFilter(const struct dirent *dir) +{ + const char *match = MatchString; + const char *name = dir->d_name; + int ret = 1; + + do { + const char *p = strchr(match, '%'); + if(!p) + ret = strcmp(match, name) == 0; + else + { + size_t len = p-match; + ret = strncmp(match, name, len) == 0; + if(ret) + { + match += len; + name += len; + + ++p; + if(*p == 'r') + { + char *end; + ret = strtoul(name, &end, 10) > 0; + if(ret) name = end; + ++p; + } + } + } + + match = p; + } while(ret && match && *match); + + return ret; +} + +static void RecurseDirectorySearch(const char *path, const char *match, vector_al_string *results) +{ + struct dirent **namelist; + char *sep, *p; + int n, i; + + if(!match[0]) + return; + + sep = strrchr(match, '/'); + p = strchr(match, '%'); + + if(!sep) + { + MatchString = match; + TRACE("Searching %s for %s\n", path?path:"/", match); + n = scandir(path?path:"/", &namelist, MatchFilter, alphasort); + if(n >= 0) + { + for(i = 0;i < n;++i) + { + al_string str = AL_STRING_INIT_STATIC(); + if(path) al_string_copy_cstr(&str, path); + al_string_append_char(&str, '/'); + al_string_append_cstr(&str, namelist[i]->d_name); + TRACE("Got result %s\n", al_string_get_cstr(str)); + VECTOR_PUSH_BACK(*results, str); + free(namelist[i]); + } + free(namelist); + } + + return; + } + + if(!p || p-sep >= 0) + { + al_string npath = AL_STRING_INIT_STATIC(); + if(path) al_string_append_cstr(&npath, path); + al_string_append_char(&npath, '/'); + al_string_append_range(&npath, match, sep); + + TRACE("Recursing into %s with %s\n", al_string_get_cstr(npath), sep+1); + RecurseDirectorySearch(al_string_get_cstr(npath), sep+1, results); + + al_string_deinit(&npath); + return; + } + + sep = strchr(match, '/'); + if(sep-p >= 0) sep = NULL; + for(;;) + { + char *next = strchr(sep?sep+1:match, '/'); + if(next-p < 0) + { + al_string npath = AL_STRING_INIT_STATIC(); + al_string nmatch = AL_STRING_INIT_STATIC(); + + if(!sep) + { + al_string_append_cstr(&npath, path?path:"/."); + MatchString = match; + } + else + { + if(path) al_string_append_cstr(&npath, path); + al_string_append_char(&npath, '/'); + al_string_append_range(&npath, match, sep); + + al_string_append_range(&nmatch, sep+1, next); + MatchString = al_string_get_cstr(nmatch); + } + + TRACE("Searching %s for %s\n", al_string_get_cstr(npath), MatchString); + n = scandir(al_string_get_cstr(npath), &namelist, MatchFilter, alphasort); + if(n >= 0) + { + al_string ndir = AL_STRING_INIT_STATIC(); + for(i = 0;i < n;++i) + { + al_string_copy(&ndir, npath); + al_string_append_char(&ndir, '/'); + al_string_append_cstr(&ndir, namelist[i]->d_name); + free(namelist[i]); + TRACE("Recursing %s with %s\n", al_string_get_cstr(ndir), next+1); + RecurseDirectorySearch(al_string_get_cstr(ndir), next+1, results); + } + al_string_deinit(&ndir); + free(namelist); + } + + al_string_deinit(&nmatch); + al_string_deinit(&npath); + break; + } + + sep = next; + } +} + +vector_al_string SearchDataFiles(const char *match, const char *subdir) +{ + static RefCount search_lock; + vector_al_string results = VECTOR_INIT_STATIC(); + + while(ATOMIC_EXCHANGE(uint, &search_lock, 1) == 1) + althrd_yield(); + + if(match[0] == '/') + RecurseDirectorySearch(NULL, match+1, &results); + else + { + al_string path = AL_STRING_INIT_STATIC(); + const char *str, *next; + char cwdbuf[PATH_MAX]; + + // Search CWD + if(!getcwd(cwdbuf, sizeof(cwdbuf))) + strcpy(cwdbuf, "."); + RecurseDirectorySearch(cwdbuf, match, &results); + + // Search local data dir + if((str=getenv("XDG_DATA_HOME")) != NULL && str[0] != '\0') + { + al_string_append_cstr(&path, str); + al_string_append_char(&path, '/'); + al_string_append_cstr(&path, subdir); + } + else if((str=getenv("HOME")) != NULL && str[0] != '\0') + { + al_string_append_cstr(&path, str); + al_string_append_cstr(&path, "/.local/share/"); + al_string_append_cstr(&path, subdir); + } + if(!al_string_empty(path)) + RecurseDirectorySearch(al_string_get_cstr(path), match, &results); + + // Search global data dirs + if((str=getenv("XDG_DATA_DIRS")) == NULL || str[0] == '\0') + str = "/usr/local/share/:/usr/share/"; + + next = str; + while((str=next) != NULL && str[0] != '\0') + { + next = strchr(str, ':'); + if(!next) + al_string_copy_cstr(&path, str); + else + { + al_string_clear(&path); + al_string_append_range(&path, str, next); + ++next; + } + if(!al_string_empty(path)) + { + al_string_append_char(&path, '/'); + al_string_append_cstr(&path, subdir); + + RecurseDirectorySearch(al_string_get_cstr(path), match, &results); + } + } + + al_string_deinit(&path); + } + + ATOMIC_STORE(&search_lock, 0); + + return results; +} + #endif @@ -586,24 +1181,19 @@ void SetRTPriority(void) } -ALboolean vector_reserve(void *ptr, size_t base_size, size_t obj_count, size_t obj_size, ALboolean exact) +ALboolean vector_reserve(char *ptr, size_t base_size, size_t obj_size, size_t obj_count, ALboolean exact) { - vector_ *vecptr = ptr; - if((size_t)(*vecptr ? (*vecptr)->Capacity : 0) < obj_count) + vector_ *vecptr = (vector_*)ptr; + if((*vecptr ? (*vecptr)->Capacity : 0) < obj_count) { - ALsizei old_size = (*vecptr ? (*vecptr)->Size : 0); + size_t old_size = (*vecptr ? (*vecptr)->Size : 0); void *temp; - /* Limit vector sizes to the greatest power-of-two value that an - * ALsizei can hold. */ - if(obj_count > (INT_MAX>>1)+1) - return AL_FALSE; - /* Use the next power-of-2 size if we don't need to allocate the exact * amount. This is preferred when regularly increasing the vector since * it means fewer reallocations. Though it means it also wastes some * memory. */ - if(exact == AL_FALSE) + if(exact == AL_FALSE && obj_count < INT_MAX) obj_count = NextPowerOf2((ALuint)obj_count); /* Need to be explicit with the caller type's base size, because it @@ -613,39 +1203,41 @@ ALboolean vector_reserve(void *ptr, size_t base_size, size_t obj_count, size_t o if(temp == NULL) return AL_FALSE; *vecptr = temp; - (*vecptr)->Capacity = (ALsizei)obj_count; + (*vecptr)->Capacity = obj_count; (*vecptr)->Size = old_size; } return AL_TRUE; } -ALboolean vector_resize(void *ptr, size_t base_size, size_t obj_count, size_t obj_size) +ALboolean vector_resize(char *ptr, size_t base_size, size_t obj_size, size_t obj_count) { - vector_ *vecptr = ptr; + vector_ *vecptr = (vector_*)ptr; if(*vecptr || obj_count > 0) { - if(!vector_reserve(vecptr, base_size, obj_count, obj_size, AL_TRUE)) + if(!vector_reserve((char*)vecptr, base_size, obj_size, obj_count, AL_TRUE)) return AL_FALSE; - (*vecptr)->Size = (ALsizei)obj_count; + (*vecptr)->Size = obj_count; } return AL_TRUE; } -ALboolean vector_insert(void *ptr, size_t base_size, size_t obj_size, void *ins_pos, const void *datstart, const void *datend) +ALboolean vector_insert(char *ptr, size_t base_size, size_t obj_size, void *ins_pos, const void *datstart, const void *datend) { - vector_ *vecptr = ptr; + vector_ *vecptr = (vector_*)ptr; if(datstart != datend) { - ptrdiff_t ins_elem = ((char*)ins_pos - ((char*)(*vecptr) + base_size)) / obj_size; + ptrdiff_t ins_elem = (*vecptr ? ((char*)ins_pos - ((char*)(*vecptr) + base_size)) : + ((char*)ins_pos - (char*)NULL)) / + obj_size; ptrdiff_t numins = ((const char*)datend - (const char*)datstart) / obj_size; assert(numins > 0); - if(INT_MAX-VECTOR_SIZE(*vecptr) <= numins || - !vector_reserve(vecptr, base_size, VECTOR_SIZE(*vecptr)+numins, obj_size, AL_TRUE)) + if((size_t)numins + VECTOR_SIZE(*vecptr) < (size_t)numins || + !vector_reserve((char*)vecptr, base_size, obj_size, VECTOR_SIZE(*vecptr)+numins, AL_TRUE)) return AL_FALSE; /* NOTE: ins_pos may have been invalidated if *vecptr moved. Use ins_elem instead. */ - if(ins_elem < (*vecptr)->Size) + if((size_t)ins_elem < (*vecptr)->Size) { memmove((char*)(*vecptr) + base_size + ((ins_elem+numins)*obj_size), (char*)(*vecptr) + base_size + ((ins_elem )*obj_size), @@ -653,13 +1245,14 @@ ALboolean vector_insert(void *ptr, size_t base_size, size_t obj_size, void *ins_ } memcpy((char*)(*vecptr) + base_size + (ins_elem*obj_size), datstart, numins*obj_size); - (*vecptr)->Size += (ALsizei)numins; + (*vecptr)->Size += numins; } return AL_TRUE; } -extern inline ALsizei al_string_length(const_al_string str); +extern inline void al_string_deinit(al_string *str); +extern inline size_t al_string_length(const_al_string str); extern inline ALboolean al_string_empty(const_al_string str); extern inline const al_string_char_type *al_string_get_cstr(const_al_string str); @@ -673,10 +1266,10 @@ void al_string_clear(al_string *str) *VECTOR_ITER_END(*str) = 0; } -static inline int al_string_compare(const al_string_char_type *str1, ALsizei str1len, - const al_string_char_type *str2, ALsizei str2len) +static inline int al_string_compare(const al_string_char_type *str1, size_t str1len, + const al_string_char_type *str2, size_t str2len) { - ALsizei complen = mini(str1len, str2len); + size_t complen = (str1len < str2len) ? str1len : str2len; int ret = memcmp(str1, str2, complen); if(ret == 0) { @@ -693,12 +1286,12 @@ int al_string_cmp(const_al_string str1, const_al_string str2) int al_string_cmp_cstr(const_al_string str1, const al_string_char_type *str2) { return al_string_compare(&VECTOR_FRONT(str1), al_string_length(str1), - str2, (ALsizei)strlen(str2)); + str2, strlen(str2)); } void al_string_copy(al_string *str, const_al_string from) { - ALsizei len = VECTOR_SIZE(from); + size_t len = al_string_length(from); VECTOR_RESERVE(*str, len+1); VECTOR_RESIZE(*str, 0); VECTOR_INSERT(*str, VECTOR_ITER_END(*str), VECTOR_ITER_BEGIN(from), VECTOR_ITER_BEGIN(from)+len); @@ -754,4 +1347,30 @@ void al_string_copy_wcstr(al_string *str, const wchar_t *from) *VECTOR_ITER_END(*str) = 0; } } + +void al_string_append_wcstr(al_string *str, const wchar_t *from) +{ + int len; + if((len=WideCharToMultiByte(CP_UTF8, 0, from, -1, NULL, 0, NULL, NULL)) > 0) + { + size_t strlen = al_string_length(*str); + VECTOR_RESERVE(*str, strlen+len); + VECTOR_RESIZE(*str, strlen+len-1); + WideCharToMultiByte(CP_UTF8, 0, from, -1, &VECTOR_FRONT(*str) + strlen, len, NULL, NULL); + *VECTOR_ITER_END(*str) = 0; + } +} + +void al_string_append_wrange(al_string *str, const wchar_t *from, const wchar_t *to) +{ + int len; + if((len=WideCharToMultiByte(CP_UTF8, 0, from, (int)(to-from), NULL, 0, NULL, NULL)) > 0) + { + size_t strlen = al_string_length(*str); + VECTOR_RESERVE(*str, strlen+len+1); + VECTOR_RESIZE(*str, strlen+len); + WideCharToMultiByte(CP_UTF8, 0, from, (int)(to-from), &VECTOR_FRONT(*str) + strlen, len+1, NULL, NULL); + *VECTOR_ITER_END(*str) = 0; + } +} #endif |