@@ -18,6 +18,7 @@ enum LauncherError : int {
18
18
E_OS_ERROR = -1 ,
19
19
E_APP_NOT_FOUND = -2 ,
20
20
E_MODENGINE_NOT_FOUND = -3 ,
21
+ E_CREATE_PROCESS_FAILED = -4 ,
21
22
};
22
23
23
24
struct LaunchTargetParams {
@@ -51,46 +52,74 @@ static std::map<std::string, LaunchTarget> exe_names {
51
52
{ " armoredcore6.exe" , ARMORED_CORE_6 },
52
53
};
53
54
54
- std::wstring GetCurrentDirectory ()
55
+ namespace platform {
56
+ std::wstring get_env_var (const std::wstring& name)
55
57
{
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 ];
59
60
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;
61
71
}
62
72
63
- int main ( )
73
+ void set_env_var ( const std::wstring& name, const std::wstring& value )
64
74
{
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 ];
66
82
67
- wchar_t launcher_filename[MAX_PATH] ;
83
+ fs::path path = fs::current_path () ;
68
84
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 ();
73
90
}
74
91
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
+
75
105
CLI::App app { " ModEngine Launcher" };
76
106
77
107
LaunchTarget target = AUTODETECT;
78
108
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));
80
110
81
111
std::string target_path_string;
82
112
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);
84
114
85
115
std::string config_path_string;
86
116
auto config_option = app.add_option (" -c,--config" , config_path_string, " ModEngine configuration file path" )
87
- ->transform (CLI::ExistingFile);
117
+ ->transform (CLI::ExistingFile);
88
118
89
119
bool suspend = false ;
90
120
app.add_option (" -s,--suspend" , suspend, " Start the game in a suspended state" );
91
121
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" ;
94
123
95
124
app.add_option (" --modengine-dll" , modengine_dll_path, " ModEngine DLL file path (modengine2.dll)" );
96
125
@@ -103,8 +132,7 @@ int main()
103
132
std::optional<fs::path> app_path = std::nullopt;
104
133
105
134
// 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 ()) {
108
136
app_path = absolute (CLI::to_path (target_path_string)).parent_path ().parent_path ();
109
137
if (target == AUTODETECT) {
110
138
logger->error (" Game target must be specified when supplying a manual path" );
@@ -115,7 +143,7 @@ int main()
115
143
// If the game target was not set, try to find a game exe in the current directory and infer from that
116
144
if (target_option->empty ()) {
117
145
for (auto & name_kv : exe_names) {
118
- auto exepath = launcher_path. parent_path () / name_kv.first ;
146
+ auto exepath = launcher_directory / name_kv.first ;
119
147
if (fs::exists (exepath)) {
120
148
target = name_kv.second ;
121
149
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()
133
161
134
162
// If a config wasn't specified, try to load the default one for the game
135
163
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 ;
137
165
if (!fs::exists (default_config_path)) {
138
166
logger->error (" Could not find default config file at {}" , default_config_path.string ());
139
167
}
@@ -164,30 +192,34 @@ int main()
164
192
auto kernel32 = LoadLibraryW (L" kernel32.dll" );
165
193
auto create_process_addr = GetProcAddress (kernel32, " CreateProcessW" );
166
194
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" );
169
196
exec_path.append (L" ;" );
170
197
exec_path.append (modengine_dll_path.parent_path ().native ());
171
198
172
199
auto config_path = CLI::to_path (config_path_string);
173
200
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);
176
203
}
177
204
178
205
// 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);
182
209
183
210
if (suspend || IsDebuggerPresent ()) {
184
- SetEnvironmentVariableW (L" MODENGINE_DEBUG_GAME" , L" 1" );
211
+ platform::set_env_var (L" MODENGINE_DEBUG_GAME" , L" 1" );
185
212
}
186
213
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 ;
189
219
190
- auto proc_flags = CREATE_NEW_PROCESS_GROUP;
220
+ wcscpy_s (cmd, cmd_len, cmd_str.c_str ());
221
+
222
+ auto proc_flags = 0 ;
191
223
bool success = DetourCreateProcessWithDllW (
192
224
cmd,
193
225
nullptr ,
@@ -202,12 +234,15 @@ int main()
202
234
fs::absolute (modengine_dll_path).string ().c_str (),
203
235
reinterpret_cast <PDETOUR_CREATE_PROCESS_ROUTINEW>(create_process_addr));
204
236
237
+ auto status = E_OK;
238
+
205
239
if (!success) {
206
240
logger->error (" Couldn't create process: {:x}" , GetLastError ());
241
+ status = E_CREATE_PROCESS_FAILED;
207
242
}
208
243
209
244
CloseHandle (pi .hProcess );
210
245
CloseHandle (pi .hThread );
211
246
212
- return E_OK ;
247
+ return status ;
213
248
}
0 commit comments