Dalamud/Dalamud.Boot/dllmain.cpp

197 lines
7.2 KiB
C++

#include "pch.h"
#include "DalamudStartInfo.h"
#include "logging.h"
#include "utils.h"
#include "veh.h"
#include "xivfixes.h"
HMODULE g_hModule;
HINSTANCE g_hGameInstance = GetModuleHandleW(nullptr);
DWORD WINAPI InitializeImpl(LPVOID lpParam, HANDLE hMainThreadContinue) {
g_startInfo.from_envvars();
std::string jsonParseError;
try {
from_json(nlohmann::json::parse(std::string_view(static_cast<char*>(lpParam))), g_startInfo);
} catch (const std::exception& e) {
jsonParseError = e.what();
}
if (g_startInfo.BootShowConsole)
ConsoleSetup(L"Dalamud Boot");
logging::update_dll_load_status(true);
const auto logFilePath = unicode::convert<std::wstring>(g_startInfo.BootLogPath);
auto attemptFallbackLog = false;
if (logFilePath.empty()) {
attemptFallbackLog = true;
logging::I("No log file path given; not logging to file.");
} else {
try {
logging::start_file_logging(logFilePath, !g_startInfo.BootShowConsole);
logging::I("Logging to file: {}", logFilePath);
} catch (const std::exception& e) {
attemptFallbackLog = true;
logging::E("Couldn't open log file: {}", logFilePath);
logging::E("Error: {} / {}", errno, e.what());
}
}
if (!jsonParseError.empty())
logging::E("Couldn't parse input JSON: {}", jsonParseError);
if (attemptFallbackLog) {
std::wstring logFilePath(PATHCCH_MAX_CCH + 1, L'\0');
logFilePath.resize(GetTempPathW(static_cast<DWORD>(logFilePath.size()), &logFilePath[0]));
if (logFilePath.empty()) {
logFilePath.resize(PATHCCH_MAX_CCH + 1);
logFilePath.resize(GetCurrentDirectoryW(static_cast<DWORD>(logFilePath.size()), &logFilePath[0]));
}
if (!logFilePath.empty() && logFilePath.back() != '/' && logFilePath.back() != '\\')
logFilePath += L"\\";
SYSTEMTIME st;
GetLocalTime(&st);
logFilePath += std::format(L"Dalamud.Boot.{:04}{:02}{:02}.{:02}{:02}{:02}.{:03}.{}.log", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds, GetCurrentProcessId());
try {
logging::start_file_logging(logFilePath, !g_startInfo.BootShowConsole);
logging::I("Logging to fallback log file: {}", logFilePath);
} catch (const std::exception& e) {
if (!g_startInfo.BootShowConsole && !g_startInfo.BootDisableFallbackConsole)
ConsoleSetup(L"Dalamud Boot - Fallback Console");
logging::E("Couldn't open fallback log file: {}", logFilePath);
logging::E("Error: {} / {}", errno, e.what());
}
}
auto minHookLoaded = false;
if (const auto mhStatus = MH_Initialize(); mhStatus == MH_OK) {
logging::I("MinHook initialized.");
minHookLoaded = true;
} else if (mhStatus == MH_ERROR_ALREADY_INITIALIZED) {
logging::I("MinHook already initialized.");
minHookLoaded = true;
} else {
logging::E("Failed to initialize MinHook (status={}({}))", MH_StatusToString(mhStatus), static_cast<int>(mhStatus));
}
logging::I("Dalamud.Boot Injectable, (c) 2021 XIVLauncher Contributors");
logging::I("Built at: " __DATE__ "@" __TIME__);
if (static_cast<int>(g_startInfo.BootWaitMessageBox) & static_cast<int>(DalamudStartInfo::WaitMessageboxFlags::BeforeInitialize))
MessageBoxW(nullptr, L"Press OK to continue (BeforeInitialize)", L"Dalamud Boot", MB_OK);
if (minHookLoaded) {
logging::I("Applying fixes...");
xivfixes::apply_all(true);
logging::I("Fixes OK");
} else {
logging::W("Skipping fixes, as MinHook has failed to load.");
}
if (g_startInfo.BootWaitDebugger) {
logging::I("Waiting for debugger to attach...");
while (!IsDebuggerPresent())
Sleep(100);
logging::I("Debugger attached.");
}
const auto fs_module_path = utils::get_module_path(g_hModule);
const auto runtimeconfig_path = std::filesystem::path(fs_module_path).replace_filename(L"Dalamud.runtimeconfig.json").wstring();
const auto module_path = std::filesystem::path(fs_module_path).replace_filename(L"Dalamud.dll").wstring();
// ============================== CLR ========================================= //
logging::I("Calling InitializeClrAndGetEntryPoint");
void* entrypoint_vfn;
int result = InitializeClrAndGetEntryPoint(
g_hModule,
g_startInfo.BootEnableEtw,
runtimeconfig_path,
module_path,
L"Dalamud.EntryPoint, Dalamud",
L"Initialize",
L"Dalamud.EntryPoint+InitDelegate, Dalamud",
&entrypoint_vfn);
if (result != 0)
return result;
using custom_component_entry_point_fn = void (CORECLR_DELEGATE_CALLTYPE*)(LPVOID, HANDLE);
const auto entrypoint_fn = reinterpret_cast<custom_component_entry_point_fn>(entrypoint_vfn);
// ============================== VEH ======================================== //
logging::I("Initializing VEH...");
if (utils::is_running_on_linux()) {
logging::I("=> VEH was disabled, running on linux");
} else if (g_startInfo.BootVehEnabled) {
if (veh::add_handler(g_startInfo.BootVehFull))
logging::I("=> Done!");
else
logging::I("=> Failed!");
} else {
logging::I("VEH was disabled manually");
}
// ============================== Dalamud ==================================== //
if (static_cast<int>(g_startInfo.BootWaitMessageBox) & static_cast<int>(DalamudStartInfo::WaitMessageboxFlags::BeforeDalamudEntrypoint))
MessageBoxW(nullptr, L"Press OK to continue (BeforeDalamudEntrypoint)", L"Dalamud Boot", MB_OK);
// We don't need to do this anymore, Dalamud now loads without needing the window to be there. Speed!
// utils::wait_for_game_window();
logging::I("Initializing Dalamud...");
entrypoint_fn(lpParam, hMainThreadContinue);
logging::I("Done!");
return 0;
}
DllExport DWORD WINAPI Initialize(LPVOID lpParam) {
return InitializeImpl(lpParam, CreateEvent(nullptr, TRUE, FALSE, nullptr));
}
BOOL APIENTRY DllMain(const HMODULE hModule, const DWORD dwReason, LPVOID lpReserved) {
DisableThreadLibraryCalls(hModule);
switch (dwReason) {
case DLL_PROCESS_ATTACH:
g_hModule = hModule;
break;
case DLL_PROCESS_DETACH:
// do not show debug message boxes on abort() here
_set_abort_behavior(0, _WRITE_ABORT_MSG);
// process is terminating; don't bother cleaning up
if (lpReserved)
return TRUE;
logging::update_dll_load_status(false);
xivfixes::apply_all(false);
MH_DisableHook(MH_ALL_HOOKS);
if (const auto mhStatus = MH_Uninitialize(); MH_OK != mhStatus && MH_ERROR_NOT_INITIALIZED != mhStatus) {
logging::E("Failed to uninitialize MinHook (status={})", static_cast<int>(mhStatus));
__fastfail(logging::MinHookUnload);
}
veh::remove_handler();
//logging::log_file.close();
break;
}
return TRUE;
}