From 9d01ae939a7545e71f2cd9e0066e34096e2dba50 Mon Sep 17 00:00:00 2001 From: Anonymous275 <36374260+Anonymous-275@users.noreply.github.com> Date: Mon, 19 Dec 2022 20:41:45 +0000 Subject: [PATCH] - reduce the amount of data required from the registry - remove wxWidgets - general cleanup --- CMakeLists.txt | 15 +- include/Launcher.h | 15 +- src/Config.cpp | 137 +++-- src/Launcher.cpp | 14 +- src/Logger.cpp | 1 - src/Network/HttpAPI.cpp | 23 +- src/Network/Resources.cpp | 9 +- src/gui/Gui.cpp | 1010 ------------------------------------- src/main.cpp | 6 +- 9 files changed, 140 insertions(+), 1090 deletions(-) delete mode 100644 src/gui/Gui.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 15684d2..b111c57 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,17 +15,9 @@ if (WIN32) set(VcpkgRoot ${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}) include_directories(${VcpkgRoot}/include) link_directories(${VcpkgRoot}/lib) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DUNICODE") endif(WIN32) -set(wxUSE_STL ON) -set(BUILD_EXAMPLES OFF) -set(wxBUILD_SHARED OFF) -set(wxUSE_HTML ON) -set(wxUSE_STREAMS ON) -set(wxBUILD_MSVC_MULTIPROC ON) -set(wxBUILD_USE_STATIC_RUNTIME ON) - -add_subdirectory(include/wxWidgets) add_subdirectory(include/cpp-httplib) add_subdirectory(include/tomlplusplus) @@ -33,7 +25,7 @@ set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG") -add_executable(${PROJECT_NAME} WIN32 +add_executable(${PROJECT_NAME} src/main.cpp include/easyloggingpp/src/easylogging++.cc src/Launcher.cpp include/Launcher.h include/Memory/Hook.h src/Memory/Definitions.cpp include/Memory/Definitions.h @@ -42,7 +34,7 @@ add_executable(${PROJECT_NAME} WIN32 src/Memory/GELua.cpp include/Memory/GELua.h src/Memory/IPC.cpp include/Memory/IPC.h src/Logger.cpp include/Logger.h - src/gui/Gui.cpp + #src/gui/Gui.cpp include/Json.h src/Network/HttpAPI.cpp include/HttpAPI.h src/Network/Server.cpp include/Server.h @@ -56,7 +48,6 @@ if (WIN32) if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug") target_link_libraries(${PROJECT_NAME} PRIVATE discord-rpc) endif() - target_link_libraries(${PROJECT_NAME} PRIVATE wx::net wx::html wx::webview wx::core wx::base) target_link_libraries(${PROJECT_NAME} PRIVATE ZLIB::ZLIB OpenSSL::SSL OpenSSL::Crypto ws2_32 Dbghelp comsuppw minhook::minhook nlohmann_json nlohmann_json::nlohmann_json) diff --git a/include/Launcher.h b/include/Launcher.h index d9efdc2..48de5a0 100644 --- a/include/Launcher.h +++ b/include/Launcher.h @@ -24,16 +24,16 @@ typedef HKEY__* HKEY; class Launcher { public: // constructors - Launcher(); + Launcher(int argc, char* argv[]); ~Launcher(); public: // available functions static void StaticAbort(Launcher* Instance = nullptr); std::string Login(const std::string& fields); void SendIPC(const std::string& Data, bool core = true); + void LoadConfig(const fs::path& conf); void RunDiscordRPC(); void WaitForGame(); - void LoadConfig(); void LaunchGame(); void CheckKey(); void SetupMOD(); @@ -53,6 +53,7 @@ class Launcher { private: // functions void HandleIPC(const std::string& Data); + fs::path GetBeamNGProfile(); void UpdatePresence(); void RichPresence(); void WindowsInit(); @@ -63,16 +64,14 @@ class Launcher { public: // variables static inline std::thread EntryThread{}; - static inline VersionParser SupportedVersion{"0.26.1.0"}; static inline std::string Version{"3.0"}; static inline std::string FullVersion{Version + ".0"}; private: // variables uint32_t GamePID{0}; - bool EnableUI = true; fs::path MPUserPath{}; fs::path BeamUserPath{}; - fs::path LauncherCache{}; + fs::path LauncherCache{"Resources"}; int64_t DiscordTime{}; bool LoginAuth = false; fs::path CurrentPath{}; @@ -97,10 +96,4 @@ class ShutdownException : public std::runtime_error { runtime_error(message){}; }; -struct UIData { - static inline std::string GamePath, ProfilePath, CachePath, Build, PublicKey, UserRole, Username, GameVer, ConfigPath; - static inline bool LoginAuth{false}, Console{false}, UI; -}; -void UpdateKey(const std::string& newKey); -int entry(); diff --git a/src/Config.cpp b/src/Config.cpp index acf9e49..94d0686 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -3,46 +3,39 @@ /// Copyright (c) 2021-present Anonymous275 read the LICENSE file for more info. /// + #include #include "Launcher.h" #include "Logger.h" +#include +#include +#include +#include -void Launcher::LoadConfig() { - if (fs::exists(UIData::ConfigPath)) { - toml::parse_result config = toml::parse_file(UIData::ConfigPath); - auto ui = config["UI"]; +void Launcher::LoadConfig(const fs::path& conf) { // check if json (issue) + if (fs::is_regular_file(conf)) { + if(fs::is_empty(conf)) { + fs::remove(conf); + LoadConfig(conf); + } + toml::parse_result config = toml::parse_file(conf.string()); auto build = config["Build"]; - auto GamePath = config["GamePath"]; auto ProfilePath = config["ProfilePath"]; auto CachePath = config["CachePath"]; - EnableUI = false; - /*if (ui.is_boolean()) { - EnableUI = ui.as_boolean()->get(); - } else LOG(ERROR) << "Failed to get 'UI' boolean from config";*/ - // Default -1 / Release 1 / EA 2 / Dev 3 / Custom 3 if (build.is_string()) { TargetBuild = build.as_string()->get(); for (char& c : TargetBuild) c = char(tolower(c)); } else LOG(ERROR) << "Failed to get 'Build' string from config"; - if (GamePath.is_string()) { - if (!GamePath.as_string()->get().empty()) { - BeamRoot = GamePath.as_string()->get(); - } else throw ShutdownException("GamePath cannot be empty"); - } else LOG(ERROR) << "Failed to get 'GamePath' string from config"; - - if (ProfilePath.is_string()) { - if (!UIData::GameVer.empty()) { - auto GameVer = VersionParser(UIData::GameVer).split; - - if (!ProfilePath.as_string()->get().empty()) { - BeamUserPath = fs::path(ProfilePath.as_string()->get()) / (GameVer[0] + '.' + GameVer[1]); - MPUserPath = BeamUserPath / "mods" / "multiplayer"; - } else throw ShutdownException("ProfilePath cannot be empty"); - } else throw ShutdownException ("Check game path in config"); - } else LOG(ERROR) << "Failed to get 'ProfilePath' string from config"; + if (ProfilePath.is_string() && !ProfilePath.as_string()->get().empty()) { + BeamUserPath = fs::path(ProfilePath.as_string()->get()); + } else { + LOG(ERROR) << "'ProfilePath' string from config is empty defaulting"; + BeamUserPath = GetBeamNGProfile(); + } + MPUserPath = BeamUserPath / "mods" / "multiplayer"; if (CachePath.is_string()) { if (!CachePath.as_string()->get().empty()) { @@ -50,9 +43,95 @@ void Launcher::LoadConfig() { } else throw ShutdownException("CachePath cannot be empty"); } else LOG(ERROR) << "Failed to get 'CachePath' string from config"; - BeamVersion = UIData::GameVer; } else { - LOG(FATAL) << "Failed to find config on disk!"; - throw ShutdownException("Fatal Error"); + auto GameProfile = GetBeamNGProfile(); + std::ofstream tml(conf); + if (tml.is_open()) { + tml << "Build = 'Default'\n" + "CachePath = 'Resources'\n" + "ProfilePath = '" + << GameProfile.string() << "'"; + + tml.close(); + LoadConfig(conf); + } else LOG(ERROR) << "Failed to create config file"; } } + +fs::path Launcher::GetBeamNGProfile() { + + HKEY BeamNG; + fs::path ProfilePath; + LONG RegRes = + RegOpenKeyExA(HKEY_CURRENT_USER, R"(Software\BeamNG\BeamNG.drive)", 0, + KEY_READ, &BeamNG); + if (RegRes == ERROR_SUCCESS) { + ProfilePath = QueryValue(BeamNG, "userpath_override"); + RegCloseKey(BeamNG); + } + + if (ProfilePath.empty()) { + PWSTR folderPath = nullptr; + HRESULT hr = + SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr, &folderPath); + + if (FAILED(hr)) { + throw ShutdownException("Please launch the game at least once, failed to read registry key Software\\BeamNG\\BeamNG.drive"); + } else { + ProfilePath = fs::path(folderPath)/"BeamNG.drive"; + CoTaskMemFree(folderPath); + } + } + + /////Load latest link + + if (CoInitializeEx(nullptr, COINIT_MULTITHREADED) != S_OK) { + throw ShutdownException("Failed to read link: CoInitializeEx"); + } + + HRESULT rc; + + IShellLink* iShellLink; + rc = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, + IID_IShellLink, (LPVOID*)&iShellLink); + + if (FAILED(rc)) { + throw ShutdownException("Failed to read link: CoCreateInstance"); + } + + IPersistFile* iPersistFile; + + rc = iShellLink->QueryInterface(IID_IPersistFile, (LPVOID*)&iPersistFile); + + if (FAILED(rc)) { + throw ShutdownException("Failed to read link: QueryInterface(IID_IPersistFile)"); + } + + rc = iPersistFile->Load((ProfilePath/"latest.lnk").wstring().c_str(), STGM_READ); + + if (FAILED(rc)) { + throw ShutdownException("Failed to read link: iPersistFile->Load()"); + } + + rc = iShellLink->Resolve(nullptr, 0); + + if (FAILED(rc)) { + throw ShutdownException("Failed to read link: IShellLink failed to resolve"); + } + + std::wstring linkTarget(MAX_PATH, '\x00'); + rc = iShellLink->GetPath(&linkTarget[0], MAX_PATH, nullptr, SLGP_SHORTPATH); + + if (FAILED(rc)) { + throw ShutdownException("Failed to read link: IShellLink failed to get path"); + } + + linkTarget.resize(linkTarget.find(L'\000')); + + iPersistFile->Release(); + iShellLink->Release(); + + BeamVersion = std::filesystem::path(linkTarget).filename().string(); + LOG(INFO) << "Found BeamNG profile " << BeamVersion; + return ProfilePath/BeamVersion; +} diff --git a/src/Launcher.cpp b/src/Launcher.cpp index 7d776d2..32d5300 100644 --- a/src/Launcher.cpp +++ b/src/Launcher.cpp @@ -21,9 +21,10 @@ LONG WINAPI CrashHandler(EXCEPTION_POINTERS* p) { return EXCEPTION_EXECUTE_HANDLER; } -Launcher::Launcher() : +Launcher::Launcher(int argc, char* argv[]) : CurrentPath(std::filesystem::current_path()), DiscordMessage("Just launched") { + Log::Init(); Shutdown.store(false); Exit.store(false); Launcher::StaticAbort(this); @@ -31,6 +32,8 @@ Launcher::Launcher() : WindowsInit(); SetUnhandledExceptionFilter(CrashHandler); LOG(INFO) << "Starting Launcher V" << FullVersion; + if (argc > 1) LoadConfig(fs::current_path() / argv[1]); + else LoadConfig(fs::current_path() / "Launcher.toml"); } void Launcher::Abort() { @@ -88,7 +91,7 @@ void Launcher::WindowsInit() { } void Launcher::LaunchGame() { - VersionParser GameVersion(BeamVersion); + /*VersionParser GameVersion(BeamVersion); if (GameVersion.data[1] > SupportedVersion.data[1]) { LOG(FATAL) << "BeamNG V" << BeamVersion << " not yet supported, please wait until we update BeamMP!"; @@ -105,7 +108,9 @@ void Launcher::LaunchGame() { LOG(WARNING) << "BeamNG V" << BeamVersion << " is slightly older than recommended, this might cause issues!"; - } + }*/ + + if (Memory::GetBeamNGPID({}) == 0) { if(Memory::GetLauncherPID({GetCurrentProcessId()}) != 0) { @@ -225,5 +230,8 @@ const std::string& Launcher::getPublicKey() { } const fs::path& Launcher::getCachePath() { + if (!fs::exists(LauncherCache)) { + fs::create_directories(LauncherCache); + } return LauncherCache; } diff --git a/src/Logger.cpp b/src/Logger.cpp index 9db5b8d..f786321 100644 --- a/src/Logger.cpp +++ b/src/Logger.cpp @@ -13,7 +13,6 @@ void Log::Init() { Conf.setGlobally(ConfigurationType::Format, "%datetime{[%d/%M/%y %H:%m:%s]} [%level] %msg"); Conf.setGlobally(ConfigurationType::LogFlushThreshold, "2"); - Conf.setGlobally(ConfigurationType::ToStandardOutput, "false"); Conf.set(Level::Verbose, ConfigurationType::Format, DFormat); Conf.set(Level::Debug, ConfigurationType::Format, DFormat); Conf.set(Level::Trace, ConfigurationType::Format, DFormat); diff --git a/src/Network/HttpAPI.cpp b/src/Network/HttpAPI.cpp index c1a2783..7cb6a1d 100644 --- a/src/Network/HttpAPI.cpp +++ b/src/Network/HttpAPI.cpp @@ -87,16 +87,14 @@ bool HTTP::ProgressBar(size_t c, size_t t) { if (isDownload) { static double progress_bar_adv; progress_bar_adv = round(double(c) / double(t) * 25); - if (UIData::Console) { - std::cout << "\r"; - std::cout << "Progress: [ "; - std::cout << round(double(c) / double(t) * 100); - std::cout << "% ] ["; - int i; - for (i = 0; i <= progress_bar_adv; i++) std::cout << "#"; - for (i = 0; i < 25 - progress_bar_adv; i++) std::cout << "."; - std::cout << "]"; - } + std::cout << "\r"; + std::cout << "Progress: [ "; + std::cout << round(double(c) / double(t) * 100); + std::cout << "% ] ["; + int i; + for (i = 0; i <= progress_bar_adv; i++) std::cout << "#"; + for (i = 0; i < 25 - progress_bar_adv; i++) std::cout << "."; + std::cout << "]"; } if (Launcher::Terminated()) { CliRef.load()->stop(); @@ -117,9 +115,8 @@ bool HTTP::Download(const std::string& IP, const std::string& Path, DownloadProg if (Ret.empty()) return false; - if (UIData::Console) { - std::cout << "\n"; - } + std::cout << "\n"; + std::ofstream File(Path, std::ios::binary); if (File.is_open()) { diff --git a/src/Network/Resources.cpp b/src/Network/Resources.cpp index 887f684..8c5d529 100644 --- a/src/Network/Resources.cpp +++ b/src/Network/Resources.cpp @@ -31,11 +31,6 @@ std::vector Split(const std::string& String, return Val; } -void CheckForDir() { - if (!fs::exists("Resources")) { - _wmkdir(L"Resources"); - } -} void Server::WaitForConfirm() { while (!Terminate && !ModLoaded) { @@ -218,7 +213,7 @@ void Server::SyncResources() { std::string Ret = Auth(); if (Ret.empty()) return; LOG(INFO) << "Checking Resources..."; - CheckForDir(); + std::vector list = Split(Ret, ";"); std::vector FNames(list.begin(), @@ -281,7 +276,7 @@ void Server::SyncResources() { continue; } else remove(a.c_str()); } - CheckForDir(); + std::string FName = a.substr(a.find_last_of("/\\")+1); do { TCPSend("f" + *FN); diff --git a/src/gui/Gui.cpp b/src/gui/Gui.cpp deleted file mode 100644 index 771ea04..0000000 --- a/src/gui/Gui.cpp +++ /dev/null @@ -1,1010 +0,0 @@ -/// -/// Created by Anonymous275 on 12/27/21 -/// Copyright (c) 2021-present Anonymous275 read the LICENSE file for more info. -/// -// clang-format off -#include -#ifndef WX_PRECOMP -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "Json.h" -#include "Launcher.h" -#include "Logger.h" -#include "HttpAPI.h" -#include -#endif -// clang-format on - -/////////// Inherit App class /////////// -class MyApp : public wxApp { - public: - bool OnInit() override; -}; - -/////////// AccountFrame class /////////// -class MyAccountFrame : public wxFrame { - public: - MyAccountFrame(); - - private: - static inline wxTextCtrl *ctrlUsername, *ctrlPassword; - bool DarkMode = wxSystemSettings::GetAppearance().IsDark(); - void OnClickRegister(wxCommandEvent& event); - void OnClickLogout(wxCommandEvent& event); - void OnClickLogin(wxCommandEvent& event); - wxDECLARE_EVENT_TABLE(); -}; - -/////////// MainFrame class /////////// -class MyMainFrame : public wxFrame { - public: - MyMainFrame(); - static void GameVersionLabel(); - static inline MyAccountFrame* AccountFrame; - static inline MyMainFrame* MainFrameInstance; - static inline std::thread UpdateThread; - wxGauge* UpdateBar; - wxStaticText* txtUpdate; - wxBitmapButton* BitAccount; - void OnClickAccount(wxCommandEvent& event); - wxButton* btnLaunch; - - private: - wxStaticText* txtStatusResult; - static inline wxStaticText *txtGameVersion, *txtPlayers, *txtModVersion, *txtServers; - - bool DarkMode = wxSystemSettings::GetAppearance().IsDark(); - void GetStats(); - - void OnClickSettings(wxCommandEvent& event); - void OnClickLaunch(wxCommandEvent& event); - void OnClickLogo(wxCommandEvent& event); - wxDECLARE_EVENT_TABLE(); -}; - -/////////// SettingsFrame class /////////// -class MySettingsFrame : public wxFrame { - public: - MySettingsFrame(); - void UpdateInfo(); - void UpdateGameDirectory(const std::string& path); - void UpdateProfileDirectory(const std::string& path); - void UpdateCacheDirectory(const std::string& path); - - private: - wxCheckBox* checkConsole; - wxDirPickerCtrl *ctrlGameDirectory, *ctrlProfileDirectory, *ctrlCacheDirectory; - wxChoice* choiceController; - - bool DarkMode = wxSystemSettings::GetAppearance().IsDark(); - - void OnClickConsole(wxCommandEvent& event); - void OnChangedGameDir(wxFileDirPickerEvent& event); - void OnChangedProfileDir(wxFileDirPickerEvent& event); - void OnChangedCacheDir(wxFileDirPickerEvent& event); - void OnChangedBuild(wxCommandEvent& event); - void OnAutoDetectGame(wxCommandEvent& event); - void OnAutoDetectProfile(wxCommandEvent& event); - void OnResetCache(wxCommandEvent& event); - wxDECLARE_EVENT_TABLE(); -}; - -// clang-format off -/////////// Event Tables /////////// -// MainFrame (ID range 1 to 99): -wxBEGIN_EVENT_TABLE(MyMainFrame, wxFrame) - EVT_BUTTON(1, MyMainFrame::OnClickAccount) - EVT_BUTTON(2, MyMainFrame::OnClickSettings) - EVT_BUTTON(3, MyMainFrame::OnClickLaunch) - EVT_BUTTON(4, MyMainFrame::OnClickLogo) -wxEND_EVENT_TABLE() - -// AccountFrame (ID range 100 to 199): -wxBEGIN_EVENT_TABLE(MyAccountFrame, wxFrame) - EVT_BUTTON(100, MyAccountFrame::OnClickLogout) - EVT_BUTTON(101, MyAccountFrame::OnClickRegister) - EVT_BUTTON(102, MyAccountFrame::OnClickLogin) -wxEND_EVENT_TABLE() - -// SettingsFrame (ID range 200 to 299): -wxBEGIN_EVENT_TABLE(MySettingsFrame, wxFrame) - EVT_DIRPICKER_CHANGED(200, MySettingsFrame::OnChangedGameDir) - EVT_DIRPICKER_CHANGED(201, MySettingsFrame::OnChangedProfileDir) - EVT_DIRPICKER_CHANGED(202, MySettingsFrame::OnChangedCacheDir) - EVT_BUTTON(203, MySettingsFrame::OnAutoDetectGame) - EVT_BUTTON(204, MySettingsFrame::OnAutoDetectProfile) - EVT_BUTTON(205, MySettingsFrame::OnResetCache) - EVT_CHOICE(206, MySettingsFrame::OnChangedBuild) - EVT_CHECKBOX(207, MySettingsFrame::OnClickConsole) -wxEND_EVENT_TABLE(); -// clang-format on - -/////////// Get Stats Function /////////// -void MyMainFrame::GetStats() { - std::string results = HTTP::Get("https://backend.beammp.com/stats_raw"); - - nlohmann::json jf = nlohmann::json::parse(results, nullptr, false); - if (!jf.is_discarded() && !jf.empty()) { - txtPlayers->SetLabel(to_string(jf["Players"])); - txtServers->SetLabel(to_string(jf["PublicServers"])); - - if (jf["Players"].get() < 559) - txtPlayers->SetForegroundColour("green"); - else - txtPlayers->SetForegroundColour(wxColour(255, 173, 0)); - - if (jf["PublicServers"].get() > 679) - txtServers->SetForegroundColour("green"); - else - txtServers->SetForegroundColour(wxColour(255, 173, 0)); - - if (!jf["ModVersion"].is_null()) { - txtModVersion->SetForegroundColour("green"); - txtModVersion->SetLabel(to_string(jf["ModVersion"])); - } else { - txtModVersion->SetLabel("NA"); - txtModVersion->SetForegroundColour("red"); - } - } else { - txtPlayers->SetLabel("NA"); - txtPlayers->SetForegroundColour("red"); - txtServers->SetLabel("NA"); - txtServers->SetForegroundColour("red"); - txtModVersion->SetLabel("NA"); - txtModVersion->SetForegroundColour("red"); - } -} - -/////////// Update Info Function /////////// -void MySettingsFrame::UpdateInfo() { - ctrlGameDirectory->SetPath(UIData::GamePath); - ctrlProfileDirectory->SetPath(UIData::ProfilePath); - ctrlCacheDirectory->SetPath(UIData::CachePath); - checkConsole->SetValue(UIData::Console); - choiceController->SetStringSelection(UIData::Build); -} - -/////////// Update Game Directory Function /////////// -void MySettingsFrame::UpdateGameDirectory(const std::string& path) { - ctrlGameDirectory->SetPath(path); - UIData::GamePath = path; - MyMainFrame::GameVersionLabel(); -} - -/////////// Update Profile Directory Function /////////// -void MySettingsFrame::UpdateProfileDirectory(const std::string& path) { - ctrlProfileDirectory->SetPath(path); - UIData::ProfilePath = path; -} - -/////////// Update Cache Directory Function /////////// -void MySettingsFrame::UpdateCacheDirectory(const std::string& path) { - ctrlCacheDirectory->SetPath(path); - UIData::CachePath = path; -} - -/////////// UpdateConfig Function /////////// -template -void UpdateConfig(const std::string& key, ValueType&& value) { - if (fs::exists(UIData::ConfigPath)) { - toml::parse_result config = toml::parse_file(UIData::ConfigPath); - config.insert_or_assign(key, value); - - std::ofstream tml(UIData::ConfigPath); - if (tml.is_open()) { - tml << config; - tml.close(); - } else wxMessageBox("Failed to modify config file", "Error"); - } -} - -/////////// Auto Detect Game Function /////////// -std::string AutoDetectGame() { - HKEY BeamNG; - std::string GamePath; - LONG RegRes = - RegOpenKeyExA(HKEY_CURRENT_USER, R"(Software\BeamNG\BeamNG.drive)", 0, - KEY_READ, &BeamNG); - if (RegRes == ERROR_SUCCESS) { - GamePath = Launcher::QueryValue(BeamNG, "rootpath"); - RegCloseKey(BeamNG); - } - if (!GamePath.empty()) { - if (GamePath.ends_with('\\')) GamePath.pop_back(); - UpdateConfig("GamePath", GamePath); - return GamePath; - } else { - wxMessageBox("Please launch the game at least once, failed to read registry key Software\\BeamNG\\BeamNG.drive", "Error"); - return ""; - } -} - -/////////// Auto Detect Profile Function /////////// -std::string AutoDetectProfile() { - HKEY BeamNG; - std::string ProfilePath; - LONG RegRes = - RegOpenKeyExA(HKEY_CURRENT_USER, R"(Software\BeamNG\BeamNG.drive)", 0, - KEY_READ, &BeamNG); - if (RegRes == ERROR_SUCCESS) { - ProfilePath = Launcher::QueryValue(BeamNG, "userpath_override"); - RegCloseKey(BeamNG); - } - if (ProfilePath.empty()) { - PWSTR folderPath = nullptr; - HRESULT hr = - SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr, &folderPath); - - if (!SUCCEEDED(hr)) { - wxMessageBox( - "Please launch the game at least once, failed to read registry key Software\\BeamNG\\BeamNG.drive", - "Error"); - return ""; - } else { - _bstr_t bstrPath(folderPath); - std::string Path((char*)bstrPath); - CoTaskMemFree(folderPath); - ProfilePath = Path + "\\BeamNG.drive"; - } - } - UpdateConfig("ProfilePath", ProfilePath); - return ProfilePath; -} - -/////////// Reset Cache Function /////////// -std::string ResetCache() { - std::string CachePath = fs::current_path().append("Resources").string(); - UpdateConfig("CachePath", CachePath); - return CachePath; -} - -/////////// Load Config Function /////////// -void LoadConfig() { - if (fs::exists(UIData::ConfigPath)) { - toml::parse_result config = toml::parse_file(UIData::ConfigPath); - auto UI = config["UI"]; - auto Build = config["Build"]; - auto GamePath = config["GamePath"]; - auto ProfilePath = config["ProfilePath"]; - auto CachePath = config["CachePath"]; - auto Console = config["Console"]; - - if (GamePath.is_string()) { - UIData::GamePath = GamePath.as_string()->get(); - } else wxMessageBox("Game path not found!", "Error"); - - if (ProfilePath.is_string()) { - UIData::ProfilePath = ProfilePath.as_string()->get(); - } else wxMessageBox("Profile path not found!", "Error"); - - if (CachePath.is_string()) { - UIData::CachePath = CachePath.as_string()->get(); - } else wxMessageBox("Cache path not found!", "Error"); - - UIData::Console = true; - /*if (Console.is_boolean()) { - UIData::Console = Console.as_boolean()->get(); - } else wxMessageBox("Unable to retrieve console state!", "Error");*/ - - if (Build.is_string()) { - UIData::Build = Build.as_string()->get(); - } else wxMessageBox("Unable to retrieve build state!", "Error"); - - - UIData::UI = false; - /*if (UI.is_boolean()) { - UIData::UI = UI.as_boolean()->get(); - } else wxMessageBox("Unable to retrieve UI state!", "Error");*/ - - } else { - std::ofstream tml(UIData::ConfigPath); - if (tml.is_open()) { - tml << //"UI = true\n" - "Build = 'Default'\n" - "GamePath = ''\n" - "ProfilePath = ''\n" - "CachePath = ''"; - //"Console = false"; - tml.close(); - AutoDetectGame(); - AutoDetectProfile(); - ResetCache(); - LoadConfig(); - } else wxMessageBox("Failed to create config file", "Error"); - } -} - -/////////// Login Function /////////// -bool Login(const std::string& fields) { - if (fields == "LO") { - UIData::LoginAuth = false; - UpdateKey(""); - return false; - } - std::string Buffer = HTTP::Post("https://auth.beammp.com/userlogin", fields); - Json d = Json::parse(Buffer, nullptr, false); - - if (Buffer == "-1") { - wxMessageBox("Failed to communicate with the auth system!", "Error"); - return false; - } - - if (Buffer.at(0) != '{' || d.is_discarded()) { - wxMessageBox( - "Invalid answer from authentication servers, please try again later!", - "Error"); - return false; - } - - if (!d["success"].is_null() && d["success"].get()) { - UIData::LoginAuth = true; - if (!d["private_key"].is_null()) { - UpdateKey(d["private_key"].get()); - } - if (!d["public_key"].is_null()) { - UIData::PublicKey = d["public_key"].get(); - } - if (!d["username"].is_null()) { - UIData::Username = d["username"].get(); - } - if (!d["role"].is_null()) { - UIData::UserRole = d["role"].get(); - } - return true; - } else if (!d["message"].is_null()) wxMessageBox(d["message"].get(), "Error"); - return false; -} - -/////////// Check Key Function /////////// -void CheckKey() { - if (fs::exists("key") && fs::file_size("key") < 100) { - std::ifstream Key("key"); - if (Key.is_open()) { - auto Size = fs::file_size("key"); - std::string Buffer(Size, 0); - Key.read(&Buffer[0], std::streamsize(Size)); - Key.close(); - - for (char& c : Buffer) { - if (!std::isalnum(c) && c != '-') { - UpdateKey(""); - return; - } - } - - Buffer = HTTP::Post("https://auth.beammp.com/userlogin", - R"({"pk":")" + Buffer + "\"}"); - - Json d = Json::parse(Buffer, nullptr, false); - if (Buffer == "-1" || Buffer.at(0) != '{' || d.is_discarded()) { - LOG(ERROR) << Buffer; - wxMessageBox("Couldn't connect to auth server, you might be offline!", "Warning", wxICON_WARNING); - UpdateKey(""); - return; - } - if (d["success"].get()) { - UIData::LoginAuth = true; - UpdateKey(d["private_key"].get()); - UIData::PublicKey = d["public_key"].get(); - UIData::UserRole = d["role"].get(); - UIData::Username = d["username"].get(); - } else UpdateKey(""); - } else UpdateKey(""); - } else UpdateKey(""); -} - -void WindowsConsole(bool isChecked); -void UpdateCheck(); -/////////// OnInit Function /////////// -bool MyApp::OnInit() { - if (!fs::exists("icons")) { - fs::create_directory("icons"); - std::string picture = HTTP::Get("https://forum.beammp.com/uploads/default/original/3X/c/d/cd7f8f044efe85e5a0f7bc1d296775438e800488.png"); - std::ofstream File("icons/BeamMP_white.png", std::ios::binary); - if (File.is_open()) { - File << picture; - File.close(); - } - picture = HTTP::Get("https://forum.beammp.com/uploads/default/original/3X/b/9/b95f01469238d3ec92163d1b0be79cdd1662fdcd.png"); - File.open("icons/BeamMP_black.png", std::ios::binary); - if (File.is_open()) { - File << picture; - File.close(); - } - picture = HTTP::Get("https://forum.beammp.com/uploads/default/original/3X/6/f/6f0da341a1e570dc973d7251ecd8b4bb3c17eb65.png"); - File.open("icons/default.png", std::ios::binary); - if (File.is_open()) { - File << picture; - File.close(); - } - } - - CheckKey(); - - WindowsConsole(UIData::Console); - Log::ConsoleOutput(UIData::Console); - - auto* MainFrame = new MyMainFrame(); - MyMainFrame::MainFrameInstance = MainFrame; - MyMainFrame::UpdateThread = std::thread(UpdateCheck); - - MainFrame->SetIcon(wxIcon("icons/BeamMP_black.png", wxBITMAP_TYPE_PNG)); - - // Set MainFrame properties: - MainFrame->SetSize(1000, 650); - MainFrame->Center(); - - if (wxSystemSettings::GetAppearance().IsDark()) { - MainFrame->SetBackgroundColour(wxColour(40, 40, 40)); - MainFrame->SetForegroundColour(wxColour(255, 255, 255)); - } else { - MainFrame->SetBackgroundColour(wxColour("white")); - MainFrame->SetForegroundColour(wxColour("white")); - } - wxFileSystem::AddHandler(new wxInternetFSHandler); - MainFrame->Show(true); - return true; -} - -/////////// Windows Console Function /////////// -void WindowsConsole(bool isChecked) { - if (isChecked) { - AllocConsole(); - FILE* pNewStdout = nullptr; - FILE* pNewStderr = nullptr; - FILE* pNewStdin = nullptr; - - ::freopen_s(&pNewStdout, "CONOUT$", "w", stdout); - ::freopen_s(&pNewStderr, "CONOUT$", "w", stderr); - ::freopen_s(&pNewStdin, "CONIN$", "r", stdin); - } else { - FreeConsole(); - ::fclose(stdout); - ::fclose(stderr); - ::fclose(stdin); - } - // Clear the error state for all the C++ standard streams. Attempting to accessing the streams before they refer - // to a valid target causes the stream to enter an error state. Clearing the error state will fix this problem, - // which seems to occur in newer version of Visual Studio even when the console has not been read from or written - // to yet. - std::cout.clear(); - std::cerr.clear(); - std::cin.clear(); - std::wcout.clear(); - std::wcerr.clear(); - std::wcin.clear(); -} - -/////////// Read json Function /////////// -std::string jsonRead() { - fs::path path = fs::path(UIData::GamePath).append("integrity.json"); - if (fs::exists(path)) { - std::ifstream ifs(path); - nlohmann::json jf = nlohmann::json::parse(ifs, nullptr, false); - if (!jf.is_discarded() && !jf.empty()) return jf["version"]; - } else wxMessageBox("Couldn't read game version, check game path in settings", "Error"); - return ""; -} - -/////////// Picture Type Function /////////// -// JPG 0 / PNG 1 -std::string PictureType(const std::string& picture) { - for (int i = 0; i < 15; i++) { - if (picture[i] == 'J') - return ".jpg"; - else if (picture[i] == 'P' && picture[i + 1] == 'N') - return ".png"; - } - return ""; -} - -/////////// Get Picture Name Function /////////// -std::string GetPictureName() { - for (const auto& entry : fs::recursive_directory_iterator("icons")) { - if (entry.path().filename().string().find(UIData::Username) != std::string::npos) { - return entry.path().string(); - } - } - return ""; -} - -/////////// Progress Bar Function /////////// -bool ProgressBar(size_t c, size_t t) { - int Percent = int(round(double(c) / double(t) * 100)); - MyMainFrame::MainFrameInstance->UpdateBar->SetValue(Percent); - MyMainFrame::MainFrameInstance->txtUpdate->SetLabel("Downloading " + std::to_string(Percent) + "%"); - return true; -} - -/////////// Admin Relaunch Functions /////////// -void AdminRelaunch(const std::string& executable) { - system("cls"); - ShellExecuteA(nullptr, "runas", executable.c_str(), nullptr, - nullptr, SW_SHOWNORMAL); - ShowWindow(GetConsoleWindow(), 0); - throw ShutdownException("Launcher Relaunching"); -} - -/////////// Relaunch Functions /////////// -void Relaunch(const std::string& executable) { - ShellExecuteA(nullptr, "open", executable.c_str(), nullptr, - nullptr, SW_SHOWNORMAL); - ShowWindow(GetConsoleWindow(), 0); - std::this_thread::sleep_for(std::chrono::seconds(1)); - throw ShutdownException("Launcher Relaunching"); -} - -/////////// Update Check Function /////////// -void UpdateCheck() { - MyMainFrame::MainFrameInstance->btnLaunch->Disable(); - std::string link; - std::string HTTP = HTTP::Get("https://beammp.com/builds/launcher?version=true"); - bool fallback = false; - if (HTTP.find_first_of("0123456789") == std::string::npos) { - HTTP = HTTP::Get("https://backup1.beammp.com/builds/launcher?version=true"); - fallback = true; - if (HTTP.find_first_of("0123456789") == std::string::npos) - wxMessageBox("Primary Servers Offline! sorry for the inconvenience!", "Error"); - } - if (fallback) { - link = "https://backup1.beammp.com/builds/launcher?download=true"; - } else link = "https://beammp.com/builds/launcher?download=true"; - const auto CurrentPath = fs::current_path(); - std::string EP((CurrentPath / "BeamMP-Launcher.exe").string()), Tmp(EP + ".tmp"); - std::string Back((CurrentPath / "BeamMP-Launcher.back").string()); - - if (fs::exists(Back)) remove(Back.c_str()); - - std::string RemoteVer; - for (char& c : HTTP) { - if (std::isdigit(c) || c == '.') { - RemoteVer += c; - } - } - - if (VersionParser(RemoteVer) > VersionParser(Launcher::FullVersion)) { - system("cls"); - MyMainFrame::MainFrameInstance->txtUpdate->SetLabel("Downloading..."); - - if (!HTTP::Download(link, Tmp, ProgressBar)) { - wxMessageBox("Launcher Update failed! trying again...", "Error"); - std::this_thread::sleep_for(std::chrono::seconds(2)); - - if (!HTTP::Download(link, Tmp, ProgressBar)) { - wxMessageBox("Launcher Update failed!", "Error"); - std::this_thread::sleep_for(std::chrono::seconds(5)); - AdminRelaunch(EP); - } - } - - MyMainFrame::MainFrameInstance->Destroy(); - - if (std::rename(EP.c_str(), Back.c_str()) || std::rename(Tmp.c_str(), EP.c_str())) { - LOG(ERROR) << "Failed to create a backup!"; - } - - Relaunch(EP); - } else MyMainFrame::MainFrameInstance->btnLaunch->Enable(); -} - -/////////// Main Frame Content /////////// -MyMainFrame::MyMainFrame() : - wxFrame(nullptr, wxID_ANY, "BeamMP Launcher V3", wxDefaultPosition, wxDefaultSize, - wxMINIMIZE_BOX | wxSYSTEM_MENU | wxCAPTION | wxCLOSE_BOX) { - auto* panel = new wxPanel(this, wxID_ANY, wxPoint(), wxSize(1000, 650)); - - // News: - wxWebView::New()->Create(panel, wxID_ANY, "https://beammp.com", wxPoint(10, 70), wxSize(950, 400)); - auto* txtNews = new wxStaticText(panel, wxID_ANY, wxT("News"), wxPoint(10, 40)); - MyMainFrame::SetFocus(); - - auto* HorizontalLine1 = new wxStaticLine(panel, wxID_ANY, wxPoint(10, 60), wxSize(950, 1)); - auto* HorizontalLine2 = new wxStaticLine(panel, wxID_ANY, wxPoint(10, 480), wxSize(950, 1)); - - // Hyperlinks: - auto* HyperForum = new wxHyperlinkCtrl(panel, wxID_ANY, wxT("Forum"), wxT("https://forum.beammp.com"), wxPoint(10, 10)); - auto* txtSeparator1 = new wxStaticText(panel, wxID_ANY, wxT("|"), wxPoint(55, 10)); - - auto* HyperDiscord = new wxHyperlinkCtrl(panel, wxID_ANY, wxT("Discord"), wxT("https://discord.gg/beammp"), wxPoint(70, 10)); - auto* txtSeparator2 = new wxStaticText(panel, wxID_ANY, wxT("|"), wxPoint(120, 10)); - - auto* HyperGithub = new wxHyperlinkCtrl(panel, wxID_ANY, wxT("GitHub"), wxT("https://github.com/BeamMP"), wxPoint(130, 10)); - auto* txtSeparator3 = new wxStaticText(panel, wxID_ANY, wxT("|"), wxPoint(180, 10)); - - auto* HyperWiki = new wxHyperlinkCtrl(panel, wxID_ANY, wxT("Wiki"), wxT("https://wiki.beammp.com"), wxPoint(195, 10)); - auto* txtSeparator4 = new wxStaticText(panel, wxID_ANY, wxT("|"), wxPoint(230, 10)); - - auto* HyperPatreon = new wxHyperlinkCtrl(panel, wxID_ANY, wxT("Patreon"), wxT("https://www.patreon.com/BeamMP"), wxPoint(240, 10)); - - // Update: - txtUpdate = new wxStaticText(panel, wxID_ANY, wxT("BeamMP V" + Launcher::FullVersion), wxPoint(10, 490)); - - UpdateBar = new wxGauge(panel, wxID_ANY, 100, wxPoint(10, 520), wxSize(127, -1)); - UpdateBar->SetValue(100); - - // Information: - auto* txtGameVersionTitle = new wxStaticText(panel, wxID_ANY, wxT("Game Version: "), wxPoint(160, 490)); - txtGameVersion = new wxStaticText(panel, wxID_ANY, "NA", wxPoint(240, 490)); - - auto* txtPlayersTitle = new wxStaticText(panel, wxID_ANY, wxT("Currently Playing:"), wxPoint(300, 490)); - txtPlayers = new wxStaticText(panel, wxID_ANY, wxT("NA"), wxPoint(400, 490)); - - auto* txtPatreon = new wxStaticText(panel, wxID_ANY, wxT("Special thanks to our Patreon Members!"), wxPoint(570, 490)); - - auto* txtModVersionTitle = new wxStaticText(panel, wxID_ANY, wxT("Mod Version:"), wxPoint(160, 520)); - txtModVersion = new wxStaticText(panel, wxID_ANY, wxT("NA"), wxPoint(235, 520)); - - auto* txtServersTitle = new wxStaticText(panel, wxID_ANY, wxT("Available Servers:"), wxPoint(300, 520)); - txtServers = new wxStaticText(panel, wxID_ANY, wxT("NA"), wxPoint(395, 520)); - - auto* txtStatus = new wxStaticText(panel, wxID_ANY, wxT("Status: "), wxPoint(880, 520)); - txtStatusResult = new wxStaticText(panel, wxID_ANY, wxT("Online"), wxPoint(920, 520)); - - auto* HorizontalLine3 = new wxStaticLine(panel, wxID_ANY, wxPoint(10, 550), wxSize(950, 1)); - - wxInitAllImageHandlers(); - - // Account: - BitAccount = new wxBitmapButton(panel, 1, wxBitmapBundle(wxImage("icons/default.png", wxBITMAP_TYPE_PNG).Scale(45, 45, wxIMAGE_QUALITY_HIGH)), wxPoint(20, 560), wxSize(45, 45)); - std::string PictureString = GetPictureName(); - if (UIData::LoginAuth && fs::exists(PictureString)) - BitAccount->SetBitmap(wxBitmapBundle(wxImage(PictureString).Scale(45, 45, wxIMAGE_QUALITY_HIGH))); - else - BitAccount->SetBitmap(wxBitmapBundle(wxImage("icons/default.png", wxBITMAP_TYPE_PNG).Scale(45, 45, wxIMAGE_QUALITY_HIGH))); - - BitAccount->SetBackgroundColour(wxColour(40, 40, 40)); - BitAccount->SetWindowStyle(wxBORDER_NONE); - // Buttons: - auto btnSettings = new wxButton(panel, 2, wxT("Settings"), wxPoint(730, 570), wxSize(110, 25)); - btnLaunch = new wxButton(panel, 3, wxT("Launch"), wxPoint(850, 570), wxSize(110, 25)); - - GetStats(); - - // UI Colors: - GameVersionLabel(); - if (DarkMode) { - // Text Separators: - txtSeparator1->SetForegroundColour("white"); - txtSeparator2->SetForegroundColour("white"); - txtSeparator3->SetForegroundColour("white"); - txtSeparator4->SetForegroundColour("white"); - - // Texts: - txtNews->SetForegroundColour("white"); - txtUpdate->SetForegroundColour("white"); - txtGameVersionTitle->SetForegroundColour("white"); - txtPlayersTitle->SetForegroundColour("white"); - txtModVersionTitle->SetForegroundColour("white"); - txtServersTitle->SetForegroundColour("white"); - txtPatreon->SetForegroundColour("white"); - txtStatus->SetForegroundColour("white"); - - // Line Separators: - HorizontalLine1->SetForegroundColour("white"); - HorizontalLine2->SetForegroundColour("white"); - HorizontalLine3->SetForegroundColour("white"); - - // Logo: - auto* logo = new wxBitmapButton(panel, 4, wxBitmapBundle(wxImage("icons/BeamMP_white.png", wxBITMAP_TYPE_PNG).Scale(100, 100, wxIMAGE_QUALITY_HIGH)), wxPoint(850, -15), wxSize(100, 100), wxBORDER_NONE); - logo->SetBackgroundColour(wxColour(40, 40, 40)); - } else { - // Logo: - auto* logo = new wxBitmapButton(panel, 4, wxBitmapBundle(wxImage("icons/BeamMP_black.png", wxBITMAP_TYPE_PNG).Scale(100, 100, wxIMAGE_QUALITY_HIGH)), wxPoint(850, -15), wxSize(100, 100), wxBORDER_NONE); - logo->SetBackgroundColour("white"); - } - txtStatusResult->SetForegroundColour("green"); -} - -/////////// Account Frame Content /////////// -MyAccountFrame::MyAccountFrame() : - wxFrame(nullptr, wxID_ANY, "Account Manager", wxDefaultPosition, wxDefaultSize, - wxMINIMIZE_BOX | wxSYSTEM_MENU | wxCAPTION | wxCLOSE_BOX) { - MyAccountFrame::SetFocus(); - auto* handler = new wxPNGHandler; - wxImage::AddHandler(handler); - wxStaticBitmap* image; - image = new wxStaticBitmap(this, wxID_ANY, wxBitmapBundle(wxImage("icons/BeamMP_black.png", wxBITMAP_TYPE_PNG).Scale(120, 120, wxIMAGE_QUALITY_HIGH)), wxPoint(180, 20), wxSize(120, 120)); - auto* panel = new wxPanel(this, wxID_ANY, wxPoint(), wxSize(500, 650)); - std::string PictureString = GetPictureName(); - if (UIData::LoginAuth) { - if (fs::exists(PictureString)) - image->SetBitmap(wxBitmapBundle(wxImage(PictureString).Scale(120, 120, wxIMAGE_QUALITY_HIGH))); - else - image->SetBitmap(wxBitmapBundle(wxImage("icons/default.png", wxBITMAP_TYPE_PNG).Scale(120, 120, wxIMAGE_QUALITY_HIGH))); - - auto* txtName = new wxStaticText(panel, wxID_ANY, wxT("Username: " + UIData::Username), wxPoint(190, 190)); - auto* txtRole = new wxStaticText(panel, wxID_ANY, wxT("Role: " + UIData::UserRole), wxPoint(190, 230)); - auto btnLogout = new wxButton(panel, 100, wxT("Logout"), wxPoint(185, 550), wxSize(110, 25)); - - // UI Colors: - if (DarkMode) { - // Text: - txtName->SetForegroundColour("white"); - txtRole->SetForegroundColour("white"); - } - } else { - image->SetBitmap(wxBitmapBundle(wxImage("icons/default.png", wxBITMAP_TYPE_PNG).Scale(120, 120, wxIMAGE_QUALITY_HIGH))); - - auto* txtLogin = new wxStaticText(panel, wxID_ANY, wxT("Login with your BeamMP account."), wxPoint(150, 200)); - - ctrlUsername = new wxTextCtrl(panel, wxID_ANY, wxT(""), wxPoint(131, 230), wxSize(220, 25)); - ctrlPassword = new wxTextCtrl(panel, wxID_ANY, wxT(""), wxPoint(131, 300), wxSize(220, 25), wxTE_PASSWORD); - ctrlUsername->SetHint("Username / Email"); - ctrlPassword->SetHint("Password"); - - auto btnRegister = new wxButton(panel, 101, wxT("Register"), wxPoint(250, 375), wxSize(110, 25)); - auto btnLogin = new wxButton(panel, 102, wxT("Login"), wxPoint(120, 375), wxSize(110, 25)); - - // UI Colors: - if (DarkMode) { - // Text: - txtLogin->SetForegroundColour("white"); - } - } -} - -/////////// Settings Frame Content /////////// -MySettingsFrame::MySettingsFrame() : - wxFrame(nullptr, wxID_ANY, "Settings", wxDefaultPosition, wxDefaultSize, - wxMINIMIZE_BOX | wxSYSTEM_MENU | wxCAPTION | wxCLOSE_BOX) { - auto* panel = new wxPanel(this, wxID_ANY, wxPoint(), wxSize(500, 650)); - - auto* txtGameDirectory = new wxStaticText(panel, wxID_ANY, wxT("Game Directory: "), wxPoint(30, 100)); - ctrlGameDirectory = new wxDirPickerCtrl(panel, 200, wxEmptyString, wxT("Game Directory"), wxPoint(130, 100), wxSize(300, -1)); - ctrlGameDirectory->SetLabel("GamePath"); - MySettingsFrame::SetFocus(); - auto btnDetectGameDirectory = new wxButton(panel, 203, wxT("Detect"), wxPoint(185, 140), wxSize(90, 25)); - - auto* txtProfileDirectory = new wxStaticText(panel, wxID_ANY, wxT("Profile Directory: "), wxPoint(30, 200)); - ctrlProfileDirectory = new wxDirPickerCtrl(panel, 201, wxEmptyString, wxT("Profile Directory"), wxPoint(130, 200), wxSize(300, -1)); - ctrlProfileDirectory->SetLabel("ProfilePath"); - auto btnDetectProfileDirectory = new wxButton(panel, 204, wxT("Detect"), wxPoint(185, 240), wxSize(90, 25)); - - auto* txtCacheDirectory = new wxStaticText(panel, wxID_ANY, wxT("Download Cache: "), wxPoint(30, 300)); - ctrlCacheDirectory = new wxDirPickerCtrl(panel, 202, wxEmptyString, wxT("Download Cache"), wxPoint(130, 300), wxSize(300, -1)); - ctrlCacheDirectory->SetLabel("CachePath"); - auto btnCacheDirectory = new wxButton(panel, 205, wxT("Reset"), wxPoint(185, 340), wxSize(90, 25)); - - auto* txtBuild = new wxStaticText(panel, wxID_ANY, wxT("Build: "), wxPoint(30, 400)); - wxArrayString BuildChoices; - BuildChoices.Add("Default"); - BuildChoices.Add("Release"); - BuildChoices.Add("EA"); - BuildChoices.Add("Dev"); - choiceController = new wxChoice(panel, 206, wxPoint(85, 400), wxSize(120, 20), BuildChoices); - choiceController->Select(0); - - checkConsole = new wxCheckBox(panel, 207, " Show Console", wxPoint(30, 450)); - - // UI Colors: - if (DarkMode) { - // Text: - txtGameDirectory->SetForegroundColour("white"); - txtProfileDirectory->SetForegroundColour("white"); - txtCacheDirectory->SetForegroundColour("white"); - txtBuild->SetForegroundColour("white"); - checkConsole->SetForegroundColour("white"); - - // Style: - ctrlCacheDirectory->SetWindowStyle(wxBORDER_NONE); - ctrlProfileDirectory->SetWindowStyle(wxBORDER_NONE); - ctrlGameDirectory->SetWindowStyle(wxBORDER_NONE); - } -} - -/////////// Game Version Label Function /////////// -void MyMainFrame::GameVersionLabel() { - std::string read = jsonRead(); - if (!read.empty()) { - txtGameVersion->SetLabel(read); - UIData::GameVer = read; - VersionParser CurrentVersion(read); - if (CurrentVersion > Launcher::SupportedVersion) - txtGameVersion->SetForegroundColour(wxColour(255, 173, 0)); - else if (CurrentVersion < Launcher::SupportedVersion) - txtGameVersion->SetForegroundColour("red"); - else txtGameVersion->SetForegroundColour("green"); - } else { - txtGameVersion->SetLabel(wxT("NA")); - txtGameVersion->SetForegroundColour("red"); - UIData::GameVer = ""; - } -} - -/////////// OnClick Account Event /////////// -void MyMainFrame::OnClickAccount(wxCommandEvent& event WXUNUSED(event)) { - AccountFrame = new MyAccountFrame(); - AccountFrame->SetSize(500, 650); - AccountFrame->Center(); - AccountFrame->SetIcon(wxIcon("icons/BeamMP_black.png", wxBITMAP_TYPE_PNG)); - - if (wxSystemSettings::GetAppearance().IsDark()) { - AccountFrame->SetBackgroundColour(wxColour(40, 40, 40)); - AccountFrame->SetForegroundColour(wxColour(255, 255, 255)); - } else { - AccountFrame->SetBackgroundColour(wxColour("white")); - AccountFrame->SetForegroundColour(wxColour("white")); - } - AccountFrame->Show(); -} - -/////////// OnClick Settings Event /////////// -void MyMainFrame::OnClickSettings(wxCommandEvent& event WXUNUSED(event)) { - auto* SettingsFrame = new MySettingsFrame(); - SettingsFrame->SetSize(500, 650); - SettingsFrame->Center(); - SettingsFrame->SetIcon(wxIcon("icons/BeamMP_black.png", wxBITMAP_TYPE_PNG)); - - if (wxSystemSettings::GetAppearance().IsDark()) { - SettingsFrame->SetBackgroundColour(wxColour(40, 40, 40)); - SettingsFrame->SetForegroundColour(wxColour(255, 255, 255)); - } else { - SettingsFrame->SetBackgroundColour(wxColour("white")); - SettingsFrame->SetForegroundColour(wxColour("white")); - } - SettingsFrame->UpdateInfo(); - SettingsFrame->Show(true); -} - -/////////// OnClick Launch Event /////////// -void MyMainFrame::OnClickLaunch(wxCommandEvent& event WXUNUSED(event)) { - static bool FirstTime = true; - if (UIData::GameVer.empty()) { - wxMessageBox("Game path is invalid please check settings", "Error"); - return; - } - if (Launcher::EntryThread.joinable()) Launcher::EntryThread.join(); - Launcher::EntryThread = std::thread([&]() { - entry(); - std::this_thread::sleep_for(std::chrono::seconds(2)); - txtStatusResult->SetLabelText(wxT("Online")); - txtStatusResult->SetForegroundColour("green"); - btnLaunch->Enable(); - }); - txtStatusResult->SetLabelText(wxT("In-Game")); - txtStatusResult->SetForegroundColour("purple"); - btnLaunch->Disable(); - - if (FirstTime) { - wxMessageBox("Please launch BeamNG.drive manually in case of Steam issues.", "Alert"); - FirstTime = false; - } -} - -/////////// OnClick Logo Event /////////// -void MyMainFrame::OnClickLogo(wxCommandEvent& event WXUNUSED(event)) { - wxLaunchDefaultApplication("https://beammp.com"); -} - -/////////// OnClick Register Event /////////// -void MyAccountFrame::OnClickRegister(wxCommandEvent& event WXUNUSED(event)) { - wxLaunchDefaultApplication("https://forum.beammp.com/signup"); -} - -/////////// OnClick Login Event /////////// -void MyAccountFrame::OnClickLogin(wxCommandEvent& event WXUNUSED(event)) { - Json json; - json["password"] = ctrlPassword->GetValue().utf8_string(); - json["username"] = ctrlUsername->GetValue().utf8_string(); - - if (Login(json.dump())) { - std::string picture = HTTP::Get("https://forum.beammp.com/user_avatar/forum.beammp.com/" + UIData::Username + "/240/4411_2.png"); - std::ofstream File("icons/" + UIData::Username + PictureType(picture), std::ios::binary); - if (File.is_open()) { - File << picture; - File.close(); - MyMainFrame::MainFrameInstance->BitAccount->SetBitmap(wxBitmapBundle(wxImage(GetPictureName()).Scale(45, 45, wxIMAGE_QUALITY_HIGH))); - } - MyMainFrame::AccountFrame->Destroy(); - MyMainFrame::MainFrameInstance->OnClickAccount(event); - } -} - -/////////// OnClick Logout Event /////////// -void MyAccountFrame::OnClickLogout(wxCommandEvent& event WXUNUSED(event)) { - Login("LO"); - MyMainFrame::AccountFrame->Destroy(); - MyMainFrame::MainFrameInstance->BitAccount->SetBitmap(wxBitmapBundle(wxImage("icons/default.png", wxBITMAP_TYPE_PNG).Scale(45, 45, wxIMAGE_QUALITY_HIGH))); - MyMainFrame::MainFrameInstance->OnClickAccount(event); -} - -/////////// OnClick Console Event /////////// -void MySettingsFrame::OnClickConsole(wxCommandEvent& event) { - WindowsConsole(checkConsole->IsChecked()); - Log::ConsoleOutput(checkConsole->IsChecked()); - bool status = event.IsChecked(); - UpdateConfig("Console", status); - UIData::Console = status; -} - -/////////// OnChanged Game Path Event /////////// -void MySettingsFrame::OnChangedGameDir(wxFileDirPickerEvent& event) { - std::string NewPath = event.GetPath().utf8_string(); - std::string key = reinterpret_cast(event.GetEventObject())->GetLabel(); - UpdateConfig(key, NewPath); - UpdateGameDirectory(NewPath); -} - -/////////// OnChanged Profile Path Event /////////// -void MySettingsFrame::OnChangedProfileDir(wxFileDirPickerEvent& event) { - std::string NewPath = event.GetPath().utf8_string(); - std::string key = reinterpret_cast(event.GetEventObject())->GetLabel(); - UpdateConfig(key, NewPath); - UpdateProfileDirectory(NewPath); -} - -/////////// OnChanged Cache Path Event /////////// -void MySettingsFrame::OnChangedCacheDir(wxFileDirPickerEvent& event) { - std::string NewPath = event.GetPath().utf8_string(); - std::string key = reinterpret_cast(event.GetEventObject())->GetLabel(); - UpdateConfig(key, NewPath); - UpdateCacheDirectory(NewPath); -} - -/////////// OnChanged Build Event /////////// -void MySettingsFrame::OnChangedBuild(wxCommandEvent& event) { - std::string key = reinterpret_cast(event.GetEventObject())->GetString(event.GetSelection()); - UpdateConfig("Build", key); - UIData::Build = key; -} - -/////////// AutoDetect Game Function /////////// -void MySettingsFrame::OnAutoDetectGame(wxCommandEvent& event) { - UpdateGameDirectory(AutoDetectGame()); -} - -/////////// AutoDetect Profile Function /////////// -void MySettingsFrame::OnAutoDetectProfile(wxCommandEvent& event) { - UpdateProfileDirectory(AutoDetectProfile()); -} - -/////////// Reset Cache Function /////////// -void MySettingsFrame::OnResetCache(wxCommandEvent& event) { - UpdateCacheDirectory(ResetCache()); -} - -/////////// MAIN FUNCTION /////////// -int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { - std::string ConfigPath(lpCmdLine); - if (!ConfigPath.empty()) { - UIData::ConfigPath = ConfigPath; - } else UIData::ConfigPath = "Launcher.toml"; - wxDisableAsserts(); - wxLog::SetLogLevel(wxLOG_Info); - Log::Init(); - LoadConfig(); - UIData::GameVer = jsonRead(); - - if (UIData::UI) { - int result = 0; - try { - new MyApp(); - result = wxEntry(hInstance, hPrevInstance, lpCmdLine, nShowCmd); - if (Launcher::EntryThread.joinable()) - Launcher::EntryThread.join(); - if (MyMainFrame::UpdateThread.joinable()) - MyMainFrame::UpdateThread.join(); - } catch (const ShutdownException& e) { - LOG(INFO) << "Launcher shutting down with reason: " << e.what(); - } catch (const std::exception& e) { - LOG(FATAL) << e.what(); - } - return result; - } else { - WindowsConsole(true); - Log::ConsoleOutput(true); - return entry(); - } -} diff --git a/src/main.cpp b/src/main.cpp index 6a8cab4..e50831b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,13 +6,11 @@ #include "Launcher.h" #include "Logger.h" -int entry() { +int main(int argc, char* argv[]) { try { - Launcher launcher; + Launcher launcher(argc, argv); launcher.RunDiscordRPC(); - launcher.LoadConfig(); // check if json (issue) launcher.CheckKey(); - // UI call // launcher.SetupMOD(); launcher.LaunchGame(); launcher.WaitForGame();