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

Commit

Permalink
Merge pull request #193 from soulsmods/bugfix/launch-failures
Browse files Browse the repository at this point in the history
Fixes for ME2 launch failures in some scenarios
  • Loading branch information
garyttierney authored Mar 27, 2024
2 parents 69965a0 + 6a00df5 commit 5d38623
Showing 1 changed file with 68 additions and 33 deletions.
101 changes: 68 additions & 33 deletions launcher/launcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ enum LauncherError : int {
E_OS_ERROR = -1,
E_APP_NOT_FOUND = -2,
E_MODENGINE_NOT_FOUND = -3,
E_CREATE_PROCESS_FAILED = -4,
};

struct LaunchTargetParams {
Expand Down Expand Up @@ -51,46 +52,74 @@ static std::map<std::string, LaunchTarget> exe_names {
{ "armoredcore6.exe", ARMORED_CORE_6 },
};

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

return std::wstring(buffer).substr(0, pos);
std::wstring value;

if (buffer_size > 0) {
size_t len = GetEnvironmentVariableW(name.c_str(), &buffer[0], buffer_size + 1);
value.append(buffer, len);
}

delete buffer;

return value;
}

int main()
void set_env_var(const std::wstring& name, const std::wstring& value)
{
auto logger = spdlog::stderr_color_mt("stderr");
SetEnvironmentVariableW(name.c_str(), value.c_str());
}

fs::path get_launcher_directory()
{
size_t buffer_size = GetModuleFileNameW(nullptr, nullptr, 0);
auto* buffer = new wchar_t[buffer_size + 1];

wchar_t launcher_filename[MAX_PATH];
fs::path path = fs::current_path();

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

path = launcher_path.parent_path();
}

delete buffer;

return path;
}

}

int main()
{
auto logger = spdlog::stderr_color_mt("stderr");
auto launcher_directory = platform::get_launcher_directory();


CLI::App app { "ModEngine Launcher" };

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

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

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

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

auto launcher_path = fs::path(launcher_filename);
auto modengine_dll_path = launcher_path.parent_path() / L"modengine2" / L"bin" / L"modengine2.dll";
auto modengine_dll_path = launcher_directory / L"modengine2" / L"bin" / L"modengine2.dll";

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

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

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

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

auto exec_path_env = std::getenv("PATH");
auto exec_path = std::wstring(exec_path_env, exec_path_env + strlen(exec_path_env));
auto exec_path = platform::get_env_var(L"PATH");
exec_path.append(L";");
exec_path.append(modengine_dll_path.parent_path().native());

auto config_path = CLI::to_path(config_path_string);
if (config_path.is_relative()) {
const auto search_path = GetCurrentDirectory() / config_path;
config_path = absolute(search_path);
const auto search_path = launcher_directory / config_path;
config_path = fs::absolute(search_path);
}

// These are inherited by the game process we launch with Detours.
SetEnvironmentVariable(L"SteamAppId", launch_params.app_id.c_str());
SetEnvironmentVariable(L"MODENGINE_CONFIG", config_path.c_str());
SetEnvironmentVariable(L"PATH", exec_path.c_str());
platform::set_env_var(L"SteamAppId", launch_params.app_id);
platform::set_env_var(L"MODENGINE_CONFIG", config_path.native());
platform::set_env_var(L"PATH", exec_path);

if (suspend || IsDebuggerPresent()) {
SetEnvironmentVariableW(L"MODENGINE_DEBUG_GAME", L"1");
platform::set_env_var(L"MODENGINE_DEBUG_GAME", L"1");
}

wchar_t cmd[MAX_PATH] = {};
wcscpy_s(cmd, app_cmd.c_str());
std::wstring cmd_str = app_cmd.native();
size_t cmd_len = cmd_str.length();

auto *cmd = new wchar_t[cmd_len + 1];
cmd[cmd_len] = 0;

auto proc_flags = CREATE_NEW_PROCESS_GROUP;
wcscpy_s(cmd, cmd_len, cmd_str.c_str());

auto proc_flags = 0;
bool success = DetourCreateProcessWithDllW(
cmd,
nullptr,
Expand All @@ -202,12 +234,15 @@ int main()
fs::absolute(modengine_dll_path).string().c_str(),
reinterpret_cast<PDETOUR_CREATE_PROCESS_ROUTINEW>(create_process_addr));

auto status = E_OK;

if (!success) {
logger->error("Couldn't create process: {:x}", GetLastError());
status = E_CREATE_PROCESS_FAILED;
}

CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);

return E_OK;
return status;
}

0 comments on commit 5d38623

Please sign in to comment.