From 8bb08d6641ade00057c6a9562b065c06f36f1f83 Mon Sep 17 00:00:00 2001 From: SCPLEGION <91138653+SCPLEGION@users.noreply.github.com> Date: Tue, 7 Jul 2026 13:06:19 +0200 Subject: [PATCH 1/4] fix: resolve case-insensitive Steam library paths on Linux Steam's libraryfolders.vdf can store a library path with different casing than the actual filesystem path (e.g. /mnt/hdd vs /mnt/HDD), which caused std::filesystem::exists() to fail and BeamNG.drive to go undetected. Add TryResolveGameDir(), which retries each path segment in upper/lower case until integrity.json is found. Also add ~/.local/share/Steam/steamapps as a known Steam location (Arch/Fedora and other distros without the .steam symlink), and log which Steam install/library folder is being checked to aid debugging. --- src/Security/BeamNG.cpp | 75 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 72 insertions(+), 3 deletions(-) diff --git a/src/Security/BeamNG.cpp b/src/Security/BeamNG.cpp index 68b4d7c0..102e9513 100644 --- a/src/Security/BeamNG.cpp +++ b/src/Security/BeamNG.cpp @@ -11,6 +11,8 @@ #include #elif defined(__linux__) #include "vdf_parser.hpp" +#include +#include #include #include #include @@ -160,6 +162,54 @@ void FileList(std::vector& a, const std::string& Path) { } } } +#if defined(__linux__) +bool TryResolveGameDir(const std::string& libraryPath, std::string& outGameDir) { + namespace fs = std::filesystem; + + std::vector components; + for (const auto& part : fs::path(libraryPath)) { + components.push_back(part); + } + + auto buildAndCheck = [&](const std::vector& parts) -> bool { + fs::path candidate; + for (const auto& part : parts) { + candidate /= part; + } + std::string gameDir = candidate.string() + "/steamapps/common/BeamNG.drive/"; + if (fs::exists(gameDir + "integrity.json")) { + outGameDir = gameDir; + return true; + } + return false; + }; + + if (buildAndCheck(components)) { + return true; + } + + for (size_t i = 0; i < components.size(); ++i) { + std::string original = components[i].string(); + std::string upper = original; + std::string lower = original; + std::transform(upper.begin(), upper.end(), upper.begin(), [](unsigned char c) { return std::toupper(c); }); + std::transform(lower.begin(), lower.end(), lower.begin(), [](unsigned char c) { return std::tolower(c); }); + + for (const auto& variant : { upper, lower }) { + if (variant == original) { + continue; + } + auto modified = components; + modified[i] = variant; + if (buildAndCheck(modified)) { + return true; + } + } + } + return false; +} +#endif + void LegitimacyCheck() { #if defined(_WIN32) wchar_t* appDataPath = new wchar_t[MAX_PATH]; @@ -234,6 +284,7 @@ void LegitimacyCheck() { std::vector steamappsCommonPaths = { ".steam/root/steamapps", // default ".steam/steam/steamapps", // Legacy Steam installations + ".local/share/Steam/steamapps", // Arch Linux, Fedora, and other distros~ ".var/app/com.valvesoftware.Steam/.steam/root/steamapps", // flatpak "snap/steam/common/.local/share/Steam/steamapps" // snap }; @@ -248,6 +299,7 @@ void LegitimacyCheck() { if (std::filesystem::exists(steamappsPath)) { steamappsFolderFound = true; libraryFoldersPath = steamappsPath / "libraryfolders.vdf"; + info("Found Steam installation at: " + steamappsPath.string()); if (std::filesystem::exists(libraryFoldersPath)) { libraryFoldersPath = libraryFoldersPath; libraryFoldersFound = true; @@ -267,9 +319,26 @@ void LegitimacyCheck() { std::ifstream libraryFolders(libraryFoldersPath); auto root = tyti::vdf::read(libraryFolders); - for (auto folderInfo : root.childs) { - if ((folderInfo.second->childs["apps"]->attribs).contains("284160") && std::filesystem::exists(folderInfo.second->attribs["path"] + "/steamapps/common/BeamNG.drive/integrity.json")){ - GameDir = folderInfo.second->attribs["path"] + "/steamapps/common/BeamNG.drive/"; + for (const auto& folderInfo : root.childs) { + if (!folderInfo.second) { + continue; + } + const std::string& libraryPath = folderInfo.second->attribs["path"]; + info("Checking folder: " + libraryPath); + + auto appsChild = folderInfo.second->childs.find("apps"); + if (appsChild == folderInfo.second->childs.end() || !appsChild->second) { + continue; + } + + const auto& apps = appsChild->second->attribs; + if (apps.find("284160") == apps.end()) { + continue; + } + + std::string resolvedGameDir; + if (TryResolveGameDir(libraryPath, resolvedGameDir)) { + GameDir = resolvedGameDir; break; } } From 6547dcc44a9eae66d2a57fd7379ffeb2c12161c1 Mon Sep 17 00:00:00 2001 From: SCPLEGION <91138653+SCPLEGION@users.noreply.github.com> Date: Tue, 7 Jul 2026 13:14:31 +0200 Subject: [PATCH 2/4] Deleted debug info --- src/Security/BeamNG.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Security/BeamNG.cpp b/src/Security/BeamNG.cpp index 102e9513..ae4e5f86 100644 --- a/src/Security/BeamNG.cpp +++ b/src/Security/BeamNG.cpp @@ -284,7 +284,6 @@ void LegitimacyCheck() { std::vector steamappsCommonPaths = { ".steam/root/steamapps", // default ".steam/steam/steamapps", // Legacy Steam installations - ".local/share/Steam/steamapps", // Arch Linux, Fedora, and other distros~ ".var/app/com.valvesoftware.Steam/.steam/root/steamapps", // flatpak "snap/steam/common/.local/share/Steam/steamapps" // snap }; @@ -299,7 +298,6 @@ void LegitimacyCheck() { if (std::filesystem::exists(steamappsPath)) { steamappsFolderFound = true; libraryFoldersPath = steamappsPath / "libraryfolders.vdf"; - info("Found Steam installation at: " + steamappsPath.string()); if (std::filesystem::exists(libraryFoldersPath)) { libraryFoldersPath = libraryFoldersPath; libraryFoldersFound = true; @@ -324,7 +322,6 @@ void LegitimacyCheck() { continue; } const std::string& libraryPath = folderInfo.second->attribs["path"]; - info("Checking folder: " + libraryPath); auto appsChild = folderInfo.second->childs.find("apps"); if (appsChild == folderInfo.second->childs.end() || !appsChild->second) { From 9bf41799ebf6d3d9a1f9130311d05c0bd78873c0 Mon Sep 17 00:00:00 2001 From: SCPLEGION <91138653+SCPLEGION@users.noreply.github.com> Date: Tue, 7 Jul 2026 14:28:58 +0200 Subject: [PATCH 3/4] fixed copilot problems --- src/Security/BeamNG.cpp | 85 ++++++++++++++++++++++++----------------- 1 file changed, 51 insertions(+), 34 deletions(-) diff --git a/src/Security/BeamNG.cpp b/src/Security/BeamNG.cpp index ae4e5f86..2ad26966 100644 --- a/src/Security/BeamNG.cpp +++ b/src/Security/BeamNG.cpp @@ -163,48 +163,60 @@ void FileList(std::vector& a, const std::string& Path) { } } #if defined(__linux__) -bool TryResolveGameDir(const std::string& libraryPath, std::string& outGameDir) { - namespace fs = std::filesystem; +static bool ResolvePathComponent(const fs::path& parentDir, const std::string& want, fs::path& resolved) { + std::error_code ec; + fs::path exact = parentDir / want; + if (fs::exists(exact, ec)) { + resolved = exact; + return true; + } - std::vector components; - for (const auto& part : fs::path(libraryPath)) { - components.push_back(part); + if (!fs::is_directory(parentDir, ec)) { + return false; } - auto buildAndCheck = [&](const std::vector& parts) -> bool { - fs::path candidate; - for (const auto& part : parts) { - candidate /= part; - } - std::string gameDir = candidate.string() + "/steamapps/common/BeamNG.drive/"; - if (fs::exists(gameDir + "integrity.json")) { - outGameDir = gameDir; + std::string wantLower = want; + std::transform(wantLower.begin(), wantLower.end(), wantLower.begin(), [](unsigned char c) { return std::tolower(c); }); + + for (const auto& entry : fs::directory_iterator(parentDir, ec)) { + std::string nameLower = entry.path().filename().string(); + std::transform(nameLower.begin(), nameLower.end(), nameLower.begin(), [](unsigned char c) { return std::tolower(c); }); + if (nameLower == wantLower) { + resolved = entry.path(); return true; } - return false; - }; - - if (buildAndCheck(components)) { - return true; } + return false; +} - for (size_t i = 0; i < components.size(); ++i) { - std::string original = components[i].string(); - std::string upper = original; - std::string lower = original; - std::transform(upper.begin(), upper.end(), upper.begin(), [](unsigned char c) { return std::toupper(c); }); - std::transform(lower.begin(), lower.end(), lower.begin(), [](unsigned char c) { return std::tolower(c); }); +bool TryResolveGameDir(const std::string& libraryPath, std::string& outGameDir) { + fs::path current; + bool first = true; + for (const auto& part : fs::path(libraryPath)) { + if (first) { + current = part; + first = false; + continue; + } + fs::path resolved; + if (!ResolvePathComponent(current, part.string(), resolved)) { + return false; + } + current = resolved; + } - for (const auto& variant : { upper, lower }) { - if (variant == original) { - continue; - } - auto modified = components; - modified[i] = variant; - if (buildAndCheck(modified)) { - return true; - } + for (const std::string& want : { std::string("steamapps"), std::string("common"), std::string("BeamNG.drive") }) { + fs::path resolved; + if (!ResolvePathComponent(current, want, resolved)) { + return false; } + current = resolved; + } + + std::error_code ec; + if (fs::exists(current / "integrity.json", ec)) { + outGameDir = current.string() + "/"; + return true; } return false; } @@ -321,7 +333,11 @@ void LegitimacyCheck() { if (!folderInfo.second) { continue; } - const std::string& libraryPath = folderInfo.second->attribs["path"]; + auto pathAttrib = folderInfo.second->attribs.find("path"); + if (pathAttrib == folderInfo.second->attribs.end()) { + continue; + } + const std::string& libraryPath = pathAttrib->second; auto appsChild = folderInfo.second->childs.find("apps"); if (appsChild == folderInfo.second->childs.end() || !appsChild->second) { @@ -333,6 +349,7 @@ void LegitimacyCheck() { continue; } + debug("Checking Steam library for BeamNG.drive: " + libraryPath); std::string resolvedGameDir; if (TryResolveGameDir(libraryPath, resolvedGameDir)) { GameDir = resolvedGameDir; From 4f6f3e633a181341f97dc773f164c8a7948be426 Mon Sep 17 00:00:00 2001 From: SCPLEGION <91138653+SCPLEGION@users.noreply.github.com> Date: Tue, 7 Jul 2026 14:41:08 +0200 Subject: [PATCH 4/4] rev 1 --- src/Security/BeamNG.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Security/BeamNG.cpp b/src/Security/BeamNG.cpp index 2ad26966..e2a0a6a3 100644 --- a/src/Security/BeamNG.cpp +++ b/src/Security/BeamNG.cpp @@ -189,7 +189,11 @@ static bool ResolvePathComponent(const fs::path& parentDir, const std::string& w return false; } -bool TryResolveGameDir(const std::string& libraryPath, std::string& outGameDir) { +static bool TryResolveGameDir(const std::string& libraryPath, std::string& outGameDir) { + if (libraryPath.empty()) { + return false; + } + fs::path current; bool first = true; for (const auto& part : fs::path(libraryPath)) {