Files
spice2x.github.io/src/spice2x/hooks/audio/asio_driver_scan.cpp
T
2026-08-16 21:23:41 -07:00

85 lines
3.1 KiB
C++

#include "asio_driver_scan.h"
#include <algorithm>
#include <windows.h>
#include "util/utils.h"
namespace hooks::audio {
static constexpr char ASIO_REG_PATH[] = "software\\asio";
static constexpr char ASIO_REG_DESC[] = "description";
// enumerate a single registry view, appending to entries while merging
// duplicates discovered in another view. Drivers are matched by name (not
// CLSID): the game's ASIO loader selects drivers by name, and some vendors
// register the same CLSID under different 32-bit/64-bit names (e.g. "XONAR
// SOUND CARD" vs "XONAR SOUND CARD(64)"), which are distinct user choices.
static void scan_view(
REGSAM wow64_flag,
bool is_64bit,
std::vector<AsioDriverScanEntry> &entries) {
HKEY hkEnum = nullptr;
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, ASIO_REG_PATH, 0,
KEY_READ | wow64_flag, &hkEnum) != ERROR_SUCCESS) {
return;
}
char key_name[256];
for (DWORD index = 0;
RegEnumKeyA(hkEnum, index, key_name, sizeof(key_name)) == ERROR_SUCCESS;
index++) {
// read description (display name), fall back to the key name.
// RegOpenKeyExA + RegQueryValueExA is used instead of RegGetValueA
// because the latter is unavailable on Windows XP.
char desc[256] = { 0 };
DWORD size = sizeof(desc);
std::string name = key_name;
HKEY hkDriver = nullptr;
if (RegOpenKeyExA(hkEnum, key_name, 0,
KEY_QUERY_VALUE | wow64_flag, &hkDriver) == ERROR_SUCCESS) {
DWORD type = 0;
if (RegQueryValueExA(hkDriver,
ASIO_REG_DESC,
nullptr,
&type,
reinterpret_cast<LPBYTE>(desc),
&size) == ERROR_SUCCESS
&& type == REG_SZ && desc[0]) {
// ensure null termination
desc[sizeof(desc) - 1] = '\0';
name = desc;
}
RegCloseKey(hkDriver);
}
// merge with an existing entry from the other view (match by name)
const std::string name_lower = strtolower(name);
auto it = std::find_if(entries.begin(), entries.end(), [&](const auto &e) {
return strtolower(e.name) == name_lower;
});
if (it == entries.end()) {
entries.push_back({ name });
it = entries.end() - 1;
}
it->found_32bit |= !is_64bit;
it->found_64bit |= is_64bit;
}
RegCloseKey(hkEnum);
}
std::vector<AsioDriverScanEntry> scan_asio_drivers() {
std::vector<AsioDriverScanEntry> entries;
// 64-bit view first so it wins ordering when present in both
scan_view(KEY_WOW64_64KEY, true, entries);
scan_view(KEY_WOW64_32KEY, false, entries);
return entries;
}
}