mirror of
https://github.com/BeamMP/BeamMP-Launcher.git
synced 2025-07-01 15:36:10 +00:00
Switch to wstring for paths on windows (#167)
This PR changes everything relating to file system paths to use std::wstring instead of std::string. This allows for non-latin characters to be in the user's path, for example in their windows username. - [x] Convert windows code to use wstring - [x] Fix linux build - [x] Fix hashing --- 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:
commit
b64d645f73
@ -6,10 +6,12 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
|
#include "Utils.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
class HTTP {
|
class HTTP {
|
||||||
public:
|
public:
|
||||||
static bool Download(const std::string& IP, const std::string& Path);
|
static bool Download(const std::string& IP, const beammp_fs_string& Path);
|
||||||
static std::string Post(const std::string& IP, const std::string& Fields);
|
static std::string Post(const std::string& IP, const std::string& Fields);
|
||||||
static std::string Get(const std::string& IP);
|
static std::string Get(const std::string& IP);
|
||||||
static bool ProgressBar(size_t c, size_t t);
|
static bool ProgressBar(size_t c, size_t t);
|
||||||
|
@ -14,4 +14,11 @@ void debug(const std::string& toPrint);
|
|||||||
void error(const std::string& toPrint);
|
void error(const std::string& toPrint);
|
||||||
void info(const std::string& toPrint);
|
void info(const std::string& toPrint);
|
||||||
void warn(const std::string& toPrint);
|
void warn(const std::string& toPrint);
|
||||||
|
|
||||||
|
void except(const std::wstring& toPrint);
|
||||||
|
void fatal(const std::wstring& toPrint);
|
||||||
|
void debug(const std::wstring& toPrint);
|
||||||
|
void error(const std::wstring& toPrint);
|
||||||
|
void info(const std::wstring& toPrint);
|
||||||
|
void warn(const std::wstring& toPrint);
|
||||||
std::string getDate();
|
std::string getDate();
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include <filesystem>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
@ -29,7 +30,7 @@ extern bool Terminate;
|
|||||||
extern uint64_t UDPSock;
|
extern uint64_t UDPSock;
|
||||||
extern uint64_t TCPSock;
|
extern uint64_t TCPSock;
|
||||||
extern std::string Branch;
|
extern std::string Branch;
|
||||||
extern std::string CachingDirectory;
|
extern std::filesystem::path CachingDirectory;
|
||||||
extern bool deleteDuplicateMods;
|
extern bool deleteDuplicateMods;
|
||||||
extern bool TCPTerminate;
|
extern bool TCPTerminate;
|
||||||
extern std::string LastIP;
|
extern std::string LastIP;
|
||||||
|
@ -6,9 +6,9 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <string>
|
#include <string>
|
||||||
void PreGame(const std::string& GamePath);
|
void PreGame(const beammp_fs_string& GamePath);
|
||||||
std::string CheckVer(const std::string& path);
|
std::string CheckVer(const beammp_fs_string& path);
|
||||||
void InitGame(const std::string& Dir);
|
void InitGame(const beammp_fs_string& Dir);
|
||||||
std::string GetGameDir();
|
beammp_fs_string GetGameDir();
|
||||||
void LegitimacyCheck();
|
void LegitimacyCheck();
|
||||||
void CheckLocalKey();
|
void CheckLocalKey();
|
@ -5,14 +5,16 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include "Utils.h"
|
||||||
|
|
||||||
#include <compare>
|
#include <compare>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
void InitLauncher();
|
void InitLauncher();
|
||||||
std::string GetEP(const char* P = nullptr);
|
beammp_fs_string GetEP(const beammp_fs_char* P = nullptr);
|
||||||
std::string GetGamePath();
|
std::filesystem::path GetGamePath();
|
||||||
std::string GetVer();
|
std::string GetVer();
|
||||||
std::string GetPatch();
|
std::string GetPatch();
|
||||||
std::string GetEN();
|
beammp_fs_string GetEN();
|
||||||
void ConfigInit();
|
void ConfigInit();
|
||||||
|
134
include/Utils.h
134
include/Utils.h
@ -5,11 +5,29 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include <cassert>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
#include <locale>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <openssl/err.h>
|
||||||
|
#include <openssl/evp.h>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define beammp_fs_string std::wstring
|
||||||
|
#define beammp_fs_char wchar_t
|
||||||
|
#define beammp_wide(str) L##str
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <windows.h>
|
||||||
|
#else
|
||||||
|
#define beammp_fs_string std::string
|
||||||
|
#define beammp_fs_char char
|
||||||
|
#define beammp_wide(str) str
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace Utils {
|
namespace Utils {
|
||||||
|
|
||||||
inline std::vector<std::string> Split(const std::string& String, const std::string& delimiter) {
|
inline std::vector<std::string> Split(const std::string& String, const std::string& delimiter) {
|
||||||
@ -56,6 +74,41 @@ namespace Utils {
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
#ifdef _WIN32
|
||||||
|
inline std::wstring ExpandEnvVars(const std::wstring& input) {
|
||||||
|
std::wstring result;
|
||||||
|
std::wregex envPattern(LR"(%([^%]+)%|\$([A-Za-z_][A-Za-z0-9_]*)|\$\{([^}]+)\})");
|
||||||
|
|
||||||
|
std::wsregex_iterator begin(input.begin(), input.end(), envPattern);
|
||||||
|
std::wsregex_iterator end;
|
||||||
|
|
||||||
|
size_t lastPos = 0;
|
||||||
|
|
||||||
|
for (auto it = begin; it != end; ++it) {
|
||||||
|
const auto& match = *it;
|
||||||
|
|
||||||
|
result.append(input, lastPos, match.position() - lastPos);
|
||||||
|
|
||||||
|
std::wstring varName;
|
||||||
|
assert(match.size() == 4 && "Input regex has incorrect amount of capturing groups");
|
||||||
|
if (match[1].matched) varName = match[1].str(); // %VAR%
|
||||||
|
else if (match[2].matched) varName = match[2].str(); // $VAR
|
||||||
|
else if (match[3].matched) varName = match[3].str(); // ${VAR}
|
||||||
|
|
||||||
|
if (const wchar_t* envValue = _wgetenv(varName.c_str())) {
|
||||||
|
if (envValue != nullptr) {
|
||||||
|
result.append(envValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lastPos = match.position() + match.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
result.append(input, lastPos, input.length() - lastPos);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
inline std::map<std::string, std::map<std::string, std::string>> ParseINI(const std::string& contents) {
|
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;
|
std::map<std::string, std::map<std::string, std::string>> ini;
|
||||||
|
|
||||||
@ -112,4 +165,85 @@ namespace Utils {
|
|||||||
return ini;
|
return ini;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
inline std::wstring ToWString(const std::string& s) {
|
||||||
|
if (s.empty()) return std::wstring();
|
||||||
|
|
||||||
|
int size_needed = MultiByteToWideChar(CP_UTF8, 0, s.c_str(), (int)s.size(), nullptr, 0);
|
||||||
|
if (size_needed <= 0) {
|
||||||
|
return L"";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring result(size_needed, 0);
|
||||||
|
|
||||||
|
MultiByteToWideChar(CP_UTF8, 0, s.c_str(), (int)s.size(), &result[0], size_needed);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
inline std::string ToWString(const std::string& s) {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
inline std::string GetSha256HashReallyFast(const beammp_fs_string& filename) {
|
||||||
|
try {
|
||||||
|
EVP_MD_CTX* mdctx;
|
||||||
|
const EVP_MD* md;
|
||||||
|
uint8_t sha256_value[EVP_MAX_MD_SIZE];
|
||||||
|
md = EVP_sha256();
|
||||||
|
if (md == nullptr) {
|
||||||
|
throw std::runtime_error("EVP_sha256() failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
mdctx = EVP_MD_CTX_new();
|
||||||
|
if (mdctx == nullptr) {
|
||||||
|
throw std::runtime_error("EVP_MD_CTX_new() failed");
|
||||||
|
}
|
||||||
|
if (!EVP_DigestInit_ex2(mdctx, md, NULL)) {
|
||||||
|
EVP_MD_CTX_free(mdctx);
|
||||||
|
throw std::runtime_error("EVP_DigestInit_ex2() failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ifstream stream(filename, std::ios::binary);
|
||||||
|
|
||||||
|
const size_t FileSize = std::filesystem::file_size(filename);
|
||||||
|
size_t Read = 0;
|
||||||
|
std::vector<char> Data;
|
||||||
|
while (Read < FileSize) {
|
||||||
|
Data.resize(size_t(std::min<size_t>(FileSize - Read, 4096)));
|
||||||
|
size_t RealDataSize = Data.size();
|
||||||
|
stream.read(Data.data(), std::streamsize(Data.size()));
|
||||||
|
if (stream.eof() || stream.fail()) {
|
||||||
|
RealDataSize = size_t(stream.gcount());
|
||||||
|
}
|
||||||
|
Data.resize(RealDataSize);
|
||||||
|
if (RealDataSize == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (RealDataSize > 0 && !EVP_DigestUpdate(mdctx, Data.data(), Data.size())) {
|
||||||
|
EVP_MD_CTX_free(mdctx);
|
||||||
|
throw std::runtime_error("EVP_DigestUpdate() failed");
|
||||||
|
}
|
||||||
|
Read += RealDataSize;
|
||||||
|
}
|
||||||
|
unsigned int sha256_len = 0;
|
||||||
|
if (!EVP_DigestFinal_ex(mdctx, sha256_value, &sha256_len)) {
|
||||||
|
EVP_MD_CTX_free(mdctx);
|
||||||
|
throw std::runtime_error("EVP_DigestFinal_ex() failed");
|
||||||
|
}
|
||||||
|
EVP_MD_CTX_free(mdctx);
|
||||||
|
|
||||||
|
std::string result;
|
||||||
|
for (size_t i = 0; i < sha256_len; i++) {
|
||||||
|
char buf[3];
|
||||||
|
sprintf(buf, "%02x", sha256_value[i]);
|
||||||
|
buf[2] = 0;
|
||||||
|
result += buf;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
error(beammp_wide("Sha256 hashing of '") + filename + beammp_wide("' failed: ") + ToWString(e.what()));
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
@ -6,15 +6,16 @@
|
|||||||
|
|
||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
#include "Network/network.hpp"
|
#include "Network/network.hpp"
|
||||||
|
#include "Options.h"
|
||||||
|
#include "Utils.h"
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
#include "Options.h"
|
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
std::string Branch;
|
std::string Branch;
|
||||||
std::string CachingDirectory = "./Resources";
|
std::filesystem::path CachingDirectory = std::filesystem::path("./Resources");
|
||||||
bool deleteDuplicateMods = false;
|
bool deleteDuplicateMods = false;
|
||||||
|
|
||||||
void ParseConfig(const nlohmann::json& d) {
|
void ParseConfig(const nlohmann::json& d) {
|
||||||
@ -32,8 +33,8 @@ void ParseConfig(const nlohmann::json& d) {
|
|||||||
c = char(tolower(c));
|
c = char(tolower(c));
|
||||||
}
|
}
|
||||||
if (d.contains("CachingDirectory") && d["CachingDirectory"].is_string()) {
|
if (d.contains("CachingDirectory") && d["CachingDirectory"].is_string()) {
|
||||||
CachingDirectory = d["CachingDirectory"].get<std::string>();
|
CachingDirectory = std::filesystem::path(d["CachingDirectory"].get<std::string>());
|
||||||
info("Mod caching directory: " + CachingDirectory);
|
info(beammp_wide("Mod caching directory: ") + beammp_fs_string(CachingDirectory.relative_path()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (d.contains("Dev") && d["Dev"].is_boolean()) {
|
if (d.contains("Dev") && d["Dev"].is_boolean()) {
|
||||||
|
@ -4,9 +4,7 @@
|
|||||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
#include <windows.h>
|
|
||||||
#include <shlobj.h>
|
#include <shlobj.h>
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
#include "vdf_parser.hpp"
|
#include "vdf_parser.hpp"
|
||||||
@ -29,18 +27,18 @@
|
|||||||
|
|
||||||
unsigned long GamePID = 0;
|
unsigned long GamePID = 0;
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
std::string QueryKey(HKEY hKey, int ID);
|
std::wstring QueryKey(HKEY hKey, int ID);
|
||||||
std::string GetGamePath() {
|
std::filesystem::path GetGamePath() {
|
||||||
static std::filesystem::path Path;
|
static std::filesystem::path Path;
|
||||||
if (!Path.empty())
|
if (!Path.empty())
|
||||||
return Path.string();
|
return Path.wstring();
|
||||||
|
|
||||||
if (options.user_path) {
|
if (options.user_path) {
|
||||||
if (std::filesystem::exists(options.user_path)) {
|
if (std::filesystem::exists(options.user_path)) {
|
||||||
Path = options.user_path;
|
Path = options.user_path;
|
||||||
debug("Using custom user folder path: " + Path.string());
|
debug(L"Using custom user folder path: " + Path.wstring());
|
||||||
} else
|
} else
|
||||||
warn("Invalid or non-existent path (" + std::string(options.user_path) + ") specified using --user-path, skipping");
|
warn(L"Invalid or non-existent path (" + Utils::ToWString(options.user_path) + L") specified using --user-path, skipping");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (const auto startupIniPath = std::filesystem::path(GetGameDir()) / "startup.ini"; exists(startupIniPath)) {
|
if (const auto startupIniPath = std::filesystem::path(GetGameDir()) / "startup.ini"; exists(startupIniPath)) {
|
||||||
@ -49,27 +47,22 @@ std::string GetGamePath() {
|
|||||||
std::string contents((std::istreambuf_iterator(startupIni)), std::istreambuf_iterator<char>());
|
std::string contents((std::istreambuf_iterator(startupIni)), std::istreambuf_iterator<char>());
|
||||||
startupIni.close();
|
startupIni.close();
|
||||||
|
|
||||||
if (contents.size() > 3) {
|
|
||||||
contents.erase(0, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto ini = Utils::ParseINI(contents);
|
auto ini = Utils::ParseINI(contents);
|
||||||
if (ini.empty()) {
|
if (ini.empty())
|
||||||
warn("Failed to parse startup.ini");
|
warn("Failed to parse startup.ini");
|
||||||
} else
|
else
|
||||||
debug("Successfully parsed startup.ini");
|
debug("Successfully parsed startup.ini");
|
||||||
|
|
||||||
|
std::wstring userPath;
|
||||||
std::string userPath;
|
|
||||||
if (ini.contains("filesystem") && ini["filesystem"].contains("UserPath"))
|
if (ini.contains("filesystem") && ini["filesystem"].contains("UserPath"))
|
||||||
userPath = ini["filesystem"]["UserPath"];
|
userPath = Utils::ToWString(ini["filesystem"]["UserPath"]);
|
||||||
|
|
||||||
if (!userPath.empty())
|
if (!userPath.empty())
|
||||||
if (userPath = Utils::ExpandEnvVars(userPath); std::filesystem::exists(userPath)) {
|
if (userPath = Utils::ExpandEnvVars(userPath); std::filesystem::exists(userPath)) {
|
||||||
Path = userPath;
|
Path = userPath;
|
||||||
debug("Using custom user folder path from startup.ini: " + Path.string());
|
debug(L"Using custom user folder path from startup.ini: " + Path.wstring());
|
||||||
} else
|
} else
|
||||||
warn("Found custom user folder path ("+ userPath + ") in startup.ini but it doesn't exist, skipping");
|
warn(L"Found custom user folder path (" + userPath + L") in startup.ini but it doesn't exist, skipping");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Path.empty()) {
|
if (Path.empty()) {
|
||||||
@ -82,25 +75,27 @@ std::string GetGamePath() {
|
|||||||
Path = QueryKey(hKey, 4);
|
Path = QueryKey(hKey, 4);
|
||||||
|
|
||||||
if (Path.empty()) {
|
if (Path.empty()) {
|
||||||
char appDataPath[MAX_PATH];
|
wchar_t* appDataPath = new wchar_t[MAX_PATH];
|
||||||
HRESULT result = SHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, appDataPath);
|
HRESULT result = SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, appDataPath);
|
||||||
|
|
||||||
if (!SUCCEEDED(result)) {
|
if (!SUCCEEDED(result)) {
|
||||||
fatal("Cannot get Local Appdata directory");
|
fatal("Cannot get Local Appdata directory");
|
||||||
}
|
}
|
||||||
|
|
||||||
Path = std::filesystem::path(appDataPath) / "BeamNG.drive";
|
Path = std::filesystem::path(appDataPath) / "BeamNG.drive";
|
||||||
|
|
||||||
|
delete[] appDataPath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Ver = CheckVer(GetGameDir());
|
std::string Ver = CheckVer(GetGameDir());
|
||||||
Ver = Ver.substr(0, Ver.find('.', Ver.find('.') + 1));
|
Ver = Ver.substr(0, Ver.find('.', Ver.find('.') + 1));
|
||||||
Path = Path / (Ver + "\\");
|
Path /= Utils::ToWString(Ver);
|
||||||
return Path.string();
|
return Path;
|
||||||
}
|
}
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
std::string GetGamePath() {
|
std::filesystem::path GetGamePath() {
|
||||||
// Right now only steam is supported
|
// Right now only steam is supported
|
||||||
struct passwd* pw = getpwuid(getuid());
|
struct passwd* pw = getpwuid(getuid());
|
||||||
std::string homeDir = pw->pw_dir;
|
std::string homeDir = pw->pw_dir;
|
||||||
@ -114,24 +109,24 @@ std::string GetGamePath() {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
void StartGame(std::string Dir) {
|
void StartGame(std::wstring Dir) {
|
||||||
BOOL bSuccess = FALSE;
|
BOOL bSuccess = FALSE;
|
||||||
PROCESS_INFORMATION pi;
|
PROCESS_INFORMATION pi;
|
||||||
STARTUPINFO si = { 0 };
|
STARTUPINFOW si = { 0 };
|
||||||
si.cb = sizeof(si);
|
si.cb = sizeof(si);
|
||||||
std::string BaseDir = Dir; //+"\\Bin64";
|
std::wstring BaseDir = Dir; //+"\\Bin64";
|
||||||
// Dir += R"(\Bin64\BeamNG.drive.x64.exe)";
|
// Dir += R"(\Bin64\BeamNG.drive.x64.exe)";
|
||||||
Dir += "\\BeamNG.drive.exe";
|
Dir += L"\\BeamNG.drive.exe";
|
||||||
std::string gameArgs = "";
|
std::wstring gameArgs = L"";
|
||||||
|
|
||||||
for (int i = 0; i < options.game_arguments_length; i++) {
|
for (int i = 0; i < options.game_arguments_length; i++) {
|
||||||
gameArgs += " ";
|
gameArgs += L" ";
|
||||||
gameArgs += options.game_arguments[i];
|
gameArgs += Utils::ToWString(options.game_arguments[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
debug("BeamNG executable path: " + Dir);
|
debug(L"BeamNG executable path: " + Dir);
|
||||||
|
|
||||||
bSuccess = CreateProcessA(nullptr, (LPSTR)(Dir + gameArgs).c_str(), nullptr, nullptr, TRUE, 0, nullptr, BaseDir.c_str(), &si, &pi);
|
bSuccess = CreateProcessW(nullptr, (wchar_t*)(Dir + gameArgs).c_str(), nullptr, nullptr, TRUE, 0, nullptr, BaseDir.c_str(), &si, &pi);
|
||||||
if (bSuccess) {
|
if (bSuccess) {
|
||||||
info("Game Launched!");
|
info("Game Launched!");
|
||||||
GamePID = pi.dwProcessId;
|
GamePID = pi.dwProcessId;
|
||||||
@ -144,7 +139,8 @@ void StartGame(std::string Dir) {
|
|||||||
LPVOID lpErrorMsgBuffer;
|
LPVOID lpErrorMsgBuffer;
|
||||||
|
|
||||||
if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw,
|
if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw,
|
||||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpErrorMsgBuffer, 0, nullptr) == 0) {
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpErrorMsgBuffer, 0, nullptr)
|
||||||
|
== 0) {
|
||||||
err = "Unknown error code: " + std::to_string(dw);
|
err = "Unknown error code: " + std::to_string(dw);
|
||||||
} else {
|
} else {
|
||||||
err = "Error " + std::to_string(dw) + ": " + (char*)lpErrorMsgBuffer;
|
err = "Error " + std::to_string(dw) + ": " + (char*)lpErrorMsgBuffer;
|
||||||
@ -186,7 +182,7 @@ void StartGame(std::string Dir) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void InitGame(const std::string& Dir) {
|
void InitGame(const beammp_fs_string& Dir) {
|
||||||
if (!options.no_launch) {
|
if (!options.no_launch) {
|
||||||
std::thread Game(StartGame, Dir);
|
std::thread Game(StartGame, Dir);
|
||||||
Game.detach();
|
Game.detach();
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
#include "Startup.h"
|
#include "Startup.h"
|
||||||
|
#include "Utils.h"
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
@ -36,7 +37,7 @@ std::string getDate() {
|
|||||||
}
|
}
|
||||||
void InitLog() {
|
void InitLog() {
|
||||||
std::ofstream LFS;
|
std::ofstream LFS;
|
||||||
LFS.open(GetEP() + "Launcher.log");
|
LFS.open(GetEP() + beammp_wide("Launcher.log"));
|
||||||
if (!LFS.is_open()) {
|
if (!LFS.is_open()) {
|
||||||
error("logger file init failed!");
|
error("logger file init failed!");
|
||||||
} else
|
} else
|
||||||
@ -44,7 +45,13 @@ void InitLog() {
|
|||||||
}
|
}
|
||||||
void addToLog(const std::string& Line) {
|
void addToLog(const std::string& Line) {
|
||||||
std::ofstream LFS;
|
std::ofstream LFS;
|
||||||
LFS.open(GetEP() + "Launcher.log", std::ios_base::app);
|
LFS.open(GetEP() + beammp_wide("Launcher.log"), std::ios_base::app);
|
||||||
|
LFS << Line.c_str();
|
||||||
|
LFS.close();
|
||||||
|
}
|
||||||
|
void addToLog(const std::wstring& Line) {
|
||||||
|
std::wofstream LFS;
|
||||||
|
LFS.open(GetEP() + beammp_wide("Launcher.log"), std::ios_base::app);
|
||||||
LFS << Line.c_str();
|
LFS << Line.c_str();
|
||||||
LFS.close();
|
LFS.close();
|
||||||
}
|
}
|
||||||
@ -82,3 +89,41 @@ void except(const std::string& toPrint) {
|
|||||||
std::cout << Print;
|
std::cout << Print;
|
||||||
addToLog(Print);
|
addToLog(Print);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
void info(const std::wstring& toPrint) {
|
||||||
|
std::wstring Print = Utils::ToWString(getDate()) + L"[INFO] " + toPrint + L"\n";
|
||||||
|
std::wcout << Print;
|
||||||
|
addToLog(Print);
|
||||||
|
}
|
||||||
|
void debug(const std::wstring& toPrint) {
|
||||||
|
std::wstring Print = Utils::ToWString(getDate()) + L"[DEBUG] " + toPrint + L"\n";
|
||||||
|
if (options.verbose) {
|
||||||
|
std::wcout << Print;
|
||||||
|
}
|
||||||
|
addToLog(Print);
|
||||||
|
}
|
||||||
|
void warn(const std::wstring& toPrint) {
|
||||||
|
std::wstring Print = Utils::ToWString(getDate()) + L"[WARN] " + toPrint + L"\n";
|
||||||
|
std::wcout << Print;
|
||||||
|
addToLog(Print);
|
||||||
|
}
|
||||||
|
void error(const std::wstring& toPrint) {
|
||||||
|
std::wstring Print = Utils::ToWString(getDate()) + L"[ERROR] " + toPrint + L"\n";
|
||||||
|
std::wcout << Print;
|
||||||
|
addToLog(Print);
|
||||||
|
}
|
||||||
|
void fatal(const std::wstring& toPrint) {
|
||||||
|
std::wstring Print = Utils::ToWString(getDate()) + L"[FATAL] " + toPrint + L"\n";
|
||||||
|
std::wcout << Print;
|
||||||
|
addToLog(Print);
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(5));
|
||||||
|
std::exit(1);
|
||||||
|
}
|
||||||
|
void except(const std::wstring& toPrint) {
|
||||||
|
std::wstring Print = Utils::ToWString(getDate()) + L"[EXCEP] " + toPrint + L"\n";
|
||||||
|
std::wcout << Print;
|
||||||
|
addToLog(Print);
|
||||||
|
}
|
||||||
|
#endif
|
@ -12,6 +12,7 @@
|
|||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
#include <winsock2.h>
|
#include <winsock2.h>
|
||||||
#include <ws2tcpip.h>
|
#include <ws2tcpip.h>
|
||||||
|
#include <shellapi.h>
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
@ -127,7 +127,7 @@ std::string HTTP::Post(const std::string& IP, const std::string& Fields) {
|
|||||||
return Ret;
|
return Ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HTTP::Download(const std::string& IP, const std::string& Path) {
|
bool HTTP::Download(const std::string& IP, const beammp_fs_string& Path) {
|
||||||
static std::mutex Lock;
|
static std::mutex Lock;
|
||||||
std::scoped_lock Guard(Lock);
|
std::scoped_lock Guard(Lock);
|
||||||
|
|
||||||
@ -145,7 +145,7 @@ bool HTTP::Download(const std::string& IP, const std::string& Path) {
|
|||||||
File.close();
|
File.close();
|
||||||
info("Download Complete!");
|
info("Download Complete!");
|
||||||
} else {
|
} else {
|
||||||
error("Failed to open file directory: " + Path);
|
error(beammp_wide("Failed to open file directory: ") + Path);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -301,68 +301,6 @@ void InvalidResource(const std::string& File) {
|
|||||||
Terminate = true;
|
Terminate = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GetSha256HashReallyFast(const std::string& filename) {
|
|
||||||
try {
|
|
||||||
EVP_MD_CTX* mdctx;
|
|
||||||
const EVP_MD* md;
|
|
||||||
uint8_t sha256_value[EVP_MAX_MD_SIZE];
|
|
||||||
md = EVP_sha256();
|
|
||||||
if (md == nullptr) {
|
|
||||||
throw std::runtime_error("EVP_sha256() failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
mdctx = EVP_MD_CTX_new();
|
|
||||||
if (mdctx == nullptr) {
|
|
||||||
throw std::runtime_error("EVP_MD_CTX_new() failed");
|
|
||||||
}
|
|
||||||
if (!EVP_DigestInit_ex2(mdctx, md, NULL)) {
|
|
||||||
EVP_MD_CTX_free(mdctx);
|
|
||||||
throw std::runtime_error("EVP_DigestInit_ex2() failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ifstream stream(filename, std::ios::binary);
|
|
||||||
|
|
||||||
const size_t FileSize = std::filesystem::file_size(filename);
|
|
||||||
size_t Read = 0;
|
|
||||||
std::vector<char> Data;
|
|
||||||
while (Read < FileSize) {
|
|
||||||
Data.resize(size_t(std::min<size_t>(FileSize - Read, 4096)));
|
|
||||||
size_t RealDataSize = Data.size();
|
|
||||||
stream.read(Data.data(), std::streamsize(Data.size()));
|
|
||||||
if (stream.eof() || stream.fail()) {
|
|
||||||
RealDataSize = size_t(stream.gcount());
|
|
||||||
}
|
|
||||||
Data.resize(RealDataSize);
|
|
||||||
if (RealDataSize == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (RealDataSize > 0 && !EVP_DigestUpdate(mdctx, Data.data(), Data.size())) {
|
|
||||||
EVP_MD_CTX_free(mdctx);
|
|
||||||
throw std::runtime_error("EVP_DigestUpdate() failed");
|
|
||||||
}
|
|
||||||
Read += RealDataSize;
|
|
||||||
}
|
|
||||||
unsigned int sha256_len = 0;
|
|
||||||
if (!EVP_DigestFinal_ex(mdctx, sha256_value, &sha256_len)) {
|
|
||||||
EVP_MD_CTX_free(mdctx);
|
|
||||||
throw std::runtime_error("EVP_DigestFinal_ex() failed");
|
|
||||||
}
|
|
||||||
EVP_MD_CTX_free(mdctx);
|
|
||||||
|
|
||||||
std::string result;
|
|
||||||
for (size_t i = 0; i < sha256_len; i++) {
|
|
||||||
char buf[3];
|
|
||||||
sprintf(buf, "%02x", sha256_value[i]);
|
|
||||||
buf[2] = 0;
|
|
||||||
result += buf;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
error("Sha256 hashing of '" + filename + "' failed: " + e.what());
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ModInfo {
|
struct ModInfo {
|
||||||
static std::pair<bool, std::vector<ModInfo>> ParseModInfosFromPacket(const std::string& packet) {
|
static std::pair<bool, std::vector<ModInfo>> ParseModInfosFromPacket(const std::string& packet) {
|
||||||
bool success = false;
|
bool success = false;
|
||||||
@ -405,7 +343,7 @@ nlohmann::json modUsage = {};
|
|||||||
|
|
||||||
void UpdateModUsage(const std::string& fileName) {
|
void UpdateModUsage(const std::string& fileName) {
|
||||||
try {
|
try {
|
||||||
fs::path usageFile = fs::path(CachingDirectory) / "mods.json";
|
fs::path usageFile = CachingDirectory / "mods.json";
|
||||||
|
|
||||||
if (!fs::exists(usageFile)) {
|
if (!fs::exists(usageFile)) {
|
||||||
if (std::ofstream file(usageFile); !file.is_open()) {
|
if (std::ofstream file(usageFile); !file.is_open()) {
|
||||||
@ -423,7 +361,7 @@ void UpdateModUsage(const std::string& fileName) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (modUsage.empty()) {
|
if (modUsage.empty()) {
|
||||||
auto Size = fs::file_size(fs::path(CachingDirectory) / "mods.json");
|
auto Size = fs::file_size(CachingDirectory / "mods.json");
|
||||||
std::string modsJson(Size, 0);
|
std::string modsJson(Size, 0);
|
||||||
file.read(&modsJson[0], Size);
|
file.read(&modsJson[0], Size);
|
||||||
|
|
||||||
@ -506,14 +444,14 @@ void NewSyncResources(SOCKET Sock, const std::string& Mods, const std::vector<Mo
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto FileName = std::filesystem::path(ModInfoIter->FileName).stem().string() + "-" + ModInfoIter->Hash.substr(0, 8) + std::filesystem::path(ModInfoIter->FileName).extension().string();
|
auto FileName = std::filesystem::path(ModInfoIter->FileName).stem().string() + "-" + ModInfoIter->Hash.substr(0, 8) + std::filesystem::path(ModInfoIter->FileName).extension().string();
|
||||||
auto PathToSaveTo = (fs::path(CachingDirectory) / FileName).string();
|
auto PathToSaveTo = (CachingDirectory / FileName);
|
||||||
if (fs::exists(PathToSaveTo) && GetSha256HashReallyFast(PathToSaveTo) == ModInfoIter->Hash) {
|
if (fs::exists(PathToSaveTo) && Utils::GetSha256HashReallyFast(PathToSaveTo) == ModInfoIter->Hash) {
|
||||||
debug("Mod '" + FileName + "' found in cache");
|
debug("Mod '" + FileName + "' found in cache");
|
||||||
UpdateUl(false, std::to_string(ModNo) + "/" + std::to_string(TotalMods) + ": " + ModInfoIter->FileName);
|
UpdateUl(false, std::to_string(ModNo) + "/" + std::to_string(TotalMods) + ": " + ModInfoIter->FileName);
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||||
try {
|
try {
|
||||||
if (!fs::exists(GetGamePath() + "mods/multiplayer")) {
|
if (!fs::exists(GetGamePath() / beammp_wide("mods/multiplayer"))) {
|
||||||
fs::create_directories(GetGamePath() + "mods/multiplayer");
|
fs::create_directories(GetGamePath() / beammp_wide("mods/multiplayer"));
|
||||||
}
|
}
|
||||||
auto modname = ModInfoIter->FileName;
|
auto modname = ModInfoIter->FileName;
|
||||||
#if defined(__linux__)
|
#if defined(__linux__)
|
||||||
@ -537,16 +475,16 @@ void NewSyncResources(SOCKET Sock, const std::string& Mods, const std::vector<Mo
|
|||||||
}
|
}
|
||||||
WaitForConfirm();
|
WaitForConfirm();
|
||||||
continue;
|
continue;
|
||||||
} else if (auto OldCachedPath = fs::path(CachingDirectory) / std::filesystem::path(ModInfoIter->FileName).filename();
|
} else if (auto OldCachedPath = CachingDirectory / std::filesystem::path(ModInfoIter->FileName).filename();
|
||||||
fs::exists(OldCachedPath) && GetSha256HashReallyFast(OldCachedPath.string()) == ModInfoIter->Hash) {
|
fs::exists(OldCachedPath) && Utils::GetSha256HashReallyFast(OldCachedPath) == ModInfoIter->Hash) {
|
||||||
debug("Mod '" + FileName + "' found in old cache, copying it to the new cache");
|
debug("Mod '" + FileName + "' found in old cache, copying it to the new cache");
|
||||||
UpdateUl(false, std::to_string(ModNo) + "/" + std::to_string(TotalMods) + ": " + ModInfoIter->FileName);
|
UpdateUl(false, std::to_string(ModNo) + "/" + std::to_string(TotalMods) + ": " + ModInfoIter->FileName);
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||||
try {
|
try {
|
||||||
fs::copy_file(OldCachedPath, PathToSaveTo, fs::copy_options::overwrite_existing);
|
fs::copy_file(OldCachedPath, PathToSaveTo, fs::copy_options::overwrite_existing);
|
||||||
|
|
||||||
if (!fs::exists(GetGamePath() + "mods/multiplayer")) {
|
if (!fs::exists(GetGamePath() / beammp_wide("mods/multiplayer"))) {
|
||||||
fs::create_directories(GetGamePath() + "mods/multiplayer");
|
fs::create_directories(GetGamePath() / beammp_wide("mods/multiplayer"));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto modname = ModInfoIter->FileName;
|
auto modname = ModInfoIter->FileName;
|
||||||
@ -576,7 +514,7 @@ void NewSyncResources(SOCKET Sock, const std::string& Mods, const std::vector<Mo
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ModInfoIter->Protected) {
|
if (ModInfoIter->Protected) {
|
||||||
std::string message = "Mod '" + ModInfoIter->FileName + "' is protected and therefore must be placed in the Resources/Caching folder manually here: " + fs::absolute(CachingDirectory).string();
|
std::string message = "Mod '" + ModInfoIter->FileName + "' is protected and therefore must be placed in the Resources/Caching folder manually here: " + absolute(CachingDirectory).string();
|
||||||
|
|
||||||
error(message);
|
error(message);
|
||||||
UUl(message);
|
UUl(message);
|
||||||
@ -587,7 +525,7 @@ void NewSyncResources(SOCKET Sock, const std::string& Mods, const std::vector<Mo
|
|||||||
CheckForDir();
|
CheckForDir();
|
||||||
std::string FName = ModInfoIter->FileName;
|
std::string FName = ModInfoIter->FileName;
|
||||||
do {
|
do {
|
||||||
debug("Loading file '" + FName + "' to '" + PathToSaveTo + "'");
|
debug(beammp_wide("Loading file '") + Utils::ToWString(FName) + beammp_wide("' to '") + beammp_fs_string(PathToSaveTo) + beammp_wide("'"));
|
||||||
TCPSend("f" + ModInfoIter->FileName, Sock);
|
TCPSend("f" + ModInfoIter->FileName, Sock);
|
||||||
|
|
||||||
std::string Data = TCPRcv(Sock);
|
std::string Data = TCPRcv(Sock);
|
||||||
@ -620,18 +558,18 @@ void NewSyncResources(SOCKET Sock, const std::string& Mods, const std::vector<Mo
|
|||||||
}
|
}
|
||||||
// 2. verify size and hash
|
// 2. verify size and hash
|
||||||
if (std::filesystem::file_size(PathToSaveTo) != DownloadedFile.size()) {
|
if (std::filesystem::file_size(PathToSaveTo) != DownloadedFile.size()) {
|
||||||
error("Failed to write the entire file '" + PathToSaveTo + "' correctly (file size mismatch)");
|
error(beammp_wide("Failed to write the entire file '") + beammp_fs_string(PathToSaveTo) + beammp_wide("' correctly (file size mismatch)"));
|
||||||
Terminate = true;
|
Terminate = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GetSha256HashReallyFast(PathToSaveTo) != ModInfoIter->Hash) {
|
if (Utils::GetSha256HashReallyFast(PathToSaveTo) != ModInfoIter->Hash) {
|
||||||
error("Failed to write or download the entire file '" + PathToSaveTo + "' correctly (hash mismatch)");
|
error(beammp_wide("Failed to write or download the entire file '") + beammp_fs_string(PathToSaveTo) + beammp_wide("' correctly (hash mismatch)"));
|
||||||
Terminate = true;
|
Terminate = true;
|
||||||
}
|
}
|
||||||
} while (fs::file_size(PathToSaveTo) != ModInfoIter->FileSize && !Terminate);
|
} while (fs::file_size(PathToSaveTo) != ModInfoIter->FileSize && !Terminate);
|
||||||
if (!Terminate) {
|
if (!Terminate) {
|
||||||
if (!fs::exists(GetGamePath() + "mods/multiplayer")) {
|
if (!fs::exists(GetGamePath() / beammp_wide("mods/multiplayer"))) {
|
||||||
fs::create_directories(GetGamePath() + "mods/multiplayer");
|
fs::create_directories(GetGamePath() / beammp_wide("mods/multiplayer"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Linux version of the game doesnt support uppercase letters in mod names
|
// Linux version of the game doesnt support uppercase letters in mod names
|
||||||
@ -688,7 +626,8 @@ void SyncResources(SOCKET Sock) {
|
|||||||
Ret.clear();
|
Ret.clear();
|
||||||
|
|
||||||
int Amount = 0, Pos = 0;
|
int Amount = 0, Pos = 0;
|
||||||
std::string PathToSaveTo, t;
|
std::filesystem::path PathToSaveTo;
|
||||||
|
std::string t;
|
||||||
for (const std::string& name : FNames) {
|
for (const std::string& name : FNames) {
|
||||||
if (!name.empty()) {
|
if (!name.empty()) {
|
||||||
t += name.substr(name.find_last_of('/') + 1) + ";";
|
t += name.substr(name.find_last_of('/') + 1) + ";";
|
||||||
@ -716,7 +655,7 @@ void SyncResources(SOCKET Sock) {
|
|||||||
for (auto FN = FNames.begin(), FS = FSizes.begin(); FN != FNames.end() && !Terminate; ++FN, ++FS) {
|
for (auto FN = FNames.begin(), FS = FSizes.begin(); FN != FNames.end() && !Terminate; ++FN, ++FS) {
|
||||||
auto pos = FN->find_last_of('/');
|
auto pos = FN->find_last_of('/');
|
||||||
if (pos != std::string::npos) {
|
if (pos != std::string::npos) {
|
||||||
PathToSaveTo = CachingDirectory + FN->substr(pos);
|
PathToSaveTo = CachingDirectory / std::filesystem::path(*FN).filename();
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -726,21 +665,22 @@ void SyncResources(SOCKET Sock) {
|
|||||||
if (FS->find_first_not_of("0123456789") != std::string::npos)
|
if (FS->find_first_not_of("0123456789") != std::string::npos)
|
||||||
continue;
|
continue;
|
||||||
if (fs::file_size(PathToSaveTo) == FileSize) {
|
if (fs::file_size(PathToSaveTo) == FileSize) {
|
||||||
UpdateUl(false, std::to_string(Pos) + "/" + std::to_string(Amount) + ": " + PathToSaveTo.substr(PathToSaveTo.find_last_of('/')));
|
UpdateUl(false, std::to_string(Pos) + "/" + std::to_string(Amount) + ": " + PathToSaveTo.filename().string());
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||||
try {
|
try {
|
||||||
if (!fs::exists(GetGamePath() + "mods/multiplayer")) {
|
if (!fs::exists(GetGamePath() / beammp_wide("mods/multiplayer"))) {
|
||||||
fs::create_directories(GetGamePath() + "mods/multiplayer");
|
fs::create_directories(GetGamePath() / beammp_wide("mods/multiplayer"));
|
||||||
}
|
}
|
||||||
auto modname = PathToSaveTo.substr(PathToSaveTo.find_last_of('/'));
|
auto modname = PathToSaveTo.filename().string();
|
||||||
#if defined(__linux__)
|
#if defined(__linux__)
|
||||||
// Linux version of the game doesnt support uppercase letters in mod names
|
// Linux version of the game doesnt support uppercase letters in mod names
|
||||||
for (char& c : modname) {
|
for (char& c : modname) {
|
||||||
c = ::tolower(c);
|
c = ::tolower(c);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
auto name = GetGamePath() + "mods/multiplayer" + modname;
|
auto name = GetGamePath() / beammp_wide("mods/multiplayer") / Utils::ToWString(modname);
|
||||||
auto tmp_name = name + ".tmp";
|
auto tmp_name = name;
|
||||||
|
tmp_name += L".tmp";
|
||||||
fs::copy_file(PathToSaveTo, tmp_name, fs::copy_options::overwrite_existing);
|
fs::copy_file(PathToSaveTo, tmp_name, fs::copy_options::overwrite_existing);
|
||||||
fs::rename(tmp_name, name);
|
fs::rename(tmp_name, name);
|
||||||
UpdateModUsage(modname);
|
UpdateModUsage(modname);
|
||||||
@ -752,12 +692,12 @@ void SyncResources(SOCKET Sock) {
|
|||||||
WaitForConfirm();
|
WaitForConfirm();
|
||||||
continue;
|
continue;
|
||||||
} else
|
} else
|
||||||
remove(PathToSaveTo.c_str());
|
fs::remove(PathToSaveTo.wstring());
|
||||||
}
|
}
|
||||||
CheckForDir();
|
CheckForDir();
|
||||||
std::string FName = PathToSaveTo.substr(PathToSaveTo.find_last_of('/'));
|
std::string FName = PathToSaveTo.filename().string();
|
||||||
do {
|
do {
|
||||||
debug("Loading file '" + FName + "' to '" + PathToSaveTo + "'");
|
debug("Loading file '" + FName + "' to '" + PathToSaveTo.string() + "'");
|
||||||
TCPSend("f" + *FN, Sock);
|
TCPSend("f" + *FN, Sock);
|
||||||
|
|
||||||
std::string Data = TCPRcv(Sock);
|
std::string Data = TCPRcv(Sock);
|
||||||
@ -782,13 +722,13 @@ void SyncResources(SOCKET Sock) {
|
|||||||
}
|
}
|
||||||
// 2. verify size
|
// 2. verify size
|
||||||
if (std::filesystem::file_size(PathToSaveTo) != DownloadedFile.size()) {
|
if (std::filesystem::file_size(PathToSaveTo) != DownloadedFile.size()) {
|
||||||
error("Failed to write the entire file '" + PathToSaveTo + "' correctly (file size mismatch)");
|
error(beammp_wide("Failed to write the entire file '") + beammp_fs_string(PathToSaveTo) + beammp_wide("' correctly (file size mismatch)"));
|
||||||
Terminate = true;
|
Terminate = true;
|
||||||
}
|
}
|
||||||
} while (fs::file_size(PathToSaveTo) != std::stoull(*FS) && !Terminate);
|
} while (fs::file_size(PathToSaveTo) != std::stoull(*FS) && !Terminate);
|
||||||
if (!Terminate) {
|
if (!Terminate) {
|
||||||
if (!fs::exists(GetGamePath() + "mods/multiplayer")) {
|
if (!fs::exists(GetGamePath() / beammp_wide("mods/multiplayer"))) {
|
||||||
fs::create_directories(GetGamePath() + "mods/multiplayer");
|
fs::create_directories(GetGamePath() / beammp_wide("mods/multiplayer"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Linux version of the game doesnt support uppercase letters in mod names
|
// Linux version of the game doesnt support uppercase letters in mod names
|
||||||
@ -798,7 +738,7 @@ void SyncResources(SOCKET Sock) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
fs::copy_file(PathToSaveTo, GetGamePath() + "mods/multiplayer" + FName, fs::copy_options::overwrite_existing);
|
fs::copy_file(PathToSaveTo, GetGamePath() / beammp_wide("mods/multiplayer") / Utils::ToWString(FName), fs::copy_options::overwrite_existing);
|
||||||
UpdateModUsage(FN->substr(pos));
|
UpdateModUsage(FN->substr(pos));
|
||||||
}
|
}
|
||||||
WaitForConfirm();
|
WaitForConfirm();
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
#include <windows.h>
|
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
#include "vdf_parser.hpp"
|
#include "vdf_parser.hpp"
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
@ -15,6 +14,8 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#endif
|
#endif
|
||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
|
#include "Utils.h"
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
@ -23,7 +24,7 @@
|
|||||||
#define MAX_VALUE_NAME 16383
|
#define MAX_VALUE_NAME 16383
|
||||||
|
|
||||||
int TraceBack = 0;
|
int TraceBack = 0;
|
||||||
std::string GameDir;
|
beammp_fs_string GameDir;
|
||||||
|
|
||||||
void lowExit(int code) {
|
void lowExit(int code) {
|
||||||
TraceBack = 0;
|
TraceBack = 0;
|
||||||
@ -33,7 +34,7 @@ void lowExit(int code) {
|
|||||||
exit(2);
|
exit(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GetGameDir() {
|
beammp_fs_string GetGameDir() {
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
return GameDir.substr(0, GameDir.find_last_of('\\'));
|
return GameDir.substr(0, GameDir.find_last_of('\\'));
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
@ -44,8 +45,8 @@ std::string GetGameDir() {
|
|||||||
LONG OpenKey(HKEY root, const char* path, PHKEY hKey) {
|
LONG OpenKey(HKEY root, const char* path, PHKEY hKey) {
|
||||||
return RegOpenKeyEx(root, reinterpret_cast<LPCSTR>(path), 0, KEY_READ, hKey);
|
return RegOpenKeyEx(root, reinterpret_cast<LPCSTR>(path), 0, KEY_READ, hKey);
|
||||||
}
|
}
|
||||||
std::string QueryKey(HKEY hKey, int ID) {
|
std::wstring QueryKey(HKEY hKey, int ID) {
|
||||||
TCHAR achKey[MAX_KEY_LENGTH]; // buffer for subkey name
|
wchar_t* achKey; // buffer for subkey name
|
||||||
DWORD cbName; // size of name string
|
DWORD cbName; // size of name string
|
||||||
TCHAR achClass[MAX_PATH] = TEXT(""); // buffer for class name
|
TCHAR achClass[MAX_PATH] = TEXT(""); // buffer for class name
|
||||||
DWORD cchClassName = MAX_PATH; // size of class string
|
DWORD cchClassName = MAX_PATH; // size of class string
|
||||||
@ -60,7 +61,7 @@ std::string QueryKey(HKEY hKey, int ID) {
|
|||||||
|
|
||||||
DWORD i, retCode;
|
DWORD i, retCode;
|
||||||
|
|
||||||
TCHAR achValue[MAX_VALUE_NAME];
|
wchar_t* achValue = new wchar_t[MAX_VALUE_NAME];
|
||||||
DWORD cchValue = MAX_VALUE_NAME;
|
DWORD cchValue = MAX_VALUE_NAME;
|
||||||
|
|
||||||
retCode = RegQueryInfoKey(
|
retCode = RegQueryInfoKey(
|
||||||
@ -82,9 +83,9 @@ std::string QueryKey(HKEY hKey, int ID) {
|
|||||||
if (cSubKeys) {
|
if (cSubKeys) {
|
||||||
for (i = 0; i < cSubKeys; i++) {
|
for (i = 0; i < cSubKeys; i++) {
|
||||||
cbName = MAX_KEY_LENGTH;
|
cbName = MAX_KEY_LENGTH;
|
||||||
retCode = RegEnumKeyEx(hKey, i, achKey, &cbName, nullptr, nullptr, nullptr, &ftLastWriteTime);
|
retCode = RegEnumKeyExW(hKey, i, achKey, &cbName, nullptr, nullptr, nullptr, &ftLastWriteTime);
|
||||||
if (retCode == ERROR_SUCCESS) {
|
if (retCode == ERROR_SUCCESS) {
|
||||||
if (strcmp(achKey, "Steam App 284160") == 0) {
|
if (wcscmp(achKey, L"Steam App 284160") == 0) {
|
||||||
return achKey;
|
return achKey;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -94,36 +95,37 @@ std::string QueryKey(HKEY hKey, int ID) {
|
|||||||
for (i = 0, retCode = ERROR_SUCCESS; i < cValues; i++) {
|
for (i = 0, retCode = ERROR_SUCCESS; i < cValues; i++) {
|
||||||
cchValue = MAX_VALUE_NAME;
|
cchValue = MAX_VALUE_NAME;
|
||||||
achValue[0] = '\0';
|
achValue[0] = '\0';
|
||||||
retCode = RegEnumValue(hKey, i, achValue, &cchValue, nullptr, nullptr, nullptr, nullptr);
|
retCode = RegEnumValueW(hKey, i, achValue, &cchValue, nullptr, nullptr, nullptr, nullptr);
|
||||||
if (retCode == ERROR_SUCCESS) {
|
if (retCode == ERROR_SUCCESS) {
|
||||||
DWORD lpData = cbMaxValueData;
|
DWORD lpData = cbMaxValueData;
|
||||||
buffer[0] = '\0';
|
buffer[0] = '\0';
|
||||||
LONG dwRes = RegQueryValueEx(hKey, achValue, nullptr, nullptr, buffer, &lpData);
|
LONG dwRes = RegQueryValueExW(hKey, achValue, nullptr, nullptr, buffer, &lpData);
|
||||||
std::string data = (char*)(buffer);
|
std::wstring data = (wchar_t*)(buffer);
|
||||||
std::string key = achValue;
|
std::wstring key = achValue;
|
||||||
|
|
||||||
|
|
||||||
switch (ID) {
|
switch (ID) {
|
||||||
case 1:
|
case 1:
|
||||||
if (key == "SteamExe") {
|
if (key == L"SteamExe") {
|
||||||
auto p = data.find_last_of("/\\");
|
auto p = data.find_last_of(L"/\\");
|
||||||
if (p != std::string::npos) {
|
if (p != std::string::npos) {
|
||||||
return data.substr(0, p);
|
return data.substr(0, p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
if (key == "Name" && data == "BeamNG.drive")
|
if (key == L"Name" && data == L"BeamNG.drive")
|
||||||
return data;
|
return data;
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
if (key == "rootpath")
|
if (key == L"rootpath")
|
||||||
return data;
|
return data;
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
if (key == "userpath_override")
|
if (key == L"userpath_override")
|
||||||
return data;
|
return data;
|
||||||
case 5:
|
case 5:
|
||||||
if (key == "Local AppData")
|
if (key == L"Local AppData")
|
||||||
return data;
|
return data;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@ -131,8 +133,9 @@ std::string QueryKey(HKEY hKey, int ID) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
delete[] achValue;
|
||||||
delete[] buffer;
|
delete[] buffer;
|
||||||
return "";
|
return L"";
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -159,7 +162,7 @@ void FileList(std::vector<std::string>& a, const std::string& Path) {
|
|||||||
}
|
}
|
||||||
void LegitimacyCheck() {
|
void LegitimacyCheck() {
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
std::string Result;
|
std::wstring Result;
|
||||||
std::string K3 = R"(Software\BeamNG\BeamNG.drive)";
|
std::string K3 = R"(Software\BeamNG\BeamNG.drive)";
|
||||||
HKEY hKey;
|
HKEY hKey;
|
||||||
LONG dwRegOPenKey = OpenKey(HKEY_CURRENT_USER, K3.c_str(), &hKey);
|
LONG dwRegOPenKey = OpenKey(HKEY_CURRENT_USER, K3.c_str(), &hKey);
|
||||||
@ -229,12 +232,9 @@ void LegitimacyCheck() {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
std::string CheckVer(const std::string& dir) {
|
std::string CheckVer(const beammp_fs_string& dir) {
|
||||||
#if defined(_WIN32)
|
std::string temp;
|
||||||
std::string temp, Path = dir + "\\integrity.json";
|
beammp_fs_string Path = dir + beammp_wide("\\integrity.json");
|
||||||
#elif defined(__linux__)
|
|
||||||
std::string temp, Path = dir + "/integrity.json";
|
|
||||||
#endif
|
|
||||||
std::ifstream f(Path.c_str(), std::ios::binary);
|
std::ifstream f(Path.c_str(), std::ios::binary);
|
||||||
int Size = int(std::filesystem::file_size(Path));
|
int Size = int(std::filesystem::file_size(Path));
|
||||||
std::string vec(Size, 0);
|
std::string vec(Size, 0);
|
||||||
|
@ -12,20 +12,20 @@
|
|||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
#include <string>
|
#include <string>
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
#include <windows.h>
|
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
#include "Http.h"
|
#include "Http.h"
|
||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
#include "Network/network.hpp"
|
#include "Network/network.hpp"
|
||||||
|
#include "Options.h"
|
||||||
#include "Security/Init.h"
|
#include "Security/Init.h"
|
||||||
#include "Startup.h"
|
#include "Startup.h"
|
||||||
|
#include "Utils.h"
|
||||||
#include "hashpp.h"
|
#include "hashpp.h"
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include "Options.h"
|
|
||||||
|
|
||||||
extern int TraceBack;
|
extern int TraceBack;
|
||||||
int ProxyPort = 0;
|
int ProxyPort = 0;
|
||||||
@ -72,12 +72,8 @@ Version::Version(const std::array<uint8_t, 3>& v)
|
|||||||
: Version(v[0], v[1], v[2]) {
|
: Version(v[0], v[1], v[2]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GetEN() {
|
beammp_fs_string GetEN() {
|
||||||
#if defined(_WIN32)
|
return beammp_wide("BeamMP-Launcher.exe");
|
||||||
return "BeamMP-Launcher.exe";
|
|
||||||
#elif defined(__linux__)
|
|
||||||
return "BeamMP-Launcher";
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GetVer() {
|
std::string GetVer() {
|
||||||
@ -87,34 +83,34 @@ std::string GetPatch() {
|
|||||||
return ".1";
|
return ".1";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GetEP(const char* P) {
|
beammp_fs_string GetEP(const beammp_fs_char* P) {
|
||||||
static std::string Ret = [&]() {
|
static beammp_fs_string Ret = [&]() {
|
||||||
std::string path(P);
|
beammp_fs_string path(P);
|
||||||
return path.substr(0, path.find_last_of("\\/") + 1);
|
return path.substr(0, path.find_last_of(beammp_wide("\\/")) + 1);
|
||||||
}();
|
}();
|
||||||
return Ret;
|
return Ret;
|
||||||
}
|
}
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
void ReLaunch() {
|
void ReLaunch() {
|
||||||
std::string Arg;
|
std::wstring Arg;
|
||||||
for (int c = 2; c <= options.argc; c++) {
|
for (int c = 2; c <= options.argc; c++) {
|
||||||
Arg += options.argv[c - 1];
|
Arg += Utils::ToWString(options.argv[c - 1]);
|
||||||
Arg += " ";
|
Arg += L" ";
|
||||||
}
|
}
|
||||||
info("Relaunch!");
|
info("Relaunch!");
|
||||||
system("cls");
|
system("cls");
|
||||||
ShellExecute(nullptr, "runas", (GetEP() + GetEN()).c_str(), Arg.c_str(), nullptr, SW_SHOWNORMAL);
|
ShellExecuteW(nullptr, L"runas", (GetEP() + GetEN()).c_str(), Arg.c_str(), nullptr, SW_SHOWNORMAL);
|
||||||
ShowWindow(GetConsoleWindow(), 0);
|
ShowWindow(GetConsoleWindow(), 0);
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
void URelaunch() {
|
void URelaunch() {
|
||||||
std::string Arg;
|
std::wstring Arg;
|
||||||
for (int c = 2; c <= options.argc; c++) {
|
for (int c = 2; c <= options.argc; c++) {
|
||||||
Arg += options.argv[c - 1];
|
Arg += Utils::ToWString(options.argv[c - 1]);
|
||||||
Arg += " ";
|
Arg += L" ";
|
||||||
}
|
}
|
||||||
ShellExecute(nullptr, "open", (GetEP() + GetEN()).c_str(), Arg.c_str(), nullptr, SW_SHOWNORMAL);
|
ShellExecuteW(nullptr, L"open", (GetEP() + GetEN()).c_str(), Arg.c_str(), nullptr, SW_SHOWNORMAL);
|
||||||
ShowWindow(GetConsoleWindow(), 0);
|
ShowWindow(GetConsoleWindow(), 0);
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
exit(1);
|
exit(1);
|
||||||
@ -149,16 +145,16 @@ void URelaunch() {
|
|||||||
|
|
||||||
void CheckName() {
|
void CheckName() {
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
std::string DN = GetEN(), CDir = options.executable_name, FN = CDir.substr(CDir.find_last_of('\\') + 1);
|
std::wstring DN = GetEN(), CDir = Utils::ToWString(options.executable_name), FN = CDir.substr(CDir.find_last_of('\\') + 1);
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
std::string DN = GetEN(), CDir = options.executable_name, FN = CDir.substr(CDir.find_last_of('/') + 1);
|
std::string DN = GetEN(), CDir = options.executable_name, FN = CDir.substr(CDir.find_last_of('/') + 1);
|
||||||
#endif
|
#endif
|
||||||
if (FN != DN) {
|
if (FN != DN) {
|
||||||
if (fs::exists(DN))
|
if (fs::exists(DN))
|
||||||
remove(DN.c_str());
|
fs::remove(DN.c_str());
|
||||||
if (fs::exists(DN))
|
if (fs::exists(DN))
|
||||||
ReLaunch();
|
ReLaunch();
|
||||||
std::rename(FN.c_str(), DN.c_str());
|
fs::rename(FN.c_str(), DN.c_str());
|
||||||
URelaunch();
|
URelaunch();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -169,9 +165,9 @@ void CheckForUpdates(const std::string& CV) {
|
|||||||
"https://backend.beammp.com/version/launcher?branch=" + Branch + "&pk=" + PublicKey);
|
"https://backend.beammp.com/version/launcher?branch=" + Branch + "&pk=" + PublicKey);
|
||||||
|
|
||||||
transform(LatestHash.begin(), LatestHash.end(), LatestHash.begin(), ::tolower);
|
transform(LatestHash.begin(), LatestHash.end(), LatestHash.begin(), ::tolower);
|
||||||
std::string EP(GetEP() + GetEN()), Back(GetEP() + "BeamMP-Launcher.back");
|
beammp_fs_string EP(GetEP() + GetEN()), Back(GetEP() + beammp_wide("BeamMP-Launcher.back"));
|
||||||
|
|
||||||
std::string FileHash = hashpp::get::getFileHash(hashpp::ALGORITHMS::SHA2_256, EP);
|
std::string FileHash = Utils::GetSha256HashReallyFast(EP);
|
||||||
|
|
||||||
if (FileHash != LatestHash && IsOutdated(Version(VersionStrToInts(GetVer() + GetPatch())), Version(VersionStrToInts(LatestVersion)))) {
|
if (FileHash != LatestHash && IsOutdated(Version(VersionStrToInts(GetVer() + GetPatch())), Version(VersionStrToInts(LatestVersion)))) {
|
||||||
if (!options.no_update) {
|
if (!options.no_update) {
|
||||||
@ -251,7 +247,7 @@ size_t DirCount(const std::filesystem::path& path) {
|
|||||||
return (size_t)std::distance(std::filesystem::directory_iterator { path }, std::filesystem::directory_iterator {});
|
return (size_t)std::distance(std::filesystem::directory_iterator { path }, std::filesystem::directory_iterator {});
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckMP(const std::string& Path) {
|
void CheckMP(const beammp_fs_string& Path) {
|
||||||
if (!fs::exists(Path))
|
if (!fs::exists(Path))
|
||||||
return;
|
return;
|
||||||
size_t c = DirCount(fs::path(Path));
|
size_t c = DirCount(fs::path(Path));
|
||||||
@ -271,7 +267,7 @@ void CheckMP(const std::string& Path) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void EnableMP() {
|
void EnableMP() {
|
||||||
std::string File(GetGamePath() + "mods/db.json");
|
beammp_fs_string File(GetGamePath() / beammp_wide("mods/db.json"));
|
||||||
if (!fs::exists(File))
|
if (!fs::exists(File))
|
||||||
return;
|
return;
|
||||||
auto Size = fs::file_size(File);
|
auto Size = fs::file_size(File);
|
||||||
@ -294,18 +290,18 @@ void EnableMP() {
|
|||||||
ofs << d.dump();
|
ofs << d.dump();
|
||||||
ofs.close();
|
ofs.close();
|
||||||
} else {
|
} else {
|
||||||
error("Failed to write " + File);
|
error(beammp_wide("Failed to write ") + File);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PreGame(const std::string& GamePath) {
|
void PreGame(const beammp_fs_string& GamePath) {
|
||||||
std::string GameVer = CheckVer(GamePath);
|
std::string GameVer = CheckVer(GamePath);
|
||||||
info("Game Version : " + GameVer);
|
info("Game Version : " + GameVer);
|
||||||
|
|
||||||
CheckMP(GetGamePath() + "mods/multiplayer");
|
CheckMP(GetGamePath() / beammp_wide("mods/multiplayer"));
|
||||||
info("Game user path: " + GetGamePath());
|
info(beammp_wide("Game user path: ") + beammp_fs_string(GetGamePath()));
|
||||||
|
|
||||||
if (!options.no_download) {
|
if (!options.no_download) {
|
||||||
std::string LatestHash = HTTP::Get("https://backend.beammp.com/sha/mod?branch=" + Branch + "&pk=" + PublicKey);
|
std::string LatestHash = HTTP::Get("https://backend.beammp.com/sha/mod?branch=" + Branch + "&pk=" + PublicKey);
|
||||||
@ -315,21 +311,21 @@ void PreGame(const std::string& GamePath) {
|
|||||||
LatestHash.end());
|
LatestHash.end());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!fs::exists(GetGamePath() + "mods/multiplayer")) {
|
if (!fs::exists(GetGamePath() / beammp_wide("mods/multiplayer"))) {
|
||||||
fs::create_directories(GetGamePath() + "mods/multiplayer");
|
fs::create_directories(GetGamePath() / beammp_wide("mods/multiplayer"));
|
||||||
}
|
}
|
||||||
EnableMP();
|
EnableMP();
|
||||||
} catch (std::exception& e) {
|
} catch (std::exception& e) {
|
||||||
fatal(e.what());
|
fatal(e.what());
|
||||||
}
|
}
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
std::string ZipPath(GetGamePath() + R"(mods\multiplayer\BeamMP.zip)");
|
std::wstring ZipPath(GetGamePath() / LR"(mods\multiplayer\BeamMP.zip)");
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
// Linux version of the game cant handle mods with uppercase names
|
// Linux version of the game cant handle mods with uppercase names
|
||||||
std::string ZipPath(GetGamePath() + R"(mods/multiplayer/beammp.zip)");
|
std::string ZipPath(GetGamePath() / R"(mods/multiplayer/beammp.zip)");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
std::string FileHash = hashpp::get::getFileHash(hashpp::ALGORITHMS::SHA2_256, ZipPath);
|
std::string FileHash = fs::exists(ZipPath) ? Utils::GetSha256HashReallyFast(ZipPath) : "";
|
||||||
|
|
||||||
if (FileHash != LatestHash) {
|
if (FileHash != LatestHash) {
|
||||||
info("Downloading BeamMP Update " + LatestHash);
|
info("Downloading BeamMP Update " + LatestHash);
|
||||||
@ -339,9 +335,9 @@ void PreGame(const std::string& GamePath) {
|
|||||||
ZipPath);
|
ZipPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Target(GetGamePath() + "mods/unpacked/beammp");
|
beammp_fs_string Target(GetGamePath() / beammp_wide("mods/unpacked/beammp"));
|
||||||
|
|
||||||
if (fs::is_directory(Target) && !fs::is_directory(Target + "/.git")) {
|
if (fs::is_directory(Target) && !fs::is_directory(Target + beammp_wide("/.git"))) {
|
||||||
fs::remove_all(Target);
|
fs::remove_all(Target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include "Network/network.hpp"
|
#include "Network/network.hpp"
|
||||||
#include "Security/Init.h"
|
#include "Security/Init.h"
|
||||||
#include "Startup.h"
|
#include "Startup.h"
|
||||||
|
#include "Utils.h"
|
||||||
#include <curl/curl.h>
|
#include <curl/curl.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
@ -37,7 +38,7 @@ int main(int argc, const char** argv) try {
|
|||||||
|
|
||||||
curl_global_init(CURL_GLOBAL_ALL);
|
curl_global_init(CURL_GLOBAL_ALL);
|
||||||
|
|
||||||
GetEP(argv[0]);
|
GetEP(Utils::ToWString(std::string(argv[0])).c_str());
|
||||||
|
|
||||||
InitLog();
|
InitLog();
|
||||||
ConfigInit();
|
ConfigInit();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user