forked from alterware/s1-mod
init
This commit is contained in:
296
src/client/main.cpp
Normal file
296
src/client/main.cpp
Normal file
@ -0,0 +1,296 @@
|
||||
#include <std_include.hpp>
|
||||
#include "launcher/launcher.hpp"
|
||||
#include "loader/loader.hpp"
|
||||
#include "loader/component_loader.hpp"
|
||||
#include "game/game.hpp"
|
||||
|
||||
#include <utils/string.hpp>
|
||||
#include <utils/flags.hpp>
|
||||
#include <utils/io.hpp>
|
||||
|
||||
#include <DbgHelp.h>
|
||||
|
||||
#include <version.hpp>
|
||||
|
||||
LONG WINAPI exception_handler(PEXCEPTION_POINTERS exception_info)
|
||||
{
|
||||
if (exception_info->ExceptionRecord->ExceptionCode == 0x406D1388)
|
||||
{
|
||||
return EXCEPTION_CONTINUE_EXECUTION;
|
||||
}
|
||||
|
||||
if (exception_info->ExceptionRecord->ExceptionCode < 0x80000000 ||
|
||||
exception_info->ExceptionRecord->ExceptionCode == 0xE06D7363)
|
||||
{
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
MINIDUMP_EXCEPTION_INFORMATION exception_information =
|
||||
{
|
||||
GetCurrentThreadId(), exception_info, FALSE
|
||||
};
|
||||
|
||||
const auto type = MiniDumpIgnoreInaccessibleMemory
|
||||
| MiniDumpWithHandleData
|
||||
| MiniDumpScanMemory
|
||||
| MiniDumpWithProcessThreadData
|
||||
| MiniDumpWithFullMemoryInfo
|
||||
| MiniDumpWithThreadInfo;
|
||||
|
||||
CreateDirectoryA("minidumps", nullptr);
|
||||
const auto* file_name = utils::string::va("minidumps\\s1-mod_%s_%u.dmp", SHORTVERSION, static_cast<std::uint32_t>(std::time(nullptr)));
|
||||
constexpr auto file_share = FILE_SHARE_READ | FILE_SHARE_WRITE;
|
||||
|
||||
const auto file_handle = CreateFileA(file_name, GENERIC_WRITE | GENERIC_READ, file_share, nullptr,
|
||||
CREATE_ALWAYS, NULL, nullptr);
|
||||
|
||||
if (!MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(),
|
||||
file_handle, static_cast<MINIDUMP_TYPE>(type),
|
||||
&exception_information, nullptr, nullptr))
|
||||
{
|
||||
char buf[4096]{};
|
||||
sprintf_s(buf, "An exception 0x%08X occurred at location 0x%p\n",
|
||||
exception_info->ExceptionRecord->ExceptionCode,
|
||||
exception_info->ExceptionRecord->ExceptionAddress);
|
||||
game::show_error(buf);
|
||||
}
|
||||
|
||||
CloseHandle(file_handle);
|
||||
TerminateProcess(GetCurrentProcess(), exception_info->ExceptionRecord->ExceptionCode);
|
||||
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
[[noreturn]] void WINAPI exit_hook(const int code)
|
||||
{
|
||||
component_loader::pre_destroy();
|
||||
std::exit(code);
|
||||
}
|
||||
|
||||
BOOL WINAPI system_parameters_info_a(const UINT uiAction, const UINT uiParam, const PVOID pvParam, const UINT fWinIni)
|
||||
{
|
||||
component_loader::post_unpack();
|
||||
return SystemParametersInfoA(uiAction, uiParam, pvParam, fWinIni);
|
||||
}
|
||||
|
||||
FARPROC WINAPI get_proc_address(const HMODULE hModule, const LPCSTR lpProcName)
|
||||
{
|
||||
if (lpProcName == "GlobalMemoryStatusEx"s)
|
||||
{
|
||||
component_loader::post_unpack();
|
||||
}
|
||||
|
||||
return GetProcAddress(hModule, lpProcName);
|
||||
}
|
||||
|
||||
launcher::mode detect_mode_from_arguments()
|
||||
{
|
||||
if (utils::flags::has_flag("dedicated"))
|
||||
{
|
||||
return launcher::mode::server;
|
||||
}
|
||||
|
||||
if (utils::flags::has_flag("multiplayer"))
|
||||
{
|
||||
return launcher::mode::multiplayer;
|
||||
}
|
||||
|
||||
if (utils::flags::has_flag("survival"))
|
||||
{
|
||||
return launcher::mode::survival;
|
||||
}
|
||||
|
||||
if (utils::flags::has_flag("zombies"))
|
||||
{
|
||||
return launcher::mode::zombies;
|
||||
}
|
||||
|
||||
if (utils::flags::has_flag("singleplayer"))
|
||||
{
|
||||
return launcher::mode::singleplayer;
|
||||
}
|
||||
|
||||
return launcher::mode::none;
|
||||
}
|
||||
|
||||
|
||||
FARPROC load_binary(const launcher::mode mode)
|
||||
{
|
||||
loader loader;
|
||||
utils::nt::library self;
|
||||
|
||||
loader.set_import_resolver([self](const std::string& library, const std::string& function) -> void*
|
||||
{
|
||||
if (library == "steam_api64.dll")
|
||||
{
|
||||
return self.get_proc<FARPROC>(function);
|
||||
}
|
||||
else if (function == "ExitProcess")
|
||||
{
|
||||
return exit_hook;
|
||||
}
|
||||
else if (function == "SystemParametersInfoA")
|
||||
{
|
||||
return system_parameters_info_a;
|
||||
}
|
||||
else if (function == "GetProcAddress")
|
||||
{
|
||||
return get_proc_address;
|
||||
}
|
||||
|
||||
return component_loader::load_import(library, function);
|
||||
});
|
||||
|
||||
std::string binary;
|
||||
switch (mode)
|
||||
{
|
||||
case launcher::mode::server:
|
||||
case launcher::mode::multiplayer:
|
||||
case launcher::mode::survival:
|
||||
case launcher::mode::zombies:
|
||||
binary = "s1_mp64_ship.exe";
|
||||
break;
|
||||
case launcher::mode::singleplayer:
|
||||
binary = "s1_sp64_ship.exe";
|
||||
break;
|
||||
case launcher::mode::none:
|
||||
default:
|
||||
throw std::runtime_error("Invalid game mode!");
|
||||
}
|
||||
|
||||
std::string data;
|
||||
if (!utils::io::read_file(binary, &data))
|
||||
{
|
||||
throw std::runtime_error(utils::string::va(
|
||||
"Failed to read game binary (%s)!\nPlease select the correct path in the launcher settings.",
|
||||
binary.data()));
|
||||
}
|
||||
|
||||
return loader.load_library(binary);
|
||||
}
|
||||
|
||||
void remove_crash_file()
|
||||
{
|
||||
utils::io::remove_file("__s1Exe");
|
||||
}
|
||||
|
||||
void enable_dpi_awareness()
|
||||
{
|
||||
const utils::nt::library user32{"user32.dll"};
|
||||
const auto set_dpi = user32
|
||||
? user32.get_proc<BOOL(WINAPI*)(DPI_AWARENESS_CONTEXT)>("SetProcessDpiAwarenessContext")
|
||||
: nullptr;
|
||||
if (set_dpi)
|
||||
{
|
||||
set_dpi(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
|
||||
}
|
||||
}
|
||||
|
||||
void limit_parallel_dll_loading()
|
||||
{
|
||||
const utils::nt::library self;
|
||||
const auto registry_path = R"(Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\)" + self.get_name();
|
||||
|
||||
HKEY key = nullptr;
|
||||
if (RegCreateKeyA(HKEY_LOCAL_MACHINE, registry_path.data(), &key) == ERROR_SUCCESS)
|
||||
{
|
||||
RegCloseKey(key);
|
||||
}
|
||||
|
||||
key = nullptr;
|
||||
if (RegOpenKeyExA(
|
||||
HKEY_LOCAL_MACHINE, registry_path.data(), 0,
|
||||
KEY_ALL_ACCESS, &key) != ERROR_SUCCESS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DWORD value = 1;
|
||||
RegSetValueExA(key, "MaxLoaderThreads", 0, REG_DWORD, reinterpret_cast<const BYTE*>(&value), sizeof(value));
|
||||
|
||||
RegCloseKey(key);
|
||||
}
|
||||
|
||||
void apply_environment()
|
||||
{
|
||||
char* buffer{};
|
||||
std::size_t size{};
|
||||
if (_dupenv_s(&buffer, &size, "AW_INSTALL") != 0 || buffer == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto _ = gsl::finally([&]
|
||||
{
|
||||
std::free(buffer);
|
||||
});
|
||||
|
||||
SetCurrentDirectoryA(buffer);
|
||||
SetDllDirectoryA(buffer);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
AddVectoredExceptionHandler(0, exception_handler);
|
||||
SetProcessDEPPolicy(PROCESS_DEP_ENABLE);
|
||||
|
||||
FARPROC entry_point;
|
||||
enable_dpi_awareness();
|
||||
|
||||
// This requires admin privilege, but I suppose many
|
||||
// people will start with admin rights if it crashes.
|
||||
limit_parallel_dll_loading();
|
||||
|
||||
std::srand(static_cast<std::uint32_t>(std::time(nullptr)) ^ ~(GetTickCount() * GetCurrentProcessId()));
|
||||
|
||||
{
|
||||
auto premature_shutdown = true;
|
||||
const auto _ = gsl::finally([&premature_shutdown]
|
||||
{
|
||||
if (premature_shutdown)
|
||||
{
|
||||
component_loader::pre_destroy();
|
||||
}
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
apply_environment();
|
||||
remove_crash_file();
|
||||
|
||||
if (!component_loader::post_start()) return 0;
|
||||
|
||||
auto mode = detect_mode_from_arguments();
|
||||
if (mode == launcher::mode::none)
|
||||
{
|
||||
const launcher launcher;
|
||||
mode = launcher.run();
|
||||
if (mode == launcher::mode::none) return 0;
|
||||
}
|
||||
|
||||
game::environment::set_mode(mode);
|
||||
|
||||
entry_point = load_binary(mode);
|
||||
if (!entry_point)
|
||||
{
|
||||
throw std::runtime_error("Unable to load binary into memory");
|
||||
}
|
||||
|
||||
if (!component_loader::post_load()) return 0;
|
||||
|
||||
premature_shutdown = false;
|
||||
}
|
||||
catch (const std::exception& ex)
|
||||
{
|
||||
game::show_error(ex.what());
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return static_cast<int>(entry_point());
|
||||
}
|
||||
|
||||
int WINAPI WinMain(HINSTANCE, HINSTANCE, PSTR, int)
|
||||
{
|
||||
return main();
|
||||
}
|
Reference in New Issue
Block a user