12 #include <string_view>
19 #include "opthelpers.h"
25 eLogLevel LogLevel
{eLogLevel::Error
};
26 gsl::owner
<std::FILE*> LogFile
;
30 std::vector
<std::wstring
> gAcceptList
;
31 std::vector
<std::wstring
> gRejectList
;
34 void AddModule(HMODULE module
, const std::wstring_view name
)
36 for(auto &drv
: DriverList
)
38 if(drv
->Module
== module
)
40 TRACE("Skipping already-loaded module %p\n", decltype(std::declval
<void*>()){module
});
46 TRACE("Skipping similarly-named module %.*ls\n", al::sizei(name
), name
.data());
51 if(!gAcceptList
.empty())
53 auto iter
= std::find_if(gAcceptList
.cbegin(), gAcceptList
.cend(),
54 [name
](const std::wstring_view accept
)
55 { return al::case_compare(name
, accept
) == 0; });
56 if(iter
== gAcceptList
.cend())
58 TRACE("%.*ls not found in ALROUTER_ACCEPT, skipping\n", al::sizei(name
), name
.data());
63 if(!gRejectList
.empty())
65 auto iter
= std::find_if(gRejectList
.cbegin(), gRejectList
.cend(),
66 [name
](const std::wstring_view accept
)
67 { return al::case_compare(name
, accept
) == 0; });
68 if(iter
!= gRejectList
.cend())
70 TRACE("%.*ls found in ALROUTER_REJECT, skipping\n", al::sizei(name
), name
.data());
76 DriverIface
&newdrv
= *DriverList
.emplace_back(std::make_unique
<DriverIface
>(name
, module
));
78 /* Load required functions. */
80 auto do_load
= [module
,name
](auto &func
, const char *fname
) -> bool
82 using func_t
= std::remove_reference_t
<decltype(func
)>;
83 auto ptr
= GetProcAddress(module
, fname
);
86 ERR("Failed to find entry point for %s in %.*ls\n", fname
, al::sizei(name
),
91 func
= reinterpret_cast<func_t
>(reinterpret_cast<void*>(ptr
));
94 #define LOAD_PROC(x) loadok &= do_load(newdrv.x, #x)
95 LOAD_PROC(alcCreateContext
);
96 LOAD_PROC(alcMakeContextCurrent
);
97 LOAD_PROC(alcProcessContext
);
98 LOAD_PROC(alcSuspendContext
);
99 LOAD_PROC(alcDestroyContext
);
100 LOAD_PROC(alcGetCurrentContext
);
101 LOAD_PROC(alcGetContextsDevice
);
102 LOAD_PROC(alcOpenDevice
);
103 LOAD_PROC(alcCloseDevice
);
104 LOAD_PROC(alcGetError
);
105 LOAD_PROC(alcIsExtensionPresent
);
106 LOAD_PROC(alcGetProcAddress
);
107 LOAD_PROC(alcGetEnumValue
);
108 LOAD_PROC(alcGetString
);
109 LOAD_PROC(alcGetIntegerv
);
110 LOAD_PROC(alcCaptureOpenDevice
);
111 LOAD_PROC(alcCaptureCloseDevice
);
112 LOAD_PROC(alcCaptureStart
);
113 LOAD_PROC(alcCaptureStop
);
114 LOAD_PROC(alcCaptureSamples
);
117 LOAD_PROC(alDisable
);
118 LOAD_PROC(alIsEnabled
);
119 LOAD_PROC(alGetString
);
120 LOAD_PROC(alGetBooleanv
);
121 LOAD_PROC(alGetIntegerv
);
122 LOAD_PROC(alGetFloatv
);
123 LOAD_PROC(alGetDoublev
);
124 LOAD_PROC(alGetBoolean
);
125 LOAD_PROC(alGetInteger
);
126 LOAD_PROC(alGetFloat
);
127 LOAD_PROC(alGetDouble
);
128 LOAD_PROC(alGetError
);
129 LOAD_PROC(alIsExtensionPresent
);
130 LOAD_PROC(alGetProcAddress
);
131 LOAD_PROC(alGetEnumValue
);
132 LOAD_PROC(alListenerf
);
133 LOAD_PROC(alListener3f
);
134 LOAD_PROC(alListenerfv
);
135 LOAD_PROC(alListeneri
);
136 LOAD_PROC(alListener3i
);
137 LOAD_PROC(alListeneriv
);
138 LOAD_PROC(alGetListenerf
);
139 LOAD_PROC(alGetListener3f
);
140 LOAD_PROC(alGetListenerfv
);
141 LOAD_PROC(alGetListeneri
);
142 LOAD_PROC(alGetListener3i
);
143 LOAD_PROC(alGetListeneriv
);
144 LOAD_PROC(alGenSources
);
145 LOAD_PROC(alDeleteSources
);
146 LOAD_PROC(alIsSource
);
147 LOAD_PROC(alSourcef
);
148 LOAD_PROC(alSource3f
);
149 LOAD_PROC(alSourcefv
);
150 LOAD_PROC(alSourcei
);
151 LOAD_PROC(alSource3i
);
152 LOAD_PROC(alSourceiv
);
153 LOAD_PROC(alGetSourcef
);
154 LOAD_PROC(alGetSource3f
);
155 LOAD_PROC(alGetSourcefv
);
156 LOAD_PROC(alGetSourcei
);
157 LOAD_PROC(alGetSource3i
);
158 LOAD_PROC(alGetSourceiv
);
159 LOAD_PROC(alSourcePlayv
);
160 LOAD_PROC(alSourceStopv
);
161 LOAD_PROC(alSourceRewindv
);
162 LOAD_PROC(alSourcePausev
);
163 LOAD_PROC(alSourcePlay
);
164 LOAD_PROC(alSourceStop
);
165 LOAD_PROC(alSourceRewind
);
166 LOAD_PROC(alSourcePause
);
167 LOAD_PROC(alSourceQueueBuffers
);
168 LOAD_PROC(alSourceUnqueueBuffers
);
169 LOAD_PROC(alGenBuffers
);
170 LOAD_PROC(alDeleteBuffers
);
171 LOAD_PROC(alIsBuffer
);
172 LOAD_PROC(alBufferData
);
173 LOAD_PROC(alDopplerFactor
);
174 LOAD_PROC(alDopplerVelocity
);
175 LOAD_PROC(alSpeedOfSound
);
176 LOAD_PROC(alDistanceModel
);
180 std::array
<ALCint
,2> alc_ver
{0, 0};
181 newdrv
.alcGetIntegerv(nullptr, ALC_MAJOR_VERSION
, 1, &alc_ver
[0]);
182 newdrv
.alcGetIntegerv(nullptr, ALC_MINOR_VERSION
, 1, &alc_ver
[1]);
183 if(newdrv
.alcGetError(nullptr) == ALC_NO_ERROR
)
184 newdrv
.ALCVer
= MAKE_ALC_VER(alc_ver
[0], alc_ver
[1]);
187 WARN("Failed to query ALC version for %.*ls, assuming 1.0\n", al::sizei(name
),
189 newdrv
.ALCVer
= MAKE_ALC_VER(1, 0);
192 auto do_load2
= [module
,name
](auto &func
, const char *fname
) -> void
194 using func_t
= std::remove_reference_t
<decltype(func
)>;
195 auto ptr
= GetProcAddress(module
, fname
);
197 WARN("Failed to find optional entry point for %s in %.*ls\n", fname
,
198 al::sizei(name
), name
.data());
200 func
= reinterpret_cast<func_t
>(reinterpret_cast<void*>(ptr
));
202 #define LOAD_PROC(x) do_load2(newdrv.x, #x)
203 LOAD_PROC(alBufferf
);
204 LOAD_PROC(alBuffer3f
);
205 LOAD_PROC(alBufferfv
);
206 LOAD_PROC(alBufferi
);
207 LOAD_PROC(alBuffer3i
);
208 LOAD_PROC(alBufferiv
);
209 LOAD_PROC(alGetBufferf
);
210 LOAD_PROC(alGetBuffer3f
);
211 LOAD_PROC(alGetBufferfv
);
212 LOAD_PROC(alGetBufferi
);
213 LOAD_PROC(alGetBuffer3i
);
214 LOAD_PROC(alGetBufferiv
);
217 auto do_load3
= [name
,&newdrv
](auto &func
, const char *fname
) -> bool
219 using func_t
= std::remove_reference_t
<decltype(func
)>;
220 auto ptr
= newdrv
.alcGetProcAddress(nullptr, fname
);
223 ERR("Failed to find entry point for %s in %.*ls\n", fname
, al::sizei(name
),
228 func
= reinterpret_cast<func_t
>(ptr
);
231 #define LOAD_PROC(x) loadok &= do_load3(newdrv.x, #x)
232 if(newdrv
.alcIsExtensionPresent(nullptr, "ALC_EXT_thread_local_context"))
234 LOAD_PROC(alcSetThreadContext
);
235 LOAD_PROC(alcGetThreadContext
);
242 DriverList
.pop_back();
245 TRACE("Loaded module %p, %.*ls, ALC %d.%d\n", decltype(std::declval
<void*>()){module
},
246 al::sizei(name
), name
.data(), newdrv
.ALCVer
>>8, newdrv
.ALCVer
&255);
249 void SearchDrivers(const std::wstring_view path
)
251 TRACE("Searching for drivers in %.*ls...\n", al::sizei(path
), path
.data());
252 std::wstring srchPath
{path
};
253 srchPath
+= L
"\\*oal.dll";
255 WIN32_FIND_DATAW fdata
{};
256 HANDLE srchHdl
{FindFirstFileW(srchPath
.c_str(), &fdata
)};
257 if(srchHdl
== INVALID_HANDLE_VALUE
) return;
262 srchPath
+= std::data(fdata
.cFileName
);
263 TRACE("Found %ls\n", srchPath
.c_str());
265 HMODULE mod
{LoadLibraryW(srchPath
.c_str())};
267 WARN("Could not load %ls\n", srchPath
.c_str());
269 AddModule(mod
, std::data(fdata
.cFileName
));
270 } while(FindNextFileW(srchHdl
, &fdata
));
274 bool GetLoadedModuleDirectory(const WCHAR
*name
, std::wstring
*moddir
)
276 HMODULE module
{nullptr};
280 module
= GetModuleHandleW(name
);
281 if(!module
) return false;
284 moddir
->assign(256, '\0');
285 DWORD res
{GetModuleFileNameW(module
, moddir
->data(), static_cast<DWORD
>(moddir
->size()))};
286 if(res
>= moddir
->size())
289 moddir
->append(256, '\0');
290 res
= GetModuleFileNameW(module
, moddir
->data(), static_cast<DWORD
>(moddir
->size()));
291 } while(res
>= moddir
->size());
295 auto sep0
= moddir
->rfind('/');
296 auto sep1
= moddir
->rfind('\\');
297 if(sep0
< moddir
->size() && sep1
< moddir
->size())
298 moddir
->resize(std::max(sep0
, sep1
));
299 else if(sep0
< moddir
->size())
300 moddir
->resize(sep0
);
301 else if(sep1
< moddir
->size())
302 moddir
->resize(sep1
);
306 return !moddir
->empty();
309 void LoadDriverList()
311 if(auto list
= al::getenv(L
"ALROUTER_ACCEPT"))
313 std::wstring_view namelist
{*list
};
314 while(!namelist
.empty())
316 auto seppos
= namelist
.find(',');
318 gAcceptList
.emplace_back(namelist
.substr(0, seppos
));
319 if(seppos
< namelist
.size())
320 namelist
.remove_prefix(seppos
+1);
322 namelist
.remove_prefix(namelist
.size());
325 if(auto list
= al::getenv(L
"ALROUTER_REJECT"))
327 std::wstring_view namelist
{*list
};
328 while(!namelist
.empty())
330 auto seppos
= namelist
.find(',');
332 gRejectList
.emplace_back(namelist
.substr(0, seppos
));
333 if(seppos
< namelist
.size())
334 namelist
.remove_prefix(seppos
+1);
336 namelist
.remove_prefix(namelist
.size());
340 std::wstring dll_path
;
341 if(GetLoadedModuleDirectory(L
"OpenAL32.dll", &dll_path
))
342 TRACE("Got DLL path %ls\n", dll_path
.c_str());
344 std::wstring cwd_path
;
345 if(DWORD pathlen
{GetCurrentDirectoryW(0, nullptr)})
348 cwd_path
.resize(pathlen
);
349 pathlen
= GetCurrentDirectoryW(pathlen
, cwd_path
.data());
350 } while(pathlen
>= cwd_path
.size());
351 cwd_path
.resize(pathlen
);
353 if(!cwd_path
.empty() && (cwd_path
.back() == '\\' || cwd_path
.back() == '/'))
355 if(!cwd_path
.empty())
356 TRACE("Got current working directory %ls\n", cwd_path
.c_str());
358 std::wstring proc_path
;
359 if(GetLoadedModuleDirectory(nullptr, &proc_path
))
360 TRACE("Got proc path %ls\n", proc_path
.c_str());
362 std::wstring sys_path
;
363 if(UINT pathlen
{GetSystemDirectoryW(nullptr, 0)})
366 sys_path
.resize(pathlen
);
367 pathlen
= GetSystemDirectoryW(sys_path
.data(), pathlen
);
368 } while(pathlen
>= sys_path
.size());
369 sys_path
.resize(pathlen
);
371 if(!sys_path
.empty() && (sys_path
.back() == '\\' || sys_path
.back() == '/'))
373 if(!sys_path
.empty())
374 TRACE("Got system path %ls\n", sys_path
.c_str());
376 /* Don't search the DLL's path if it is the same as the current working
377 * directory, app's path, or system path (don't want to do duplicate
378 * searches, or increase the priority of the app or system path).
380 if(!dll_path
.empty() &&
381 (cwd_path
.empty() || dll_path
!= cwd_path
) &&
382 (proc_path
.empty() || dll_path
!= proc_path
) &&
383 (sys_path
.empty() || dll_path
!= sys_path
))
384 SearchDrivers(dll_path
);
385 if(!cwd_path
.empty() &&
386 (proc_path
.empty() || cwd_path
!= proc_path
) &&
387 (sys_path
.empty() || cwd_path
!= sys_path
))
388 SearchDrivers(cwd_path
);
389 if(!proc_path
.empty() && (sys_path
.empty() || proc_path
!= sys_path
))
390 SearchDrivers(proc_path
);
391 if(!sys_path
.empty())
392 SearchDrivers(sys_path
);
397 BOOL APIENTRY
DllMain(HINSTANCE
, DWORD reason
, void*)
401 case DLL_PROCESS_ATTACH
:
402 if(auto logfname
= al::getenv("ALROUTER_LOGFILE"))
404 gsl::owner
<std::FILE*> f
{fopen(logfname
->c_str(), "w")};
406 ERR("Could not open log file: %s\n", logfname
->c_str());
410 if(auto loglev
= al::getenv("ALROUTER_LOGLEVEL"))
413 long l
{strtol(loglev
->c_str(), &end
, 0)};
414 if(!end
|| *end
!= '\0')
415 ERR("Invalid log level value: %s\n", loglev
->c_str());
416 else if(l
< al::to_underlying(eLogLevel::None
)
417 || l
> al::to_underlying(eLogLevel::Trace
))
418 ERR("Log level out of range: %s\n", loglev
->c_str());
420 LogLevel
= static_cast<eLogLevel
>(l
);
422 TRACE("Initializing router v0.1-%s %s\n", ALSOFT_GIT_COMMIT_HASH
, ALSOFT_GIT_BRANCH
);
427 case DLL_THREAD_ATTACH
:
429 case DLL_THREAD_DETACH
:
432 case DLL_PROCESS_DETACH
: