#include "lighthouse_driver_wrapper.h" #include #include #include #include #include #include #include #include #include #if defined(_WIN32) #include #include #include #pragma comment(lib, "hid.lib") #pragma comment(lib, "setupapi.lib") #define OPENVR_DLL_EXPORT extern "C" __declspec(dllexport) #define OPENVR_DLL_IMPORT extern "C" __declspec(dllimport) #define OPENVR_FNTABLE_CALLTYPE __stdcall #define HMODULE_TYPE HMODULE #define LOAD_LIBRARY(path) LoadLibraryA(path) #define GET_PROC_ADDRESS(handle, name) GetProcAddress((HMODULE)handle, name) #define FREE_LIBRARY(handle) FreeLibrary((HMODULE)handle) #define PATH_SEPARATOR "\\" #elif defined(__linux__) || defined(__APPLE__) #include #define OPENVR_DLL_EXPORT extern "C" __attribute__((visibility("default"))) #define OPENVR_DLL_IMPORT extern "C" #define OPENVR_FNTABLE_CALLTYPE #define HMODULE_TYPE void* #define LOAD_LIBRARY(path) dlopen(path, RTLD_NOW) #define GET_PROC_ADDRESS(handle, name) dlsym(handle, name) #define FREE_LIBRARY(handle) dlclose(handle) #define PATH_SEPARATOR "/" #endif namespace sauna { LighthouseDriverWrapper::LighthouseDriverWrapper() : m_pLighthouseDriverLib(nullptr) , m_pLighthouseProvider(nullptr) , m_fnCreateInterface(nullptr) , m_fnGetDriverCount(nullptr) , m_fnGetDriverName(nullptr) { } LighthouseDriverWrapper::~LighthouseDriverWrapper() { Shutdown(); } bool LighthouseDriverWrapper::Initialize() { // Create a debug log file FILE* logFile = fopen("lighthouse_init_debug.log", "w"); if (logFile) { fprintf(logFile, "=== Initialize called at %lld ===\n", (long long)time(nullptr)); } auto Log = [&](const char* format, ...) { if (logFile) { va_list args; va_start(args, format); vfprintf(logFile, format, args); fprintf(logFile, "\n"); fflush(logFile); va_end(args); } }; Log("Starting lighthouse driver initialization"); if (m_pLighthouseProvider) { Log("Provider already initialized, returning true"); if (logFile) fclose(logFile); return true; } Log("Calling LoadLighthouseDriver()"); if (!LoadLighthouseDriver()) { Log("LoadLighthouseDriver() failed"); if (logFile) fclose(logFile); return false; } Log("LoadLighthouseDriver() succeeded"); // Get the lighthouse driver provider Log("Getting lighthouse driver provider interface"); vr::EVRInitError eError = vr::VRInitError_None; m_pLighthouseProvider = static_cast( m_fnCreateInterface(vr::IServerTrackedDeviceProvider_Version, &eError)); if (!m_pLighthouseProvider || eError != vr::VRInitError_None) { Log("Failed to get lighthouse driver provider interface, error code: %d", eError); UnloadLighthouseDriver(); if (logFile) fclose(logFile); return false; } Log("Successfully got lighthouse driver provider interface"); // Initialize the lighthouse driver Log("Initializing lighthouse driver with proper context initialization"); // We've already successfully preloaded openvr_api.dll in LoadLighthouseDriver // Let's focus on initializing the driver context properly // Based on our analysis of lighthouse_console.exe, it's not using driver_lighthouse.dll at all! // Instead, it's connecting directly to the lighthouse devices using HID interfaces. Log("Implementing a direct HID approach based on lighthouse_console.exe"); // We'll need to implement HID communication directly Log("Implementing direct HID communication"); #if defined(_WIN32) // On Windows, we'll use the Windows HID API Log("Using Windows HID API"); // First, let's enumerate all HID devices to find the lighthouse devices Log("Enumerating HID devices"); // We've already included the Windows HID API headers at the top of the file // Get the GUID for HID devices GUID hidGuid; HidD_GetHidGuid(&hidGuid); // Get a handle to the device information set HDEVINFO deviceInfoSet = SetupDiGetClassDevs(&hidGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); if (deviceInfoSet == INVALID_HANDLE_VALUE) { Log("Failed to get device information set"); m_pLighthouseProvider = nullptr; UnloadLighthouseDriver(); if (logFile) fclose(logFile); return false; } // Enumerate all HID devices SP_DEVICE_INTERFACE_DATA deviceInterfaceData; deviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); // We'll store the lighthouse devices we find std::vector lighthouseDevices; // Enumerate all HID devices for (DWORD deviceIndex = 0; SetupDiEnumDeviceInterfaces(deviceInfoSet, NULL, &hidGuid, deviceIndex, &deviceInterfaceData); deviceIndex++) { // Get the device path DWORD requiredSize = 0; SetupDiGetDeviceInterfaceDetail(deviceInfoSet, &deviceInterfaceData, NULL, 0, &requiredSize, NULL); PSP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(requiredSize); deviceInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); if (SetupDiGetDeviceInterfaceDetail(deviceInfoSet, &deviceInterfaceData, deviceInterfaceDetailData, requiredSize, NULL, NULL)) { // Get the device path std::string devicePath = ""; // Convert the device path to a string if (deviceInterfaceDetailData->DevicePath) { // Convert to a narrow string for logging char devicePathA[256] = {0}; WideCharToMultiByte(CP_ACP, 0, deviceInterfaceDetailData->DevicePath, -1, devicePathA, sizeof(devicePathA), NULL, NULL); devicePath = devicePathA; Log("Device path: %s", devicePath.c_str()); } // Open the device using CreateFileA HANDLE deviceHandle = CreateFileA(devicePath.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (deviceHandle != INVALID_HANDLE_VALUE) { // Get the device attributes HIDD_ATTRIBUTES deviceAttributes; deviceAttributes.Size = sizeof(HIDD_ATTRIBUTES); if (HidD_GetAttributes(deviceHandle, &deviceAttributes)) { // Check if this is a lighthouse device if (deviceAttributes.VendorID == 0x28DE) { // This is a Valve device Log("Found Valve device: VID=0x%04X, PID=0x%04X, Path=%s", deviceAttributes.VendorID, deviceAttributes.ProductID, devicePath.c_str()); // Get the product string wchar_t productString[256] = {0}; if (HidD_GetProductString(deviceHandle, productString, sizeof(productString))) { // Convert to ASCII char productStringA[256] = {0}; WideCharToMultiByte(CP_ACP, 0, productString, -1, productStringA, sizeof(productStringA), NULL, NULL); Log("Product String: %s", productStringA); // Check if this is a lighthouse device if (strstr(productStringA, "Lighthouse") != NULL || strstr(productStringA, "LHR-") != NULL) { Log("Found lighthouse device: %s", productStringA); // Store the device path as a string lighthouseDevices.push_back(devicePath); } } } } CloseHandle(deviceHandle); } } free(deviceInterfaceDetailData); } // Clean up SetupDiDestroyDeviceInfoList(deviceInfoSet); // Check if we found any lighthouse devices if (lighthouseDevices.empty()) { Log("No lighthouse devices found"); m_pLighthouseProvider = nullptr; UnloadLighthouseDriver(); if (logFile) fclose(logFile); return false; } Log("Found %d lighthouse devices", lighthouseDevices.size()); // For now, we'll just return success // In a real implementation, we would open the devices and start reading IMU data Log("Successfully found lighthouse devices"); // We don't need the lighthouse driver anymore UnloadLighthouseDriver(); m_pLighthouseProvider = nullptr; // Return success if (logFile) fclose(logFile); return true; #else // On other platforms, we'll need to implement a different approach Log("HID communication not implemented for this platform"); m_pLighthouseProvider = nullptr; UnloadLighthouseDriver(); if (logFile) fclose(logFile); return false; #endif } void LighthouseDriverWrapper::Shutdown() { if (m_pLighthouseProvider) { m_pLighthouseProvider->Cleanup(); m_pLighthouseProvider = nullptr; } UnloadLighthouseDriver(); } void LighthouseDriverWrapper::RunFrame() { // Create a debug log file FILE* logFile = fopen("lighthouse_runframe_debug.log", "a"); if (logFile) { fprintf(logFile, "RunFrame called at %lld\n", (long long)time(nullptr)); } if (m_pLighthouseProvider) { if (logFile) { fprintf(logFile, "Calling m_pLighthouseProvider->RunFrame()\n"); fflush(logFile); } // Since we skipped initialization, this might not work properly // But we'll try it anyway try { m_pLighthouseProvider->RunFrame(); if (logFile) { fprintf(logFile, "RunFrame completed successfully\n"); fflush(logFile); } } catch (...) { if (logFile) { fprintf(logFile, "Exception in RunFrame\n"); fflush(logFile); } } } else { if (logFile) { fprintf(logFile, "m_pLighthouseProvider is null\n"); fflush(logFile); } } if (logFile) { fclose(logFile); } } void LighthouseDriverWrapper::EnterStandby() { // Create a debug log file FILE* logFile = fopen("lighthouse_standby_debug.log", "a"); if (logFile) { fprintf(logFile, "EnterStandby called at %lld\n", (long long)time(nullptr)); } if (m_pLighthouseProvider) { if (logFile) { fprintf(logFile, "Calling m_pLighthouseProvider->EnterStandby()\n"); fflush(logFile); } try { m_pLighthouseProvider->EnterStandby(); if (logFile) { fprintf(logFile, "EnterStandby completed successfully\n"); fflush(logFile); } } catch (...) { if (logFile) { fprintf(logFile, "Exception in EnterStandby\n"); fflush(logFile); } } } else { if (logFile) { fprintf(logFile, "m_pLighthouseProvider is null\n"); fflush(logFile); } } if (logFile) { fclose(logFile); } } void LighthouseDriverWrapper::LeaveStandby() { // Create a debug log file FILE* logFile = fopen("lighthouse_standby_debug.log", "a"); if (logFile) { fprintf(logFile, "LeaveStandby called at %lld\n", (long long)time(nullptr)); } if (m_pLighthouseProvider) { if (logFile) { fprintf(logFile, "Calling m_pLighthouseProvider->LeaveStandby()\n"); fflush(logFile); } try { m_pLighthouseProvider->LeaveStandby(); if (logFile) { fprintf(logFile, "LeaveStandby completed successfully\n"); fflush(logFile); } } catch (...) { if (logFile) { fprintf(logFile, "Exception in LeaveStandby\n"); fflush(logFile); } } } else { if (logFile) { fprintf(logFile, "m_pLighthouseProvider is null\n"); fflush(logFile); } } if (logFile) { fclose(logFile); } } bool LighthouseDriverWrapper::LoadLighthouseDriver() { // Create a direct debug log file FILE* logFile = fopen("lighthouse_driver_debug.log", "w"); if (!logFile) { return false; } fprintf(logFile, "=== LoadLighthouseDriver called at %lld ===\n", (long long)time(nullptr)); fflush(logFile); // Define a simple logging function auto Log = [&](const char* format, ...) { va_list args; va_start(args, format); vfprintf(logFile, format, args); fprintf(logFile, "\n"); fflush(logFile); va_end(args); }; Log("Attempting to load lighthouse driver directly"); // Clear the driver path m_strDriverPath = ""; // Define a simple set of paths to try - focus on the most common locations std::vector paths; #if defined(_WIN32) // Windows paths - we know this one exists from the test output paths.push_back("C:\\Program Files (x86)\\Steam\\steamapps\\common\\SteamVR\\drivers\\lighthouse\\bin\\win64\\driver_lighthouse.dll"); // Add other potential paths as fallbacks paths.push_back("C:\\Program Files\\Steam\\steamapps\\common\\SteamVR\\drivers\\lighthouse\\bin\\win64\\driver_lighthouse.dll"); // Get user profile path char* userProfile = getenv("USERPROFILE"); if (userProfile) { std::string userPath = userProfile; paths.push_back(userPath + "\\AppData\\Local\\OpenVR\\drivers\\lighthouse\\bin\\win64\\driver_lighthouse.dll"); } #elif defined(__linux__) // Linux paths paths.push_back("/usr/share/steam/steamapps/common/SteamVR/drivers/lighthouse/bin/linux64/driver_lighthouse.so"); paths.push_back("~/.steam/steam/steamapps/common/SteamVR/drivers/lighthouse/bin/linux64/driver_lighthouse.so"); #elif defined(__APPLE__) // macOS paths paths.push_back("~/Library/Application Support/Steam/steamapps/common/SteamVR/drivers/lighthouse/bin/osx64/driver_lighthouse.dylib"); #endif // Try each path for (const auto& path : paths) { Log("Trying path: %s", path.c_str()); // Set the current path m_strDriverPath = path; // Check if file exists FILE* file = fopen(path.c_str(), "rb"); if (!file) { Log("File does not exist: %s", path.c_str()); continue; } // Get file size fseek(file, 0, SEEK_END); long size = ftell(file); fclose(file); Log("File exists with size: %ld bytes", size); #if defined(_WIN32) // On Windows, add the directory containing the DLL to the DLL search path std::string directory = path.substr(0, path.find_last_of('\\')); Log("Adding directory to DLL search path: %s", directory.c_str()); // Save the current directory char currentDir[MAX_PATH]; GetCurrentDirectoryA(MAX_PATH, currentDir); // Set the directory containing the DLL as the current directory SetCurrentDirectoryA(directory.c_str()); // Try to preload common dependencies Log("Preloading common dependencies..."); // Look for openvr_api.dll in various locations std::vector apiPaths; apiPaths.push_back(directory + "\\openvr_api.dll"); // Same directory as driver apiPaths.push_back("C:\\Program Files (x86)\\Steam\\steamapps\\common\\SteamVR\\bin\\win64\\openvr_api.dll"); apiPaths.push_back("C:\\Program Files\\Steam\\steamapps\\common\\SteamVR\\bin\\win64\\openvr_api.dll"); bool foundApi = false; for (const auto& apiPath : apiPaths) { Log("Checking for openvr_api.dll at: %s", apiPath.c_str()); FILE* apiFile = fopen(apiPath.c_str(), "rb"); if (apiFile) { fclose(apiFile); Log("Found openvr_api.dll at: %s", apiPath.c_str()); // Try to load it HMODULE hDep = LoadLibraryA(apiPath.c_str()); if (hDep) { Log(" Successfully preloaded openvr_api.dll from %s", apiPath.c_str()); foundApi = true; // Keep the dependency loaded break; } else { DWORD depError = GetLastError(); Log(" Failed to preload openvr_api.dll from %s (Error %d)", apiPath.c_str(), depError); } } } if (!foundApi) { Log("Could not find or load openvr_api.dll in any location"); } #endif // Try to load the library Log("Loading library..."); // If we found the API DLL but couldn't load it, try copying it to our directory if (!foundApi) { for (const auto& apiPath : apiPaths) { FILE* apiFile = fopen(apiPath.c_str(), "rb"); if (apiFile) { // Read the file fseek(apiFile, 0, SEEK_END); long size = ftell(apiFile); fseek(apiFile, 0, SEEK_SET); std::vector buffer(size); if (fread(buffer.data(), 1, size, apiFile) == size) { fclose(apiFile); // Write to our directory FILE* outFile = fopen("openvr_api.dll", "wb"); if (outFile) { if (fwrite(buffer.data(), 1, size, outFile) == size) { fclose(outFile); Log("Copied openvr_api.dll to current directory"); // Try to load it HMODULE hDep = LoadLibraryA("openvr_api.dll"); if (hDep) { Log(" Successfully loaded openvr_api.dll from current directory"); foundApi = true; break; } } else { fclose(outFile); } } } else { fclose(apiFile); } } } } m_pLighthouseDriverLib = LOAD_LIBRARY(path.c_str()); #if defined(_WIN32) // Restore the current directory SetCurrentDirectoryA(currentDir); #endif if (!m_pLighthouseDriverLib) { #if defined(_WIN32) DWORD error = GetLastError(); Log("Failed to load library. Error code: %d", error); // Get the error message char errorMsg[256] = {0}; FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), errorMsg, sizeof(errorMsg), NULL ); Log("Error message: %s", errorMsg); #else Log("Failed to load library"); #endif continue; } Log("Library loaded successfully"); // Get the factory function Log("Getting HmdDriverFactory function..."); m_fnCreateInterface = reinterpret_cast( GET_PROC_ADDRESS(m_pLighthouseDriverLib, "HmdDriverFactory")); if (!m_fnCreateInterface) { #if defined(_WIN32) DWORD error = GetLastError(); Log("Failed to get HmdDriverFactory function. Error code: %d", error); #else Log("Failed to get HmdDriverFactory function"); #endif UnloadLighthouseDriver(); continue; } Log("HmdDriverFactory function found"); Log("Lighthouse driver loaded successfully from: %s", path.c_str()); fclose(logFile); return true; } Log("Failed to load lighthouse driver from any location"); fclose(logFile); return false; } void LighthouseDriverWrapper::UnloadLighthouseDriver() { if (m_pLighthouseDriverLib) { FREE_LIBRARY(m_pLighthouseDriverLib); m_pLighthouseDriverLib = nullptr; } m_fnCreateInterface = nullptr; m_fnGetDriverCount = nullptr; m_fnGetDriverName = nullptr; } } // namespace sauna