From 13d9ba3e13bbf9a458baa18f3f17fb18694ec1ce Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Fri, 24 Mar 2023 00:10:48 +0100 Subject: [PATCH] Save SDK Launcher state in settings file Store the state of the SDK launcher (toggled/text options) to a file on the disk to persist state. --- r5dev/sdklauncher/basepanel.cpp | 184 ++++++++++++++++++++++++++ r5dev/sdklauncher/basepanel.h | 6 + r5dev/sdklauncher/sdklauncher_const.h | 2 + 3 files changed, 192 insertions(+) diff --git a/r5dev/sdklauncher/basepanel.cpp b/r5dev/sdklauncher/basepanel.cpp index c0cc42e8..c12c7ea1 100644 --- a/r5dev/sdklauncher/basepanel.cpp +++ b/r5dev/sdklauncher/basepanel.cpp @@ -27,6 +27,9 @@ void CSurface::Init() this->SetMaximizeBox(false); this->SetBackColor(Drawing::Color(47, 54, 61)); + this->Load += &OnLoad; + this->FormClosing += &OnClose; + // ######################################################################## // GAME // ######################################################################## @@ -502,6 +505,167 @@ void CSurface::Setup() this->m_VisibilityCombo->Items.Add("Offline"); } +//----------------------------------------------------------------------------- +// Purpose: load and apply settings +//----------------------------------------------------------------------------- +void CSurface::LoadSettings() +{ + const fs::path settingsPath(Format("platform/%s/%s", SDK_SYSTEM_CFG_PATH, LAUNCHER_SETTING_FILE)); + if (!fs::exists(settingsPath)) + { + return; + } + + bool success{ }; + std::ifstream fileStream(settingsPath, fstream::in); + vdf::object vRoot = vdf::read(fileStream, &success); + + if (!success) + { + printf("%s: Failed to parse VDF file: '%s'\n", __FUNCTION__, + settingsPath.u8string().c_str()); + return; + } + + try + { + // Game. + string& attributeView = vRoot.attribs["playlistsFile"]; + this->m_PlaylistFileTextBox->SetText(attributeView.data()); + + attributeView = vRoot.attribs["enableCheats"]; + this->m_CheatsToggle->SetChecked(attributeView != "0"); + + attributeView = vRoot.attribs["enableDeveloper"]; + this->m_DeveloperToggle->SetChecked(attributeView != "0"); + + attributeView = vRoot.attribs["enableConsole"]; + this->m_ConsoleToggle->SetChecked(attributeView != "0"); + + attributeView = vRoot.attribs["colorConsole"]; + this->m_ColorConsoleToggle->SetChecked(attributeView != "0"); + + // Engine. + attributeView = vRoot.attribs["reservedCoreCount"]; + this->m_ReservedCoresTextBox->SetText(attributeView.data()); + + attributeView = vRoot.attribs["workerThreadCount"]; + this->m_WorkerThreadsTextBox->SetText(attributeView.data()); + + attributeView = vRoot.attribs["singleCoreServer"]; + this->m_SingleCoreDediToggle->SetChecked(attributeView != "0"); + + attributeView = vRoot.attribs["synchronizeJobs"]; // No-async + this->m_NoAsyncJobsToggle->SetChecked(attributeView != "0"); + + // Network. + attributeView = vRoot.attribs["encryptionEnable"]; + this->m_NetEncryptionToggle->SetChecked(attributeView != "0"); + + attributeView = vRoot.attribs["randomNetKey"]; + this->m_NetRandomKeyToggle->SetChecked(attributeView != "0"); + + attributeView = vRoot.attribs["noQueuedPackets"]; + this->m_NoQueuedPacketThread->SetChecked(attributeView != "0"); + + attributeView = vRoot.attribs["noTimeOut"]; + this->m_NoTimeOutToggle->SetChecked(attributeView != "0"); + + // Video. + attributeView = vRoot.attribs["windowed"]; + this->m_WindowedToggle->SetChecked(attributeView != "0"); + + attributeView = vRoot.attribs["borderless"]; + this->m_NoBorderToggle->SetChecked(attributeView != "0"); + + attributeView = vRoot.attribs["maxFPS"]; + this->m_FpsTextBox->SetText(attributeView.data()); + + attributeView = vRoot.attribs["width"]; + this->m_WidthTextBox->SetText(attributeView.data()); + + attributeView = vRoot.attribs["height"]; + this->m_HeightTextBox->SetText(attributeView.data()); + } + catch (const std::exception& e) + { + printf("%s: Exception while parsing VDF file: %s\n", e.what()); + } +} + +//----------------------------------------------------------------------------- +// Purpose: save current launcher state +//----------------------------------------------------------------------------- +void CSurface::SaveSettings() +{ + const fs::path settingsPath(Format("platform/%s/%s", SDK_SYSTEM_CFG_PATH, LAUNCHER_SETTING_FILE)); + const fs::path parentPath = settingsPath.parent_path(); + + if (!fs::exists(parentPath) && !fs::create_directories(parentPath)) + { + printf("%s: Failed to create directory: '%s'\n", __FUNCTION__, + parentPath.relative_path().u8string().c_str()); + return; + } + + std::ofstream fileStream(settingsPath, fstream::out); + if (!fileStream) + { + printf("%s: Failed to create VDF file: '%s'\n", __FUNCTION__, + settingsPath.u8string().c_str()); + return; + } + + vdf::object vRoot; + vRoot.set_name("LauncherSettings"); + + // Game. + vRoot.add_attribute("playlistsFile", GetControlValue(this->m_PlaylistFileTextBox)); + vRoot.add_attribute("enableCheats", GetControlValue(this->m_CheatsToggle)); + vRoot.add_attribute("enableDeveloper", GetControlValue(this->m_DeveloperToggle)); + vRoot.add_attribute("enableConsole", GetControlValue(this->m_ConsoleToggle)); + vRoot.add_attribute("colorConsole", GetControlValue(this->m_ColorConsoleToggle)); + + // Engine. + vRoot.add_attribute("reservedCoreCount", GetControlValue(this->m_ReservedCoresTextBox)); + vRoot.add_attribute("workerThreadCount", GetControlValue(this->m_WorkerThreadsTextBox)); + vRoot.add_attribute("singleCoreServer", GetControlValue(this->m_SingleCoreDediToggle)); + vRoot.add_attribute("synchronizeJobs", GetControlValue(this->m_NoAsyncJobsToggle)); + + // Network. + vRoot.add_attribute("encryptionEnable", GetControlValue(this->m_NetEncryptionToggle)); + vRoot.add_attribute("randomNetKey", GetControlValue(this->m_NetRandomKeyToggle)); + vRoot.add_attribute("noQueuedPackets", GetControlValue(this->m_NoQueuedPacketThread)); + vRoot.add_attribute("noTimeOut", GetControlValue(this->m_NoTimeOutToggle)); + + // Video. + vRoot.add_attribute("windowed", GetControlValue(this->m_WindowedToggle)); + vRoot.add_attribute("borderless", GetControlValue(this->m_NoBorderToggle)); + vRoot.add_attribute("maxFPS", GetControlValue(this->m_FpsTextBox)); + vRoot.add_attribute("width", GetControlValue(this->m_WidthTextBox)); + vRoot.add_attribute("height", GetControlValue(this->m_HeightTextBox)); + + vdf::write(fileStream, vRoot); +} + +//----------------------------------------------------------------------------- +// Purpose: load callback +// Input : *pSender - +//----------------------------------------------------------------------------- +void CSurface::OnLoad(Forms::Control* pSender) +{ + ((CSurface*)pSender->FindForm())->LoadSettings(); +} + +//----------------------------------------------------------------------------- +// Purpose: close callback +// Input : *pSender - +//----------------------------------------------------------------------------- +void CSurface::OnClose(const std::unique_ptr& pEventArgs, Forms::Control* pSender) +{ + ((CSurface*)pSender->FindForm())->SaveSettings(); +} + //----------------------------------------------------------------------------- // Purpose: removes redundant files from the game install // Input : *pSender - @@ -1046,6 +1210,26 @@ eLaunchMode CSurface::BuildParameter(string& svParameters) } } +//----------------------------------------------------------------------------- +// Purpose: gets the control item value +// Input : *pControl - +//----------------------------------------------------------------------------- +const char* CSurface::GetControlValue(Forms::Control* pControl) +{ + switch (pControl->GetType()) + { + case Forms::ControlTypes::CheckBox: + case Forms::ControlTypes::RadioButton: + { + return reinterpret_cast(pControl)->Checked() ? "1" : "0"; + } + default: + { + return pControl->Text().ToCString(); + } + } +} + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- diff --git a/r5dev/sdklauncher/basepanel.h b/r5dev/sdklauncher/basepanel.h index 2a109997..3b26405b 100644 --- a/r5dev/sdklauncher/basepanel.h +++ b/r5dev/sdklauncher/basepanel.h @@ -26,9 +26,14 @@ public: private: void Init(); void Setup(); + void LoadSettings(); + void SaveSettings(); void ParseMaps(); void ParsePlaylists(); + static void OnLoad(Forms::Control* pSender); + static void OnClose(const std::unique_ptr& pEventArgs, Forms::Control* pSender); + static void LaunchGame(Forms::Control* pSender); static void CleanSDK(Forms::Control* pSender); static void UpdateSDK(Forms::Control* pSender); @@ -38,6 +43,7 @@ private: static void ForwardCommandToGame(Forms::Control* pSender); eLaunchMode BuildParameter(string& svParameter); + const char* GetControlValue(Forms::Control* pControl); void AppendParameterInternal(string& svParameterList, const char* szParameter, const char* szArgument = nullptr); void AppendReservedCoreCount(string& svParameter); diff --git a/r5dev/sdklauncher/sdklauncher_const.h b/r5dev/sdklauncher/sdklauncher_const.h index aaca6286..5e79df6a 100644 --- a/r5dev/sdklauncher/sdklauncher_const.h +++ b/r5dev/sdklauncher/sdklauncher_const.h @@ -11,6 +11,8 @@ #define DEFAULT_WINDOW_CLASS_NAME "Respawn001" +#define LAUNCHER_SETTING_FILE "launcher.vdf" + //----------------------------------------------------------------------------- // Launch and inject specified dll based on launch mode //-----------------------------------------------------------------------------