User folder parsing updates (#208)

This PR adds support for the changes made to the BeamNG userfolder path
location.
1. Registry check for userfolder removed
2. New ini file is checked for userfolder
3. Ini parser now supports global keys

---

By creating this pull request, I understand that code that is AI
generated or otherwise automatically generated may be rejected without
further discussion.
I declare that I fully understand all code I pushed into this PR, and
wrote all this code myself and own the rights to this code.
This commit is contained in:
Tixx
2025-09-16 16:36:07 +02:00
committed by GitHub
2 changed files with 54 additions and 30 deletions

View File

@@ -14,6 +14,7 @@
#include <openssl/evp.h>
#include <regex>
#include <string>
#include <variant>
#include <vector>
#ifdef _WIN32
@@ -108,8 +109,8 @@ namespace Utils {
return result;
}
#endif
inline std::map<std::string, std::map<std::string, std::string>> ParseINI(const std::string& contents) {
std::map<std::string, std::map<std::string, std::string>> ini;
inline std::map<std::string, std::variant<std::map<std::string, std::string>, std::string>> ParseINI(const std::string& contents) {
std::map<std::string, std::variant<std::map<std::string, std::string>, std::string>> ini;
std::string currentSection;
auto sections = Split(contents, "\n");
@@ -136,18 +137,19 @@ namespace Utils {
if (line[0] == '[') {
currentSection = line.substr(1, line.find(']') - 1);
} else {
if (currentSection.empty()) {
invalidLineLog();
continue;
}
std::string key, value;
size_t pos = line.find('=');
if (pos != std::string::npos) {
key = line.substr(0, pos);
key = key.substr(0, key.find_last_not_of(" \t") + 1);
value = line.substr(pos + 1);
ini[currentSection][key] = value;
if (currentSection.empty()) {
ini[key] = value;
} else {
std::get<std::map<std::string, std::string>>(ini[currentSection])[key] = value;
}
} else {
invalidLineLog();
continue;
@@ -157,7 +159,7 @@ namespace Utils {
value.erase(0, value.find_first_not_of(" \t"));
value.erase(value.find_last_not_of(" \t") + 1);
ini[currentSection][key] = value;
std::get<std::map<std::string, std::string>>(ini[currentSection])[key] = value;
}
}

View File

@@ -54,10 +54,10 @@ std::filesystem::path GetGamePath() {
debug("Successfully parsed startup.ini");
std::wstring userPath;
if (ini.contains("filesystem") && ini["filesystem"].contains("UserPath"))
userPath = Utils::ToWString(ini["filesystem"]["UserPath"]);
if (ini.contains("filesystem") && std::get<std::map<std::string, std::string>>(ini["filesystem"]).contains("UserPath"))
userPath = Utils::ToWString(std::get<std::map<std::string, std::string>>(ini["filesystem"])["UserPath"]);
if (!userPath.empty())
if (!userPath.empty() && Path.empty())
if (userPath = Utils::ExpandEnvVars(userPath); std::filesystem::exists(userPath)) {
Path = userPath;
debug(L"Using custom user folder path from startup.ini: " + Path.wstring());
@@ -66,32 +66,54 @@ std::filesystem::path GetGamePath() {
}
if (Path.empty()) {
HKEY hKey;
LPCTSTR sk = "Software\\BeamNG\\BeamNG.drive";
LONG openRes = RegOpenKeyEx(HKEY_CURRENT_USER, sk, 0, KEY_ALL_ACCESS, &hKey);
if (openRes != ERROR_SUCCESS) {
fatal("Please launch the game at least once!");
wchar_t* appDataPath = new wchar_t[MAX_PATH];
HRESULT result = SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, appDataPath);
if (!SUCCEEDED(result)) {
fatal("Cannot get Local Appdata directory");
}
auto BeamNGAppdataPath = std::filesystem::path(appDataPath) / "BeamNG";
if (const auto beamngIniPath = BeamNGAppdataPath / "BeamNG.Drive.ini"; exists(beamngIniPath)) {
if (std::ifstream beamngIni(beamngIniPath); beamngIni.is_open()) {
std::string contents((std::istreambuf_iterator(beamngIni)), std::istreambuf_iterator<char>());
beamngIni.close();
auto ini = Utils::ParseINI(contents);
if (ini.empty())
warn("Failed to parse BeamNG.Drive.ini");
else
debug("Successfully parsed BeamNG.Drive.ini");
std::wstring userPath;
if (ini.contains("userFolder")) {
userPath = Utils::ToWString(std::get<std::string>(ini["userFolder"]));
userPath.erase(0, userPath.find_first_not_of(L" \t"));
}
if (!userPath.empty() && Path.empty())
if (userPath = std::filesystem::path(Utils::ExpandEnvVars(userPath)); std::filesystem::exists(userPath)) {
Path = userPath;
debug(L"Using custom user folder path from BeamNG.Drive.ini: " + Path.wstring());
} else
warn(L"Found custom user folder path (" + userPath + L") in BeamNG.Drive.ini but it doesn't exist, skipping");
}
}
Path = QueryKey(hKey, 4);
if (Path.empty()) {
wchar_t* appDataPath = new wchar_t[MAX_PATH];
HRESULT result = SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, appDataPath);
if (!SUCCEEDED(result)) {
fatal("Cannot get Local Appdata directory");
}
Path = std::filesystem::path(appDataPath) / "BeamNG.drive";
delete[] appDataPath;
Path = BeamNGAppdataPath / "BeamNG.drive";
}
delete[] appDataPath;
}
}
std::string Ver = CheckVer(GetGameDir());
Ver = Ver.substr(0, Ver.find('.', Ver.find('.') + 1));
Path /= Utils::ToWString(Ver);
Path /= Utils::ToWString("current");
return Path;
}
#elif defined(__linux__)