From 9642ea18f392b4d1d2c9cb437dac4ba686db5adf Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Mon, 31 Jul 2023 02:51:27 +0200 Subject: [PATCH] New features - check if game is running, and if so, warn and discard update/install request. - check if launcher instance is running, and if so, not create another one but instead move focus to existing one - periodic update checks and update installations --- r5dev/sdklauncher/base_surface.cpp | 156 +++++++++++++++++++++--- r5dev/sdklauncher/base_surface.h | 10 ++ r5dev/sdklauncher/sdklauncher.cpp | 18 +++ r5dev/sdklauncher/sdklauncher_const.h | 6 + r5dev/sdklauncher/sdklauncher_utils.cpp | 143 ++++++++++++++++++---- r5dev/sdklauncher/sdklauncher_utils.h | 9 +- 6 files changed, 298 insertions(+), 44 deletions(-) diff --git a/r5dev/sdklauncher/base_surface.cpp b/r5dev/sdklauncher/base_surface.cpp index 69fc18cc..019d8f96 100644 --- a/r5dev/sdklauncher/base_surface.cpp +++ b/r5dev/sdklauncher/base_surface.cpp @@ -4,6 +4,7 @@ // //=============================================================================// #include "base_surface.h" +#include "sdklauncher.h" #include "advanced_surface.h" #include "download_surface.h" #include "sdklauncher_utils.h" @@ -13,7 +14,7 @@ #include "zip/src/ZipFile.h" #define WINDOW_SIZE_X 400 -#define WINDOW_SIZE_Y 194 +#define WINDOW_SIZE_Y 224 CBaseSurface::CBaseSurface() { @@ -45,22 +46,22 @@ CBaseSurface::CBaseSurface() this->m_BaseGroup->SetAnchor(Forms::AnchorStyles::Top | Forms::AnchorStyles::Left); this->AddControl(this->m_BaseGroup); - const bool isInstalled = fs::exists("r5apex.exe"); + m_bIsInstalled = fs::exists("r5apex.exe"); this->m_ManageButton = new UIX::UIXButton(); this->m_ManageButton->SetSize({ 168, 70 }); this->m_ManageButton->SetLocation({ 10, 10 }); this->m_ManageButton->SetTabIndex(9); - this->m_ManageButton->SetText(isInstalled ? XorStr("Launch Apex") : XorStr("Install Apex")); + this->m_ManageButton->SetText(m_bIsInstalled ? XorStr("Launch Apex") : XorStr("Install Apex")); this->m_ManageButton->SetAnchor(Forms::AnchorStyles::Bottom | Forms::AnchorStyles::Left); - this->m_ManageButton->Click += isInstalled ? &OnAdvancedClick : &OnInstallClick; + this->m_ManageButton->Click += m_bIsInstalled ? &OnLaunchClick : &OnInstallClick; m_BaseGroup->AddControl(this->m_ManageButton); this->m_RepairButton = new UIX::UIXButton(); this->m_RepairButton->SetSize({ 168, 70 }); this->m_RepairButton->SetLocation({ 10, 90 }); this->m_RepairButton->SetTabIndex(9); - this->m_RepairButton->SetEnabled(isInstalled); + this->m_RepairButton->SetEnabled(/*m_bIsInstalled*/ false); this->m_RepairButton->SetText(XorStr("Repair Apex")); this->m_RepairButton->SetAnchor(Forms::AnchorStyles::Bottom | Forms::AnchorStyles::Left); // TODO: should hash every file against a downloaded manifest instead and @@ -90,32 +91,147 @@ CBaseSurface::CBaseSurface() this->m_AdvancedButton->SetSize({ 178, 43 }); this->m_AdvancedButton->SetLocation({ 188, 116 }); this->m_AdvancedButton->SetTabIndex(9); - this->m_AdvancedButton->SetEnabled(isInstalled); + this->m_AdvancedButton->SetEnabled(m_bIsInstalled); this->m_AdvancedButton->SetText(XorStr("Advanced Options")); this->m_AdvancedButton->SetAnchor(Forms::AnchorStyles::Bottom | Forms::AnchorStyles::Left); this->m_AdvancedButton->Click += &OnAdvancedClick; m_BaseGroup->AddControl(this->m_AdvancedButton); + m_ExperimentalBuildsCheckbox = new UIX::UIXCheckBox(); + this->m_ExperimentalBuildsCheckbox->SetSize({ 250, 18 }); + this->m_ExperimentalBuildsCheckbox->SetLocation({ 10, 170 }); + this->m_ExperimentalBuildsCheckbox->SetTabIndex(0); + this->m_ExperimentalBuildsCheckbox->SetText(XorStr("Also check for playtest versions updates")); + this->m_ExperimentalBuildsCheckbox->SetAnchor(Forms::AnchorStyles::Top | Forms::AnchorStyles::Left); + + // TODO: remove this when its made public!!! + m_ExperimentalBuildsCheckbox->SetChecked(true); + + m_BaseGroup->AddControl(this->m_ExperimentalBuildsCheckbox); + // TODO: Use a toggle item instead; remove this field. m_bPartialInstall = false; + m_bUpdateViewToggled = false; + + Threading::Thread([this] { + this->Frame(); + }).Start(); +} + +void CBaseSurface::ToggleUpdateView(bool bValue) +{ + // Game must be installed before this can be called!! + Assert(m_bIsInstalled); + + this->m_ManageButton->SetText(bValue ? XorStr("Update Apex") : XorStr("Install Apex")); + + if (bValue) + { + this->m_ManageButton->Click -= &OnLaunchClick; + this->m_ManageButton->Click += &OnUpdateClick; + } + else + { + this->m_ManageButton->Click -= &OnUpdateClick; + this->m_ManageButton->Click += &OnLaunchClick; + } + + m_bUpdateViewToggled = bValue; +} + + +void CBaseSurface::Frame() +{ + for (;;) + { + printf("%s: runframe; interval=%f\n", __FUNCTION__, g_flUpdateCheckRate); + + if (!m_bUpdateViewToggled && m_bIsInstalled && SDKLauncher_CheckForUpdate(m_ExperimentalBuildsCheckbox->Checked())) + { + ToggleUpdateView(true); + printf("%s: found update; interval=%f\n", __FUNCTION__, g_flUpdateCheckRate); + } + + std::this_thread::sleep_for(IntervalToDuration(g_flUpdateCheckRate)); + } +} + +void CBaseSurface::OnUpdateClick(Forms::Control* Sender) +{ + //CBaseSurface* pSurf = (CBaseSurface*)Sender; + + vector vecHandles; + EnumWindows(EnumWindowsProc, reinterpret_cast(&vecHandles)); + + if (!vecHandles.empty()) + { + Forms::MessageBox::Show("Close all game instances before updating the game!\n", + "Warning", Forms::MessageBoxButtons::OK, Forms::MessageBoxIcon::Warning); + + return; + } + + + auto downloadSurface = std::make_unique(); + CProgressPanel* pProgress = downloadSurface.get(); + + pProgress->SetAutoClose(true); + + Threading::Thread([pProgress] { + + if (!SDKLauncher_CreateDepotDirectories()) + { + Forms::MessageBox::Show(Format("Failed to create depot directories: Error code = %08x\n", GetLastError()).c_str(), + "Error", Forms::MessageBoxButtons::OK, Forms::MessageBoxIcon::Error); + + return; + } + + CUtlVector fileList; + SDKLauncher_BeginDownload(true, false, true, fileList, pProgress); + + pProgress->SetCanCancel(false); + + SDKLauncher_InstallAssetList(false, fileList, pProgress); + + // Close on finish. + pProgress->Close(); + }).Start(); + + pProgress->ShowDialog(); + + // Restart the launcher process from here through updater.exe! + SDKLauncher_Restart(); } void CBaseSurface::OnInstallClick(Forms::Control* Sender) { + vector vecHandles; + EnumWindows(EnumWindowsProc, reinterpret_cast(&vecHandles)); + + if (!vecHandles.empty()) + { + Forms::MessageBox::Show("Close all game instances before installing the game!\n", + "Warning", Forms::MessageBoxButtons::OK, Forms::MessageBoxIcon::Warning); + + return; + } + + CBaseSurface* pSurf = (CBaseSurface*)Sender; const bool bPartial = pSurf->m_bPartialInstall; - //const int minRequiredSpace = bPartial ? MIN_REQUIRED_DISK_SPACE : MIN_REQUIRED_DISK_SPACE_OPT; - //int currentDiskSpace; + const int minRequiredSpace = bPartial ? MIN_REQUIRED_DISK_SPACE : MIN_REQUIRED_DISK_SPACE_OPT; + int currentDiskSpace; - //if (!SDKLauncher_CheckDiskSpace(minRequiredSpace, ¤tDiskSpace)) - //{ - // Forms::MessageBox::Show(Format("There is not enough space available on the disk to install R5Reloaded;" - // " you need at least %iGB, you currently have %iGB\n", minRequiredSpace, currentDiskSpace).c_str(), - // "Error", Forms::MessageBoxButtons::OK, Forms::MessageBoxIcon::Error); + if (!SDKLauncher_CheckDiskSpace(minRequiredSpace, ¤tDiskSpace)) + { + Forms::MessageBox::Show(Format("There is not enough space available on the disk to install R5Reloaded;" + " you need at least %iGB, you currently have %iGB\n", minRequiredSpace, currentDiskSpace).c_str(), + "Error", Forms::MessageBoxButtons::OK, Forms::MessageBoxIcon::Error); - // return; - //} + return; + } auto downloadSurface = std::make_unique(); CProgressPanel* pProgress = downloadSurface.get(); @@ -133,7 +249,7 @@ void CBaseSurface::OnInstallClick(Forms::Control* Sender) } CUtlVector fileList; - SDKLauncher_BeginDownload(true, false, fileList, pProgress); + SDKLauncher_BeginDownload(true, false, false, fileList, pProgress); SDKLauncher_InstallAssetList(false, fileList, pProgress); // Close on finish. @@ -146,6 +262,14 @@ void CBaseSurface::OnInstallClick(Forms::Control* Sender) SDKLauncher_Restart(); } + +void CBaseSurface::OnLaunchClick(Forms::Control* Sender) +{ + // !TODO: parameter building and settings loading should be its own class!! + if (g_pLauncher->CreateLaunchContext(eLaunchMode::LM_CLIENT, 0, "", "startup_launcher.cfg")) + g_pLauncher->LaunchProcess(); +} + void CBaseSurface::OnAdvancedClick(Forms::Control* Sender) { auto pAdvancedSurface = std::make_unique(); diff --git a/r5dev/sdklauncher/base_surface.h b/r5dev/sdklauncher/base_surface.h index 81bc63c1..b3a4c3d0 100644 --- a/r5dev/sdklauncher/base_surface.h +++ b/r5dev/sdklauncher/base_surface.h @@ -8,8 +8,12 @@ public: virtual ~CBaseSurface() {}; + void ToggleUpdateView(bool bValue); + protected: static void OnInstallClick(Forms::Control* Sender); + static void OnUpdateClick(Forms::Control* Sender); + static void OnLaunchClick(Forms::Control* Sender); static void OnAdvancedClick(Forms::Control* Sender); @@ -18,6 +22,7 @@ protected: static void OnJoinClick(Forms::Control* Sender); private: + void Frame(); enum class eMode { @@ -42,7 +47,12 @@ private: UIX::UIXButton* m_JoinButton; UIX::UIXButton* m_AdvancedButton; + UIX::UIXCheckBox* m_ExperimentalBuildsCheckbox; + // When this is false, the installer will // download the HD textures as well (STARPAK's). bool m_bPartialInstall; + + bool m_bUpdateViewToggled; + bool m_bIsInstalled; }; diff --git a/r5dev/sdklauncher/sdklauncher.cpp b/r5dev/sdklauncher/sdklauncher.cpp index d84fd815..137f16d7 100644 --- a/r5dev/sdklauncher/sdklauncher.cpp +++ b/r5dev/sdklauncher/sdklauncher.cpp @@ -390,6 +390,24 @@ void LauncherLoggerSink(LogType_t logType, LogLevel_t logLevel, eDLL_T context, /////////////////////////////////////////////////////////////////////////////// int main(int argc, char* argv[]/*, char* envp[]*/) { + HANDLE singleInstanceMutex = CreateMutexW(NULL, TRUE, L"SDKLauncher_Mutex"); + + if (singleInstanceMutex == NULL || GetLastError() == ERROR_ALREADY_EXISTS) + { + HWND existingApp = FindWindowW(0, L"SDKLauncher001"); + if (existingApp) SetForegroundWindow(existingApp); + return EXIT_SUCCESS; + } + + WNDCLASSEXW wcex = { sizeof(wcex) }; + wcex.lpszClassName = L"SDKLauncher001"; + + if (!RegisterClassExW(&wcex)) + { + DWORD dwError = GetLastError(); + return dwError; + } + g_pLauncher->InitLogger(); if (argc < 2) { diff --git a/r5dev/sdklauncher/sdklauncher_const.h b/r5dev/sdklauncher/sdklauncher_const.h index d3d61a7b..b30e49a6 100644 --- a/r5dev/sdklauncher/sdklauncher_const.h +++ b/r5dev/sdklauncher/sdklauncher_const.h @@ -23,6 +23,12 @@ // have to go here!! #define RESTART_DEPOT_DOWNLOAD_DIR DEFAULT_DEPOT_DOWNLOAD_DIR "temp\\" +#define DEPOT_MANIFEST_FILE "manifest_patch.json" + +// TODO: these should be obtained dynamically!!! +#define GAME_DEPOT_VENDOR "https://api.github.com/repos/SlaveBuild/N1094_CL456479/releases" +#define SDK_DEPOT_VENDOR "https://api.github.com/repos/Mauler125/r5sdk/releases" + //----------------------------------------------------------------------------- // Launch and inject specified dll based on launch mode //----------------------------------------------------------------------------- diff --git a/r5dev/sdklauncher/sdklauncher_utils.cpp b/r5dev/sdklauncher/sdklauncher_utils.cpp index 27f57516..de782b2f 100644 --- a/r5dev/sdklauncher/sdklauncher_utils.cpp +++ b/r5dev/sdklauncher/sdklauncher_utils.cpp @@ -4,6 +4,10 @@ #include "tier2/curlutils.h" #include "zip/src/ZipFile.h" +bool g_bPartialInstall = false; +//bool g_bExperimentalBuilds = false; +float g_flUpdateCheckRate = 64; + // !TODO: perhaps this should be a core utility shared across // the entire SDK to allow processes to restart them selfs. void SDKLauncher_Restart() @@ -173,7 +177,7 @@ bool SDKLauncher_QueryServer(const char* url, string& outResponse, string& outMe params.writeFunction = CURLWriteStringCallback; params.timeout = QUERY_TIMEOUT; params.verifyPeer = true; - params.verbose = IsDebug(); + params.verbose = 0;// IsDebug(); CURL* curl = CURLInitRequest(url, nullptr, outResponse, sList, params); @@ -204,11 +208,13 @@ bool SDKLauncher_GetLatestReleaseManifest(const char* url, string& responseMessa if (!SDKLauncher_QueryServer(url, responseBody, responseMessage, status)) { + responseMessage = responseBody; return false; } if (status != 200) { + responseMessage = responseBody; return false; } @@ -222,17 +228,17 @@ bool SDKLauncher_GetLatestReleaseManifest(const char* url, string& responseMessa if (preRelease && release["prerelease"]) { - outManifest = release["assets"]; + outManifest = release; break; } else if (!release["prerelease"]) { - outManifest = release["assets"]; + outManifest = release; break; } if (i == responseJson.size() - 1 && outManifest.empty()) - release[0]["assets"]; // Just take the first one then. + release[0]; // Just take the first one then. } } catch (const std::exception& ex) @@ -254,7 +260,10 @@ int SDKLauncher_ProgressCallback(CURLProgress* progessData, double dltotal, CProgressPanel* pDownloadSurface = (CProgressPanel*)progessData->cust; if (pDownloadSurface->IsCanceled()) + { + pDownloadSurface->Close(); return -1; + } double downloaded; curl_easy_getinfo(progessData->curl, CURLINFO_SIZE_DOWNLOAD, &downloaded); @@ -270,7 +279,7 @@ int SDKLauncher_ProgressCallback(CURLProgress* progessData, double dltotal, //---------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------- -void SDKLauncher_DownloadAsset(const char* url, const char* path, const char* fileName, +bool SDKLauncher_DownloadAsset(const char* url, const char* path, const char* fileName, const size_t fileSize, const char* options, CProgressPanel* pProgress) { CURLParams params; @@ -278,22 +287,24 @@ void SDKLauncher_DownloadAsset(const char* url, const char* path, const char* fi params.writeFunction = CURLWriteFileCallback; params.statusFunction = SDKLauncher_ProgressCallback; - CURLDownloadFile(url, path, fileName, options, fileSize, pProgress, params); + return CURLDownloadFile(url, path, fileName, options, fileSize, pProgress, params); } //---------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------- void SDKLauncher_BeginDownload(const bool bPreRelease, const bool bOptionalAssets, - CUtlVector& fileList, CProgressPanel* pProgress) + const bool bSdkOnly/*!!! REFACTOR ME MAYBE !!!*/, CUtlVector& fileList, CProgressPanel* pProgress) { - string responseMesage; + string responseMessage; nlohmann::json manifest; // These files will NOT be downloaded from the release depots. std::set blackList; blackList.insert("symbols.zip"); + + // DEBUG CODE!!! //fileList.AddToTail("audio_0.zip"); //fileList.AddToTail("audio_1.zip"); //fileList.AddToTail("binaries.zip"); @@ -303,21 +314,30 @@ void SDKLauncher_BeginDownload(const bool bPreRelease, const bool bOptionalAsset //fileList.AddToTail("starpak_0.zip"); //fileList.AddToTail("starpak_1.zip"); //fileList.AddToTail("stbsp.zip"); - //fileList.AddToTail("depot.zip"); + //fileList.AddToTail("/depot.zip"); - //// Download core game files. - //if (!SDKLauncher_GetLatestReleaseManifest(XorStr("https://api.github.com/repos/SlaveBuild/N1094_CL456479/releases"), responseMesage, manifest, bPreRelease)) + //FOR_EACH_VEC(fileList, i) //{ - // // TODO: Error dialog. - // return; + // CUtlString& filePath = fileList[i]; + // filePath = filePath.Replace("/", DEFAULT_DEPOT_DOWNLOAD_DIR); //} - //SDKLauncher_DownloadAssetList(fileList, manifest, blackList, DEFAULT_DEPOT_DOWNLOAD_DIR, pProgress); - //if (pProgress->IsCanceled()) - // return; + if (!bSdkOnly) + { + // Download core game files. + if (!SDKLauncher_GetLatestReleaseManifest(XorStr(GAME_DEPOT_VENDOR), responseMessage, manifest, bPreRelease)) + { + // TODO: Error dialog. + return; + } + SDKLauncher_DownloadAssetList(fileList, manifest, blackList, DEFAULT_DEPOT_DOWNLOAD_DIR, pProgress); + + if (pProgress->IsCanceled()) + return; + } // Download SDK files. - if (!SDKLauncher_GetLatestReleaseManifest(XorStr("https://api.github.com/repos/Mauler125/r5sdk/releases"), responseMesage, manifest, bPreRelease)) + if (!SDKLauncher_GetLatestReleaseManifest(XorStr(SDK_DEPOT_VENDOR), responseMessage, manifest, bPreRelease)) { // TODO: Error dialog. return; @@ -325,17 +345,27 @@ void SDKLauncher_BeginDownload(const bool bPreRelease, const bool bOptionalAsset SDKLauncher_DownloadAssetList(fileList, manifest, blackList, DEFAULT_DEPOT_DOWNLOAD_DIR, pProgress); if (pProgress->IsCanceled()) + { return; + } } //---------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------- -void SDKLauncher_DownloadAssetList(CUtlVector& fileList, nlohmann::json& assetList, +bool SDKLauncher_DownloadAssetList(CUtlVector& fileList, nlohmann::json& assetList, std::set& blackList, const char* pPath, CProgressPanel* pProgress) { + if (!assetList.contains("assets")) + { + Assert(0); + return false; + } + int i = 1; - for (auto& asset : assetList) + const auto assetListArray = assetList["assets"]; + + for (auto& asset : assetListArray) { if (pProgress->IsCanceled()) { @@ -347,7 +377,7 @@ void SDKLauncher_DownloadAssetList(CUtlVector& fileList, nlohmann::j !asset.contains("size")) { Assert(0); - return; + return false; } const string fileName = asset["name"]; @@ -355,12 +385,14 @@ void SDKLauncher_DownloadAssetList(CUtlVector& fileList, nlohmann::j // Asset is filtered, don't download. if (blackList.find(fileName) != blackList.end()) + { continue; + } const string downloadLink = asset["browser_download_url"]; const size_t fileSize = asset["size"]; - pProgress->SetText(Format("Downloading package %i of %i...", i, assetList.size()).c_str()); + pProgress->SetText(Format("Downloading package %i of %i...", i, assetListArray.size()).c_str()); SDKLauncher_DownloadAsset(downloadLink.c_str(), pPath, pFileName, fileSize, "wb+", pProgress); // Check if its a zip file, as these are @@ -377,12 +409,14 @@ void SDKLauncher_DownloadAssetList(CUtlVector& fileList, nlohmann::j i++; } + + return true; } //---------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------- -void SDKLauncher_InstallAssetList(const bool bOptionalAssets, +bool SDKLauncher_InstallAssetList(const bool bOptionalAssets, CUtlVector& fileList, CProgressPanel* pProgress) { // Install process cannot be canceled. @@ -393,8 +427,11 @@ void SDKLauncher_InstallAssetList(const bool bOptionalAssets, pProgress->SetText(Format("Installing package %i of %i...", i + 1, fileList.Count()).c_str()); CUtlString& fileName = fileList[i]; - SDKLauncher_ExtractZipFile(fileName.Get(), "", pProgress); + if (!SDKLauncher_ExtractZipFile(fileName.Get(), "", pProgress)) + return false; } + + return true; } //---------------------------------------------------------------------------- @@ -427,10 +464,66 @@ bool SDKLauncher_CheckDiskSpace(const int minRequiredSpace, int* const available return true; } +bool SDKLauncher_GetLocalManifest(nlohmann::json& localManifest) +{ + const char* pManifestFileName = BASE_PLATFORM_DIR DEPOT_MANIFEST_FILE; + + if (!fs::exists(pManifestFileName)) + return false; + + ifstream localFile(pManifestFileName); + + if (!localFile.good()) + return false; + + try + { + localManifest = nlohmann::json::parse(localFile); + } + catch (const std::exception& ex) + { + printf("%s - Exception while parsing manifest:\n%s\n", __FUNCTION__, ex.what()); + return false; + } + + return !localManifest.empty(); +} + +//bool SDKLauncher_WriteLocalManifest() +//{ +// +//} + //---------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------- -bool SDKLauncher_CheckForUpdate() +bool SDKLauncher_CheckForUpdate(const bool bPreRelease) { - return true; + nlohmann::json remoteManifest; + string responseMessage; + + if (!SDKLauncher_GetLatestReleaseManifest(XorStr(SDK_DEPOT_VENDOR), responseMessage, remoteManifest, bPreRelease)) + { + printf("%s: Failed to obtain remote manifest: %s\n", __FUNCTION__, responseMessage.c_str()); + return true; // Can't determine if there is an update or not; skip... + } + + nlohmann::json localManifest; + + if (!SDKLauncher_GetLocalManifest(localManifest)) + { + // Failed to load a local one; assume an update is required. + printf("%s: Failed to obtain local manifest\n", __FUNCTION__); + return true; + } + + if (!localManifest.contains("version")) + { + // No version information; assume an update is required. + printf("%s: local manifest does not contain field '%s'!\n", __FUNCTION__, "version"); + return true; + } + + // This evaluates to '0' if the version tags are equal. + return !(localManifest["version"] == remoteManifest["tag_name"]); } diff --git a/r5dev/sdklauncher/sdklauncher_utils.h b/r5dev/sdklauncher/sdklauncher_utils.h index fcddfb59..235c0fcd 100644 --- a/r5dev/sdklauncher/sdklauncher_utils.h +++ b/r5dev/sdklauncher/sdklauncher_utils.h @@ -1,18 +1,21 @@ #pragma once #include "download_surface.h" +extern float g_flUpdateCheckRate; + void SDKLauncher_Restart(); bool SDKLauncher_CreateDepotDirectories(); bool SDKLauncher_ClearDepotDirectories(); bool SDKLauncher_ExtractZipFile(const char* pZipFile, const char* pDestPath, CProgressPanel* pProgress = nullptr); -void SDKLauncher_BeginDownload(const bool bPreRelease, const bool bOptionalAssets, CUtlVector& fileList, CProgressPanel* pProgress = nullptr); +void SDKLauncher_BeginDownload(const bool bPreRelease, const bool bOptionalAssets, const bool bSdkOnly, CUtlVector& fileList, CProgressPanel* pProgress = nullptr); -void SDKLauncher_DownloadAssetList(CUtlVector& fileList, nlohmann::json& assetList, +bool SDKLauncher_DownloadAssetList(CUtlVector& fileList, nlohmann::json& assetList, std::set& blackList, const char* pPath, CProgressPanel* pProgress); -void SDKLauncher_InstallAssetList(const bool bOptionalAssets, +bool SDKLauncher_InstallAssetList(const bool bOptionalAssets, CUtlVector& fileList, CProgressPanel* pProgress); bool SDKLauncher_CheckDiskSpace(const int minRequiredSpace, int* const availableSize = nullptr); +bool SDKLauncher_CheckForUpdate(const bool bPreRelease);