Skip to content
This repository was archived by the owner on Jul 19, 2024. It is now read-only.

Commit 5d38623

Browse files
authored
Merge pull request #193 from soulsmods/bugfix/launch-failures
Fixes for ME2 launch failures in some scenarios
2 parents 69965a0 + 6a00df5 commit 5d38623

File tree

1 file changed

+68
-33
lines changed

1 file changed

+68
-33
lines changed

Diff for: launcher/launcher.cpp

+68-33
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ enum LauncherError : int {
1818
E_OS_ERROR = -1,
1919
E_APP_NOT_FOUND = -2,
2020
E_MODENGINE_NOT_FOUND = -3,
21+
E_CREATE_PROCESS_FAILED = -4,
2122
};
2223

2324
struct LaunchTargetParams {
@@ -51,46 +52,74 @@ static std::map<std::string, LaunchTarget> exe_names {
5152
{ "armoredcore6.exe", ARMORED_CORE_6 },
5253
};
5354

54-
std::wstring GetCurrentDirectory()
55+
namespace platform {
56+
std::wstring get_env_var(const std::wstring& name)
5557
{
56-
wchar_t buffer[MAX_PATH];
57-
GetModuleFileNameW(NULL, buffer, MAX_PATH);
58-
std::string::size_type pos = std::wstring(buffer).find_last_of(L"\\/");
58+
size_t buffer_size = GetEnvironmentVariableW(name.c_str(), nullptr, 0);
59+
auto* buffer = new wchar_t[buffer_size + 1];
5960

60-
return std::wstring(buffer).substr(0, pos);
61+
std::wstring value;
62+
63+
if (buffer_size > 0) {
64+
size_t len = GetEnvironmentVariableW(name.c_str(), &buffer[0], buffer_size + 1);
65+
value.append(buffer, len);
66+
}
67+
68+
delete buffer;
69+
70+
return value;
6171
}
6272

63-
int main()
73+
void set_env_var(const std::wstring& name, const std::wstring& value)
6474
{
65-
auto logger = spdlog::stderr_color_mt("stderr");
75+
SetEnvironmentVariableW(name.c_str(), value.c_str());
76+
}
77+
78+
fs::path get_launcher_directory()
79+
{
80+
size_t buffer_size = GetModuleFileNameW(nullptr, nullptr, 0);
81+
auto* buffer = new wchar_t[buffer_size + 1];
6682

67-
wchar_t launcher_filename[MAX_PATH];
83+
fs::path path = fs::current_path();
6884

69-
// This isn't always needed, but cli11 doesn't allow us to signal an error
70-
// from the function that produces the default value for the modengine.dll path.
71-
if (!GetModuleFileNameW(nullptr, launcher_filename, MAX_PATH)) {
72-
return E_OS_ERROR;
85+
if (buffer_size > 0) {
86+
size_t len = GetModuleFileNameW(nullptr, buffer, buffer_size + 1);
87+
fs::path launcher_path(std::wstring_view { buffer, len });
88+
89+
path = launcher_path.parent_path();
7390
}
7491

92+
delete buffer;
93+
94+
return path;
95+
}
96+
97+
}
98+
99+
int main()
100+
{
101+
auto logger = spdlog::stderr_color_mt("stderr");
102+
auto launcher_directory = platform::get_launcher_directory();
103+
104+
75105
CLI::App app { "ModEngine Launcher" };
76106

77107
LaunchTarget target = AUTODETECT;
78108
auto target_option = app.add_option("-t,--launch-target", target, "Launch target")
79-
->transform(CLI::CheckedTransformer(launch_target_names, CLI::ignore_case));
109+
->transform(CLI::CheckedTransformer(launch_target_names, CLI::ignore_case));
80110

81111
std::string target_path_string;
82112
auto target_path_option = app.add_option("-p,--game-path", target_path_string, "Path to game executable. Will autodetect if not specified.")
83-
->transform(CLI::ExistingFile);
113+
->transform(CLI::ExistingFile);
84114

85115
std::string config_path_string;
86116
auto config_option = app.add_option("-c,--config", config_path_string, "ModEngine configuration file path")
87-
->transform(CLI::ExistingFile);
117+
->transform(CLI::ExistingFile);
88118

89119
bool suspend = false;
90120
app.add_option("-s,--suspend", suspend, "Start the game in a suspended state");
91121

92-
auto launcher_path = fs::path(launcher_filename);
93-
auto modengine_dll_path = launcher_path.parent_path() / L"modengine2" / L"bin" / L"modengine2.dll";
122+
auto modengine_dll_path = launcher_directory / L"modengine2" / L"bin" / L"modengine2.dll";
94123

95124
app.add_option("--modengine-dll", modengine_dll_path, "ModEngine DLL file path (modengine2.dll)");
96125

@@ -103,8 +132,7 @@ int main()
103132
std::optional<fs::path> app_path = std::nullopt;
104133

105134
// First if the game path was specified, use that along with the specified target
106-
if (!target_path_option->empty())
107-
{
135+
if (!target_path_option->empty()) {
108136
app_path = absolute(CLI::to_path(target_path_string)).parent_path().parent_path();
109137
if (target == AUTODETECT) {
110138
logger->error("Game target must be specified when supplying a manual path");
@@ -115,7 +143,7 @@ int main()
115143
// If the game target was not set, try to find a game exe in the current directory and infer from that
116144
if (target_option->empty()) {
117145
for (auto& name_kv : exe_names) {
118-
auto exepath = launcher_path.parent_path() / name_kv.first;
146+
auto exepath = launcher_directory / name_kv.first;
119147
if (fs::exists(exepath)) {
120148
target = name_kv.second;
121149
app_path = exepath.parent_path().parent_path(); // app_path is expected to be steam app path not exe path
@@ -133,7 +161,7 @@ int main()
133161

134162
// If a config wasn't specified, try to load the default one for the game
135163
if (config_option->empty()) {
136-
auto default_config_path = launcher_path.parent_path() / launch_params.default_config;
164+
auto default_config_path = launcher_directory / launch_params.default_config;
137165
if (!fs::exists(default_config_path)) {
138166
logger->error("Could not find default config file at {}", default_config_path.string());
139167
}
@@ -164,30 +192,34 @@ int main()
164192
auto kernel32 = LoadLibraryW(L"kernel32.dll");
165193
auto create_process_addr = GetProcAddress(kernel32, "CreateProcessW");
166194

167-
auto exec_path_env = std::getenv("PATH");
168-
auto exec_path = std::wstring(exec_path_env, exec_path_env + strlen(exec_path_env));
195+
auto exec_path = platform::get_env_var(L"PATH");
169196
exec_path.append(L";");
170197
exec_path.append(modengine_dll_path.parent_path().native());
171198

172199
auto config_path = CLI::to_path(config_path_string);
173200
if (config_path.is_relative()) {
174-
const auto search_path = GetCurrentDirectory() / config_path;
175-
config_path = absolute(search_path);
201+
const auto search_path = launcher_directory / config_path;
202+
config_path = fs::absolute(search_path);
176203
}
177204

178205
// These are inherited by the game process we launch with Detours.
179-
SetEnvironmentVariable(L"SteamAppId", launch_params.app_id.c_str());
180-
SetEnvironmentVariable(L"MODENGINE_CONFIG", config_path.c_str());
181-
SetEnvironmentVariable(L"PATH", exec_path.c_str());
206+
platform::set_env_var(L"SteamAppId", launch_params.app_id);
207+
platform::set_env_var(L"MODENGINE_CONFIG", config_path.native());
208+
platform::set_env_var(L"PATH", exec_path);
182209

183210
if (suspend || IsDebuggerPresent()) {
184-
SetEnvironmentVariableW(L"MODENGINE_DEBUG_GAME", L"1");
211+
platform::set_env_var(L"MODENGINE_DEBUG_GAME", L"1");
185212
}
186213

187-
wchar_t cmd[MAX_PATH] = {};
188-
wcscpy_s(cmd, app_cmd.c_str());
214+
std::wstring cmd_str = app_cmd.native();
215+
size_t cmd_len = cmd_str.length();
216+
217+
auto *cmd = new wchar_t[cmd_len + 1];
218+
cmd[cmd_len] = 0;
189219

190-
auto proc_flags = CREATE_NEW_PROCESS_GROUP;
220+
wcscpy_s(cmd, cmd_len, cmd_str.c_str());
221+
222+
auto proc_flags = 0;
191223
bool success = DetourCreateProcessWithDllW(
192224
cmd,
193225
nullptr,
@@ -202,12 +234,15 @@ int main()
202234
fs::absolute(modengine_dll_path).string().c_str(),
203235
reinterpret_cast<PDETOUR_CREATE_PROCESS_ROUTINEW>(create_process_addr));
204236

237+
auto status = E_OK;
238+
205239
if (!success) {
206240
logger->error("Couldn't create process: {:x}", GetLastError());
241+
status = E_CREATE_PROCESS_FAILED;
207242
}
208243

209244
CloseHandle(pi.hProcess);
210245
CloseHandle(pi.hThread);
211246

212-
return E_OK;
247+
return status;
213248
}

0 commit comments

Comments
 (0)