Skip to content

Commit ab18db9

Browse files
authored
Game exe path command line argument (#713)
* feat: --exePath commandline argument ex. `SkyrimTogether.exe --exePath C:\Path\To\Game\SkyrimSE.exe` * tweak: add explicit target exe error * tweak: clang format * fix: properly terminate process when no exe path given * tweak: safer args bounds check + more explicit errors * tweak: remove global exePath & titlePath * tweak: clang format * tweak: const naming
1 parent f142880 commit ab18db9

File tree

4 files changed

+124
-5
lines changed

4 files changed

+124
-5
lines changed

Code/immersive_launcher/Launcher.cpp

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@
1010
#include "Utils/FileVersion.inl"
1111

1212
#include "oobe/PathSelection.h"
13+
#include "oobe/PathArgument.h"
1314
#include "oobe/SupportChecks.h"
1415
#include "steam/SteamLoader.h"
1516

1617
#include "base/dialogues/win/TaskDialog.h"
18+
#include "utils/Registry.h"
1719

1820
#include <BranchInfo.h>
1921

@@ -64,11 +66,8 @@ void SetMaxstdio()
6466
int StartUp(int argc, char** argv)
6567
{
6668
bool askSelect = (GetAsyncKeyState(VK_SPACE) & 0x8000);
67-
for (int i = 1; i < argc; i++)
68-
{
69-
if (std::strcmp(argv[i], "-r") == 0)
70-
askSelect = true;
71-
}
69+
if (!HandleArguments(argc, argv, askSelect))
70+
return -1;
7271

7372
// TODO(Force): Make some InitSharedResources func.
7473
g_SharedWindowIcon = LoadIconW(GetModuleHandleW(nullptr), MAKEINTRESOURCEW(102));
@@ -141,6 +140,33 @@ void InitClient()
141140
// Jump into client code.
142141
RunTiltedApp();
143142
}
143+
144+
bool HandleArguments(int aArgc, char** aArgv, bool& aAskSelect)
145+
{
146+
for (int i = 1; i < aArgc; i++)
147+
{
148+
if (std::strcmp(aArgv[i], "-r") == 0)
149+
aAskSelect = true;
150+
else if (std::strcmp(aArgv[i], "--exePath") == 0)
151+
{
152+
if (i + 1 >= aArgc)
153+
{
154+
SetLastError(ERROR_BAD_PATHNAME);
155+
Die(L"No exe path specified", true);
156+
return false;
157+
}
158+
159+
if (!oobe::PathArgument(aArgv[i + 1]))
160+
{
161+
SetLastError(ERROR_BAD_ARGUMENTS);
162+
Die(L"Failed to parse path argument", true);
163+
return false;
164+
}
165+
}
166+
}
167+
168+
return true;
169+
}
144170
} // namespace launcher
145171

146172
// CreateProcess in suspended mode.

Code/immersive_launcher/Launcher.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,7 @@ bool LoadProgram(LaunchContext&);
3030
int StartUp(int argc, char** argv);
3131

3232
void InitClient();
33+
34+
bool HandleArguments(int, char**, bool&);
35+
3336
} // namespace launcher
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
#include "PathArgument.h"
2+
3+
#include "TargetConfig.h"
4+
5+
#include "Utils/Error.h"
6+
#include "utils/Registry.h"
7+
8+
#include <filesystem>
9+
10+
namespace oobe
11+
{
12+
using namespace TiltedPhoques;
13+
14+
namespace
15+
{
16+
constexpr wchar_t kTiltedRegistryPath[] = LR"(Software\TiltedPhoques\TiltedEvolution\)" SHORT_NAME;
17+
18+
#define DIE_NOW(err) \
19+
{ \
20+
Die(err, true); \
21+
return false; \
22+
}
23+
24+
bool ValidatePath(const std::wstring& acPath)
25+
{
26+
const std::wstring cTitlePath = acPath.substr(0, acPath.find_last_of('\\'));
27+
std::wstring errorText{};
28+
29+
if (acPath.find_last_of('\\') == std::string::npos || acPath.ends_with(*"\\"))
30+
{
31+
SetLastError(ERROR_BAD_PATHNAME);
32+
errorText += L"Invalid path\n";
33+
}
34+
35+
if (!acPath.ends_with(L".exe"))
36+
{
37+
SetLastError(ERROR_BAD_ARGUMENTS);
38+
errorText += acPath.substr(acPath.find_last_of('\\') + 1, acPath.back()) + L" is not an executable file\n";
39+
}
40+
else if (!acPath.ends_with(TARGET_NAME L".exe"))
41+
{
42+
SetLastError(ERROR_FILE_NOT_FOUND);
43+
errorText += TARGET_NAME L".exe not found\n";
44+
}
45+
46+
if (!std::filesystem::exists(acPath) || !std::filesystem::exists(cTitlePath))
47+
{
48+
SetLastError(ERROR_BAD_PATHNAME);
49+
errorText += L"Path does not exist\n";
50+
}
51+
52+
if (!errorText.empty())
53+
{
54+
errorText += L"\nPath: " + acPath;
55+
DIE_NOW(errorText.c_str())
56+
}
57+
58+
return true;
59+
}
60+
} // namespace
61+
62+
bool PathArgument(const std::string& acPath)
63+
{
64+
const std::wstring cExePath = std::wstring(acPath.begin(), acPath.end());
65+
const std::wstring cTitlePath = cExePath.substr(0, cExePath.find_last_of('\\'));
66+
67+
if (!ValidatePath(cExePath))
68+
{
69+
DIE_NOW(L"Failed to validate path")
70+
}
71+
72+
// Write to registry so oobe::SelectInstall can handle the rest
73+
const bool result = Registry::WriteString<wchar_t>(HKEY_CURRENT_USER, kTiltedRegistryPath, L"TitlePath", cTitlePath) && Registry::WriteString<wchar_t>(HKEY_CURRENT_USER, kTiltedRegistryPath, L"TitleExe", cExePath);
74+
75+
if (!result)
76+
{
77+
DIE_NOW(L"Failed to write to registry")
78+
}
79+
80+
return true;
81+
}
82+
} // namespace oobe
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#pragma once
2+
3+
#include <string>
4+
5+
namespace oobe
6+
{
7+
bool PathArgument(const std::string& acPath);
8+
}

0 commit comments

Comments
 (0)