Clean up some clang-tidy warnings in the router
[openal-soft.git] / router / router.cpp
blob56239afde558cd79a3b57a28e2f2406c14c9d7f4
2 #include "config.h"
4 #include "router.h"
6 #include <algorithm>
7 #include <array>
8 #include <cstdio>
9 #include <cstdlib>
10 #include <cstring>
11 #include <string>
12 #include <string_view>
13 #include <vector>
15 #include "AL/alc.h"
16 #include "AL/al.h"
18 #include "alstring.h"
19 #include "opthelpers.h"
20 #include "strutils.h"
22 #include "version.h"
25 eLogLevel LogLevel{eLogLevel::Error};
26 gsl::owner<std::FILE*> LogFile;
28 namespace {
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});
41 FreeLibrary(module);
42 return;
44 if(drv->Name == name)
46 TRACE("Skipping similarly-named module %.*ls\n", al::sizei(name), name.data());
47 FreeLibrary(module);
48 return;
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());
59 FreeLibrary(module);
60 return;
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());
71 FreeLibrary(module);
72 return;
76 DriverList.emplace_back(std::make_unique<DriverIface>(name, module));
77 DriverIface &newdrv = *DriverList.back();
79 /* Load required functions. */
80 int err = 0;
81 #define LOAD_PROC(x) do { \
82 newdrv.x = reinterpret_cast<decltype(newdrv.x)>(reinterpret_cast<void*>( \
83 GetProcAddress(module, #x))); \
84 if(!newdrv.x) \
85 { \
86 ERR("Failed to find entry point for %s in %.*ls\n", #x, al::sizei(name), name.data()); \
87 err = 1; \
88 } \
89 } while(0)
90 LOAD_PROC(alcCreateContext);
91 LOAD_PROC(alcMakeContextCurrent);
92 LOAD_PROC(alcProcessContext);
93 LOAD_PROC(alcSuspendContext);
94 LOAD_PROC(alcDestroyContext);
95 LOAD_PROC(alcGetCurrentContext);
96 LOAD_PROC(alcGetContextsDevice);
97 LOAD_PROC(alcOpenDevice);
98 LOAD_PROC(alcCloseDevice);
99 LOAD_PROC(alcGetError);
100 LOAD_PROC(alcIsExtensionPresent);
101 LOAD_PROC(alcGetProcAddress);
102 LOAD_PROC(alcGetEnumValue);
103 LOAD_PROC(alcGetString);
104 LOAD_PROC(alcGetIntegerv);
105 LOAD_PROC(alcCaptureOpenDevice);
106 LOAD_PROC(alcCaptureCloseDevice);
107 LOAD_PROC(alcCaptureStart);
108 LOAD_PROC(alcCaptureStop);
109 LOAD_PROC(alcCaptureSamples);
111 LOAD_PROC(alEnable);
112 LOAD_PROC(alDisable);
113 LOAD_PROC(alIsEnabled);
114 LOAD_PROC(alGetString);
115 LOAD_PROC(alGetBooleanv);
116 LOAD_PROC(alGetIntegerv);
117 LOAD_PROC(alGetFloatv);
118 LOAD_PROC(alGetDoublev);
119 LOAD_PROC(alGetBoolean);
120 LOAD_PROC(alGetInteger);
121 LOAD_PROC(alGetFloat);
122 LOAD_PROC(alGetDouble);
123 LOAD_PROC(alGetError);
124 LOAD_PROC(alIsExtensionPresent);
125 LOAD_PROC(alGetProcAddress);
126 LOAD_PROC(alGetEnumValue);
127 LOAD_PROC(alListenerf);
128 LOAD_PROC(alListener3f);
129 LOAD_PROC(alListenerfv);
130 LOAD_PROC(alListeneri);
131 LOAD_PROC(alListener3i);
132 LOAD_PROC(alListeneriv);
133 LOAD_PROC(alGetListenerf);
134 LOAD_PROC(alGetListener3f);
135 LOAD_PROC(alGetListenerfv);
136 LOAD_PROC(alGetListeneri);
137 LOAD_PROC(alGetListener3i);
138 LOAD_PROC(alGetListeneriv);
139 LOAD_PROC(alGenSources);
140 LOAD_PROC(alDeleteSources);
141 LOAD_PROC(alIsSource);
142 LOAD_PROC(alSourcef);
143 LOAD_PROC(alSource3f);
144 LOAD_PROC(alSourcefv);
145 LOAD_PROC(alSourcei);
146 LOAD_PROC(alSource3i);
147 LOAD_PROC(alSourceiv);
148 LOAD_PROC(alGetSourcef);
149 LOAD_PROC(alGetSource3f);
150 LOAD_PROC(alGetSourcefv);
151 LOAD_PROC(alGetSourcei);
152 LOAD_PROC(alGetSource3i);
153 LOAD_PROC(alGetSourceiv);
154 LOAD_PROC(alSourcePlayv);
155 LOAD_PROC(alSourceStopv);
156 LOAD_PROC(alSourceRewindv);
157 LOAD_PROC(alSourcePausev);
158 LOAD_PROC(alSourcePlay);
159 LOAD_PROC(alSourceStop);
160 LOAD_PROC(alSourceRewind);
161 LOAD_PROC(alSourcePause);
162 LOAD_PROC(alSourceQueueBuffers);
163 LOAD_PROC(alSourceUnqueueBuffers);
164 LOAD_PROC(alGenBuffers);
165 LOAD_PROC(alDeleteBuffers);
166 LOAD_PROC(alIsBuffer);
167 LOAD_PROC(alBufferData);
168 LOAD_PROC(alDopplerFactor);
169 LOAD_PROC(alDopplerVelocity);
170 LOAD_PROC(alSpeedOfSound);
171 LOAD_PROC(alDistanceModel);
172 if(!err)
174 std::array<ALCint,2> alc_ver{0, 0};
175 newdrv.alcGetIntegerv(nullptr, ALC_MAJOR_VERSION, 1, &alc_ver[0]);
176 newdrv.alcGetIntegerv(nullptr, ALC_MINOR_VERSION, 1, &alc_ver[1]);
177 if(newdrv.alcGetError(nullptr) == ALC_NO_ERROR)
178 newdrv.ALCVer = MAKE_ALC_VER(alc_ver[0], alc_ver[1]);
179 else
181 WARN("Failed to query ALC version for %.*ls, assuming 1.0\n", al::sizei(name),
182 name.data());
183 newdrv.ALCVer = MAKE_ALC_VER(1, 0);
186 #undef LOAD_PROC
187 #define LOAD_PROC(x) do { \
188 newdrv.x = reinterpret_cast<decltype(newdrv.x)>(reinterpret_cast<void*>( \
189 GetProcAddress(module, #x))); \
190 if(!newdrv.x) \
192 WARN("Failed to find optional entry point for %s in %.*ls\n", #x, al::sizei(name), \
193 name.data()); \
195 } while(0)
196 LOAD_PROC(alBufferf);
197 LOAD_PROC(alBuffer3f);
198 LOAD_PROC(alBufferfv);
199 LOAD_PROC(alBufferi);
200 LOAD_PROC(alBuffer3i);
201 LOAD_PROC(alBufferiv);
202 LOAD_PROC(alGetBufferf);
203 LOAD_PROC(alGetBuffer3f);
204 LOAD_PROC(alGetBufferfv);
205 LOAD_PROC(alGetBufferi);
206 LOAD_PROC(alGetBuffer3i);
207 LOAD_PROC(alGetBufferiv);
209 #undef LOAD_PROC
210 #define LOAD_PROC(x) do { \
211 newdrv.x = reinterpret_cast<decltype(newdrv.x)>( \
212 newdrv.alcGetProcAddress(nullptr, #x)); \
213 if(!newdrv.x) \
215 ERR("Failed to find entry point for %s in %.*ls\n", #x, al::sizei(name), name.data()); \
216 err = 1; \
218 } while(0)
219 if(newdrv.alcIsExtensionPresent(nullptr, "ALC_EXT_thread_local_context"))
221 LOAD_PROC(alcSetThreadContext);
222 LOAD_PROC(alcGetThreadContext);
226 if(err)
228 DriverList.pop_back();
229 return;
231 TRACE("Loaded module %p, %.*ls, ALC %d.%d\n", decltype(std::declval<void*>()){module},
232 al::sizei(name), name.data(), newdrv.ALCVer>>8, newdrv.ALCVer&255);
233 #undef LOAD_PROC
236 void SearchDrivers(const std::wstring_view path)
238 TRACE("Searching for drivers in %.*ls...\n", al::sizei(path), path.data());
239 std::wstring srchPath{path};
240 srchPath += L"\\*oal.dll";
242 WIN32_FIND_DATAW fdata{};
243 HANDLE srchHdl{FindFirstFileW(srchPath.c_str(), &fdata)};
244 if(srchHdl == INVALID_HANDLE_VALUE) return;
246 do {
247 srchPath = path;
248 srchPath += L"\\";
249 srchPath += std::data(fdata.cFileName);
250 TRACE("Found %ls\n", srchPath.c_str());
252 HMODULE mod{LoadLibraryW(srchPath.c_str())};
253 if(!mod)
254 WARN("Could not load %ls\n", srchPath.c_str());
255 else
256 AddModule(mod, std::data(fdata.cFileName));
257 } while(FindNextFileW(srchHdl, &fdata));
258 FindClose(srchHdl);
261 bool GetLoadedModuleDirectory(const WCHAR *name, std::wstring *moddir)
263 HMODULE module{nullptr};
265 if(name)
267 module = GetModuleHandleW(name);
268 if(!module) return false;
271 moddir->assign(256, '\0');
272 DWORD res{GetModuleFileNameW(module, moddir->data(), static_cast<DWORD>(moddir->size()))};
273 if(res >= moddir->size())
275 do {
276 moddir->append(256, '\0');
277 res = GetModuleFileNameW(module, moddir->data(), static_cast<DWORD>(moddir->size()));
278 } while(res >= moddir->size());
280 moddir->resize(res);
282 auto sep0 = moddir->rfind('/');
283 auto sep1 = moddir->rfind('\\');
284 if(sep0 < moddir->size() && sep1 < moddir->size())
285 moddir->resize(std::max(sep0, sep1));
286 else if(sep0 < moddir->size())
287 moddir->resize(sep0);
288 else if(sep1 < moddir->size())
289 moddir->resize(sep1);
290 else
291 moddir->resize(0);
293 return !moddir->empty();
296 void LoadDriverList()
298 if(auto list = al::getenv(L"ALROUTER_ACCEPT"))
300 std::wstring_view namelist{*list};
301 while(!namelist.empty())
303 auto seppos = namelist.find(',');
304 gAcceptList.emplace_back(namelist.substr(0, seppos));
305 if(seppos < namelist.size())
306 namelist.remove_prefix(seppos+1);
307 else
308 namelist.remove_prefix(namelist.size());
311 if(auto list = al::getenv(L"ALROUTER_REJECT"))
313 std::wstring_view namelist{*list};
314 while(!namelist.empty())
316 auto seppos = namelist.find(',');
317 gRejectList.emplace_back(namelist.substr(0, seppos));
318 if(seppos < namelist.size())
319 namelist.remove_prefix(seppos+1);
320 else
321 namelist.remove_prefix(namelist.size());
325 std::wstring dll_path;
326 if(GetLoadedModuleDirectory(L"OpenAL32.dll", &dll_path))
327 TRACE("Got DLL path %ls\n", dll_path.c_str());
329 std::wstring cwd_path;
330 if(DWORD pathlen{GetCurrentDirectoryW(0, nullptr)})
332 do {
333 cwd_path.resize(pathlen);
334 pathlen = GetCurrentDirectoryW(pathlen, cwd_path.data());
335 } while(pathlen >= cwd_path.size());
336 cwd_path.resize(pathlen);
338 if(!cwd_path.empty() && (cwd_path.back() == '\\' || cwd_path.back() == '/'))
339 cwd_path.pop_back();
340 if(!cwd_path.empty())
341 TRACE("Got current working directory %ls\n", cwd_path.c_str());
343 std::wstring proc_path;
344 if(GetLoadedModuleDirectory(nullptr, &proc_path))
345 TRACE("Got proc path %ls\n", proc_path.c_str());
347 std::wstring sys_path;
348 if(UINT pathlen{GetSystemDirectoryW(nullptr, 0)})
350 do {
351 sys_path.resize(pathlen);
352 pathlen = GetSystemDirectoryW(sys_path.data(), pathlen);
353 } while(pathlen >= sys_path.size());
354 sys_path.resize(pathlen);
356 if(!sys_path.empty() && (sys_path.back() == '\\' || sys_path.back() == '/'))
357 sys_path.pop_back();
358 if(!sys_path.empty())
359 TRACE("Got system path %ls\n", sys_path.c_str());
361 /* Don't search the DLL's path if it is the same as the current working
362 * directory, app's path, or system path (don't want to do duplicate
363 * searches, or increase the priority of the app or system path).
365 if(dll_path[0] &&
366 (!cwd_path[0] || dll_path != cwd_path) &&
367 (!proc_path[0] || dll_path != proc_path) &&
368 (!sys_path[0] || dll_path != sys_path))
369 SearchDrivers(dll_path);
370 if(cwd_path[0] &&
371 (!proc_path[0] || cwd_path != proc_path) &&
372 (!sys_path[0] || cwd_path != sys_path))
373 SearchDrivers(cwd_path);
374 if(proc_path[0] && (!sys_path[0] || proc_path != sys_path))
375 SearchDrivers(proc_path);
376 if(sys_path[0])
377 SearchDrivers(sys_path);
380 } // namespace
382 BOOL APIENTRY DllMain(HINSTANCE, DWORD reason, void*)
384 switch(reason)
386 case DLL_PROCESS_ATTACH:
387 if(auto logfname = al::getenv("ALROUTER_LOGFILE"))
389 gsl::owner<std::FILE*> f{fopen(logfname->c_str(), "w")};
390 if(f == nullptr)
391 ERR("Could not open log file: %s\n", logfname->c_str());
392 else
393 LogFile = f;
395 if(auto loglev = al::getenv("ALROUTER_LOGLEVEL"))
397 char *end = nullptr;
398 long l{strtol(loglev->c_str(), &end, 0)};
399 if(!end || *end != '\0')
400 ERR("Invalid log level value: %s\n", loglev->c_str());
401 else if(l < al::to_underlying(eLogLevel::None)
402 || l > al::to_underlying(eLogLevel::Trace))
403 ERR("Log level out of range: %s\n", loglev->c_str());
404 else
405 LogLevel = static_cast<eLogLevel>(l);
407 TRACE("Initializing router v0.1-%s %s\n", ALSOFT_GIT_COMMIT_HASH, ALSOFT_GIT_BRANCH);
408 LoadDriverList();
410 break;
412 case DLL_THREAD_ATTACH:
413 break;
414 case DLL_THREAD_DETACH:
415 break;
417 case DLL_PROCESS_DETACH:
418 DriverList.clear();
420 if(LogFile)
421 fclose(LogFile);
422 LogFile = nullptr;
424 break;
426 return TRUE;