aboutsummaryrefslogtreecommitdiffstats
path: root/Alc/helpers.c
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2015-11-12 18:12:26 +0100
committerSven Gothel <[email protected]>2015-11-12 18:12:26 +0100
commit542c4804aafe3f3879cee56d19d7353ed49ee154 (patch)
tree9c89aeda9ec5d368a1fb016422448e6418fc87db /Alc/helpers.c
parent2e8f6a1704dfa0048dbfc2f826847a4aaea3cbe8 (diff)
parent5d039309b355c350fd087a48c4b896d31871d174 (diff)
Merge branch 'UPSTREAM'
Diffstat (limited to 'Alc/helpers.c')
-rw-r--r--Alc/helpers.c851
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