From c1c9d3ae15db277b59dde186cbc904fcf27dd66d Mon Sep 17 00:00:00 2001 From: Tony Date: Fri, 6 Sep 2024 17:01:37 -0400 Subject: [PATCH 1/8] feat: --exePath commandline argument ex. `SkyrimTogether.exe --exePath C:\Path\To\Game\SkyrimSE.exe` --- Code/immersive_launcher/Launcher.cpp | 31 ++++++-- Code/immersive_launcher/Launcher.h | 3 + Code/immersive_launcher/oobe/PathArgument.cpp | 77 +++++++++++++++++++ Code/immersive_launcher/oobe/PathArgument.h | 8 ++ 4 files changed, 114 insertions(+), 5 deletions(-) create mode 100644 Code/immersive_launcher/oobe/PathArgument.cpp create mode 100644 Code/immersive_launcher/oobe/PathArgument.h diff --git a/Code/immersive_launcher/Launcher.cpp b/Code/immersive_launcher/Launcher.cpp index 0547a00de..ef65c4128 100644 --- a/Code/immersive_launcher/Launcher.cpp +++ b/Code/immersive_launcher/Launcher.cpp @@ -10,10 +10,12 @@ #include "Utils/FileVersion.inl" #include "oobe/PathSelection.h" +#include "oobe/PathArgument.h" #include "oobe/SupportChecks.h" #include "steam/SteamLoader.h" #include "base/dialogues/win/TaskDialog.h" +#include "utils/Registry.h" #include @@ -64,11 +66,7 @@ void SetMaxstdio() int StartUp(int argc, char** argv) { bool askSelect = (GetAsyncKeyState(VK_SPACE) & 0x8000); - for (int i = 1; i < argc; i++) - { - if (std::strcmp(argv[i], "-r") == 0) - askSelect = true; - } + HandleArguments(argc, argv, askSelect); // TODO(Force): Make some InitSharedResources func. g_SharedWindowIcon = LoadIconW(GetModuleHandleW(nullptr), MAKEINTRESOURCEW(102)); @@ -141,6 +139,29 @@ void InitClient() // Jump into client code. RunTiltedApp(); } + +bool HandleArguments(int aArgc, char** aArgv, bool& aAskSelect) +{ + for (int i = 1; i < aArgc; i++) + { + if (std::strcmp(aArgv[i], "-r") == 0) + aAskSelect = true; + else if (std::strcmp(aArgv[i], "--exePath") == 0) + { + if (!aArgv[i + 1]) + { + DIE_NOW(L"No exe path specified"); + } + + if (!oobe::PathArgument(aArgv[i + 1])) + { + DIE_NOW(L"Failed to parse path argument"); + } + } + } + + return true; +} } // namespace launcher // CreateProcess in suspended mode. diff --git a/Code/immersive_launcher/Launcher.h b/Code/immersive_launcher/Launcher.h index 4c1571d83..185cb3616 100644 --- a/Code/immersive_launcher/Launcher.h +++ b/Code/immersive_launcher/Launcher.h @@ -30,4 +30,7 @@ bool LoadProgram(LaunchContext&); int StartUp(int argc, char** argv); void InitClient(); + +bool HandleArguments(int, char**, bool&); + } // namespace launcher diff --git a/Code/immersive_launcher/oobe/PathArgument.cpp b/Code/immersive_launcher/oobe/PathArgument.cpp new file mode 100644 index 000000000..2609b2811 --- /dev/null +++ b/Code/immersive_launcher/oobe/PathArgument.cpp @@ -0,0 +1,77 @@ +#include "PathArgument.h" + +#include "TargetConfig.h" + +#include "Utils/Error.h" +#include "utils/Registry.h" + +#include + +std::wstring g_titlePath; +std::wstring g_exePath; + +namespace oobe +{ +using namespace TiltedPhoques; + +namespace +{ +constexpr wchar_t kTiltedRegistryPath[] = LR"(Software\TiltedPhoques\TiltedEvolution\)" SHORT_NAME; + +#define DIE_NOW(err) \ + { \ + Die(err, true); \ + return false; \ + } + +bool ValidatePath(const std::wstring& acPath) +{ + std::wstring errorText{}; + + if (acPath.find_last_of('\\') == std::string::npos || acPath.ends_with(*"\\")) + { + errorText += L"Invalid path\n"; + } + + if (!acPath.ends_with(L".exe")) + { + errorText += acPath.substr(acPath.find_last_of('\\') + 1, acPath.back()) + L" is not an executable file\n"; + } + + if (!std::filesystem::exists(g_exePath) || !std::filesystem::exists(g_titlePath)) + { + errorText += L"Path does not exist\n"; + } + + if (!errorText.empty()) + { + errorText += L"Path: " + acPath; + DIE_NOW(errorText.c_str()) + } + + return true; +} +} // namespace + +bool PathArgument(const std::string& acPath) +{ + g_exePath = std::wstring(acPath.begin(), acPath.end()); + g_titlePath = g_exePath.substr(0, g_exePath.find_last_of('\\')); + + if (!ValidatePath(g_exePath)) + { + DIE_NOW(L"Failed to validate path") + } + + // Write to registry so oobe::SelectInstall can handle the rest + bool result = Registry::WriteString(HKEY_CURRENT_USER, kTiltedRegistryPath, L"TitlePath", g_titlePath) && + Registry::WriteString(HKEY_CURRENT_USER, kTiltedRegistryPath, L"TitleExe", g_exePath); + + if (!result) + { + DIE_NOW(L"Failed to write to registry") + } + + return true; +} +} // namespace oobe diff --git a/Code/immersive_launcher/oobe/PathArgument.h b/Code/immersive_launcher/oobe/PathArgument.h new file mode 100644 index 000000000..8c2b8f1ba --- /dev/null +++ b/Code/immersive_launcher/oobe/PathArgument.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +namespace oobe +{ +bool PathArgument(const std::string& acPath); +} From b7aa97298746d87b64dfbd451004eb632e314953 Mon Sep 17 00:00:00 2001 From: Tony Date: Fri, 6 Sep 2024 17:34:28 -0400 Subject: [PATCH 2/8] tweak: add explicit target exe error --- Code/immersive_launcher/oobe/PathArgument.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Code/immersive_launcher/oobe/PathArgument.cpp b/Code/immersive_launcher/oobe/PathArgument.cpp index 2609b2811..6b216ce1d 100644 --- a/Code/immersive_launcher/oobe/PathArgument.cpp +++ b/Code/immersive_launcher/oobe/PathArgument.cpp @@ -37,6 +37,10 @@ bool ValidatePath(const std::wstring& acPath) { errorText += acPath.substr(acPath.find_last_of('\\') + 1, acPath.back()) + L" is not an executable file\n"; } + else if (!acPath.ends_with(TARGET_NAME L".exe")) + { + errorText += TARGET_NAME L".exe not found\n"; + } if (!std::filesystem::exists(g_exePath) || !std::filesystem::exists(g_titlePath)) { From 8da8dd8a2156252f86a0a08edf30bc9cb62ddf07 Mon Sep 17 00:00:00 2001 From: Tony Date: Fri, 6 Sep 2024 17:38:19 -0400 Subject: [PATCH 3/8] tweak: clang format --- Code/immersive_launcher/oobe/PathArgument.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Code/immersive_launcher/oobe/PathArgument.cpp b/Code/immersive_launcher/oobe/PathArgument.cpp index 6b216ce1d..55b5d781c 100644 --- a/Code/immersive_launcher/oobe/PathArgument.cpp +++ b/Code/immersive_launcher/oobe/PathArgument.cpp @@ -68,8 +68,7 @@ bool PathArgument(const std::string& acPath) } // Write to registry so oobe::SelectInstall can handle the rest - bool result = Registry::WriteString(HKEY_CURRENT_USER, kTiltedRegistryPath, L"TitlePath", g_titlePath) && - Registry::WriteString(HKEY_CURRENT_USER, kTiltedRegistryPath, L"TitleExe", g_exePath); + bool result = Registry::WriteString(HKEY_CURRENT_USER, kTiltedRegistryPath, L"TitlePath", g_titlePath) && Registry::WriteString(HKEY_CURRENT_USER, kTiltedRegistryPath, L"TitleExe", g_exePath); if (!result) { From 1bc1a1ba338f7f39b01e7bb6dd91d2b06bf169d9 Mon Sep 17 00:00:00 2001 From: Tony Date: Fri, 6 Sep 2024 17:52:51 -0400 Subject: [PATCH 4/8] fix: properly terminate process when no exe path given --- Code/immersive_launcher/Launcher.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Code/immersive_launcher/Launcher.cpp b/Code/immersive_launcher/Launcher.cpp index ef65c4128..8f522a92e 100644 --- a/Code/immersive_launcher/Launcher.cpp +++ b/Code/immersive_launcher/Launcher.cpp @@ -66,7 +66,8 @@ void SetMaxstdio() int StartUp(int argc, char** argv) { bool askSelect = (GetAsyncKeyState(VK_SPACE) & 0x8000); - HandleArguments(argc, argv, askSelect); + if (!HandleArguments(argc, argv, askSelect)) + return -1; // TODO(Force): Make some InitSharedResources func. g_SharedWindowIcon = LoadIconW(GetModuleHandleW(nullptr), MAKEINTRESOURCEW(102)); @@ -150,12 +151,14 @@ bool HandleArguments(int aArgc, char** aArgv, bool& aAskSelect) { if (!aArgv[i + 1]) { - DIE_NOW(L"No exe path specified"); + Die(L"No exe path specified", true); + return false; } if (!oobe::PathArgument(aArgv[i + 1])) { - DIE_NOW(L"Failed to parse path argument"); + Die(L"Failed to parse path argument", true); + return false; } } } From 50488e30c719f1f62139861b52517c40dbef45a9 Mon Sep 17 00:00:00 2001 From: Tony Date: Mon, 9 Sep 2024 16:01:11 -0400 Subject: [PATCH 5/8] tweak: safer args bounds check + more explicit errors --- Code/immersive_launcher/Launcher.cpp | 4 +++- Code/immersive_launcher/oobe/PathArgument.cpp | 6 +++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Code/immersive_launcher/Launcher.cpp b/Code/immersive_launcher/Launcher.cpp index 8f522a92e..d71fce5d1 100644 --- a/Code/immersive_launcher/Launcher.cpp +++ b/Code/immersive_launcher/Launcher.cpp @@ -149,14 +149,16 @@ bool HandleArguments(int aArgc, char** aArgv, bool& aAskSelect) aAskSelect = true; else if (std::strcmp(aArgv[i], "--exePath") == 0) { - if (!aArgv[i + 1]) + if(i + 1 >= aArgc) { + SetLastError(ERROR_BAD_PATHNAME); Die(L"No exe path specified", true); return false; } if (!oobe::PathArgument(aArgv[i + 1])) { + SetLastError(ERROR_BAD_ARGUMENTS); Die(L"Failed to parse path argument", true); return false; } diff --git a/Code/immersive_launcher/oobe/PathArgument.cpp b/Code/immersive_launcher/oobe/PathArgument.cpp index 55b5d781c..7b582b5c8 100644 --- a/Code/immersive_launcher/oobe/PathArgument.cpp +++ b/Code/immersive_launcher/oobe/PathArgument.cpp @@ -30,26 +30,30 @@ bool ValidatePath(const std::wstring& acPath) if (acPath.find_last_of('\\') == std::string::npos || acPath.ends_with(*"\\")) { + SetLastError(ERROR_BAD_PATHNAME); errorText += L"Invalid path\n"; } if (!acPath.ends_with(L".exe")) { + SetLastError(ERROR_BAD_ARGUMENTS); errorText += acPath.substr(acPath.find_last_of('\\') + 1, acPath.back()) + L" is not an executable file\n"; } else if (!acPath.ends_with(TARGET_NAME L".exe")) { + SetLastError(ERROR_FILE_NOT_FOUND); errorText += TARGET_NAME L".exe not found\n"; } if (!std::filesystem::exists(g_exePath) || !std::filesystem::exists(g_titlePath)) { + SetLastError(ERROR_BAD_PATHNAME); errorText += L"Path does not exist\n"; } if (!errorText.empty()) { - errorText += L"Path: " + acPath; + errorText += L"\nPath: " + acPath; DIE_NOW(errorText.c_str()) } From 0f9c6addc6ac0973b1b481f6e50d3cfd7a03cae8 Mon Sep 17 00:00:00 2001 From: Tony Date: Mon, 9 Sep 2024 16:14:59 -0400 Subject: [PATCH 6/8] tweak: remove global exePath & titlePath --- Code/immersive_launcher/oobe/PathArgument.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Code/immersive_launcher/oobe/PathArgument.cpp b/Code/immersive_launcher/oobe/PathArgument.cpp index 7b582b5c8..ac1b230c7 100644 --- a/Code/immersive_launcher/oobe/PathArgument.cpp +++ b/Code/immersive_launcher/oobe/PathArgument.cpp @@ -7,9 +7,6 @@ #include -std::wstring g_titlePath; -std::wstring g_exePath; - namespace oobe { using namespace TiltedPhoques; @@ -26,6 +23,7 @@ constexpr wchar_t kTiltedRegistryPath[] = LR"(Software\TiltedPhoques\TiltedEvolu bool ValidatePath(const std::wstring& acPath) { + const std::wstring titlePath = acPath.substr(0, acPath.find_last_of('\\')); std::wstring errorText{}; if (acPath.find_last_of('\\') == std::string::npos || acPath.ends_with(*"\\")) @@ -45,7 +43,8 @@ bool ValidatePath(const std::wstring& acPath) errorText += TARGET_NAME L".exe not found\n"; } - if (!std::filesystem::exists(g_exePath) || !std::filesystem::exists(g_titlePath)) + + if (!std::filesystem::exists(acPath) || !std::filesystem::exists(titlePath)) { SetLastError(ERROR_BAD_PATHNAME); errorText += L"Path does not exist\n"; @@ -63,16 +62,16 @@ bool ValidatePath(const std::wstring& acPath) bool PathArgument(const std::string& acPath) { - g_exePath = std::wstring(acPath.begin(), acPath.end()); - g_titlePath = g_exePath.substr(0, g_exePath.find_last_of('\\')); + const std::wstring exePath = std::wstring(acPath.begin(), acPath.end()); + const std::wstring titlePath = exePath.substr(0, exePath.find_last_of('\\')); - if (!ValidatePath(g_exePath)) + if (!ValidatePath(exePath)) { DIE_NOW(L"Failed to validate path") } // Write to registry so oobe::SelectInstall can handle the rest - bool result = Registry::WriteString(HKEY_CURRENT_USER, kTiltedRegistryPath, L"TitlePath", g_titlePath) && Registry::WriteString(HKEY_CURRENT_USER, kTiltedRegistryPath, L"TitleExe", g_exePath); + const bool result = Registry::WriteString(HKEY_CURRENT_USER, kTiltedRegistryPath, L"TitlePath", titlePath) && Registry::WriteString(HKEY_CURRENT_USER, kTiltedRegistryPath, L"TitleExe", exePath); if (!result) { From 855a9b1bc2afcaab18d79706f04e748c3f5b2f62 Mon Sep 17 00:00:00 2001 From: Tony Date: Mon, 9 Sep 2024 16:16:39 -0400 Subject: [PATCH 7/8] tweak: clang format --- Code/immersive_launcher/Launcher.cpp | 2 +- Code/immersive_launcher/oobe/PathArgument.cpp | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Code/immersive_launcher/Launcher.cpp b/Code/immersive_launcher/Launcher.cpp index d71fce5d1..185a7d9fd 100644 --- a/Code/immersive_launcher/Launcher.cpp +++ b/Code/immersive_launcher/Launcher.cpp @@ -149,7 +149,7 @@ bool HandleArguments(int aArgc, char** aArgv, bool& aAskSelect) aAskSelect = true; else if (std::strcmp(aArgv[i], "--exePath") == 0) { - if(i + 1 >= aArgc) + if (i + 1 >= aArgc) { SetLastError(ERROR_BAD_PATHNAME); Die(L"No exe path specified", true); diff --git a/Code/immersive_launcher/oobe/PathArgument.cpp b/Code/immersive_launcher/oobe/PathArgument.cpp index ac1b230c7..3c960456d 100644 --- a/Code/immersive_launcher/oobe/PathArgument.cpp +++ b/Code/immersive_launcher/oobe/PathArgument.cpp @@ -43,7 +43,6 @@ bool ValidatePath(const std::wstring& acPath) errorText += TARGET_NAME L".exe not found\n"; } - if (!std::filesystem::exists(acPath) || !std::filesystem::exists(titlePath)) { SetLastError(ERROR_BAD_PATHNAME); From 79f3ba817974d37b9d870da2f7c0363d2f987894 Mon Sep 17 00:00:00 2001 From: Tony Date: Mon, 9 Sep 2024 16:20:56 -0400 Subject: [PATCH 8/8] tweak: const naming --- Code/immersive_launcher/oobe/PathArgument.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Code/immersive_launcher/oobe/PathArgument.cpp b/Code/immersive_launcher/oobe/PathArgument.cpp index 3c960456d..38daaaee6 100644 --- a/Code/immersive_launcher/oobe/PathArgument.cpp +++ b/Code/immersive_launcher/oobe/PathArgument.cpp @@ -23,7 +23,7 @@ constexpr wchar_t kTiltedRegistryPath[] = LR"(Software\TiltedPhoques\TiltedEvolu bool ValidatePath(const std::wstring& acPath) { - const std::wstring titlePath = acPath.substr(0, acPath.find_last_of('\\')); + const std::wstring cTitlePath = acPath.substr(0, acPath.find_last_of('\\')); std::wstring errorText{}; if (acPath.find_last_of('\\') == std::string::npos || acPath.ends_with(*"\\")) @@ -43,7 +43,7 @@ bool ValidatePath(const std::wstring& acPath) errorText += TARGET_NAME L".exe not found\n"; } - if (!std::filesystem::exists(acPath) || !std::filesystem::exists(titlePath)) + if (!std::filesystem::exists(acPath) || !std::filesystem::exists(cTitlePath)) { SetLastError(ERROR_BAD_PATHNAME); errorText += L"Path does not exist\n"; @@ -61,16 +61,16 @@ bool ValidatePath(const std::wstring& acPath) bool PathArgument(const std::string& acPath) { - const std::wstring exePath = std::wstring(acPath.begin(), acPath.end()); - const std::wstring titlePath = exePath.substr(0, exePath.find_last_of('\\')); + const std::wstring cExePath = std::wstring(acPath.begin(), acPath.end()); + const std::wstring cTitlePath = cExePath.substr(0, cExePath.find_last_of('\\')); - if (!ValidatePath(exePath)) + if (!ValidatePath(cExePath)) { DIE_NOW(L"Failed to validate path") } // Write to registry so oobe::SelectInstall can handle the rest - const bool result = Registry::WriteString(HKEY_CURRENT_USER, kTiltedRegistryPath, L"TitlePath", titlePath) && Registry::WriteString(HKEY_CURRENT_USER, kTiltedRegistryPath, L"TitleExe", exePath); + const bool result = Registry::WriteString(HKEY_CURRENT_USER, kTiltedRegistryPath, L"TitlePath", cTitlePath) && Registry::WriteString(HKEY_CURRENT_USER, kTiltedRegistryPath, L"TitleExe", cExePath); if (!result) {