#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); HRESULT WINAPI InitializeImpl(LPVOID lpParam, HANDLE hMainThreadContinue) { g_startInfo.from_envvars(); std::string jsonParseError; try { from_json(nlohmann::json::parse(std::string_view(static_cast(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(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(logFilePath.size()), &logFilePath[0])); if (logFilePath.empty()) { logFilePath.resize(PATHCCH_MAX_CCH + 1); logFilePath.resize(GetCurrentDirectoryW(static_cast(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(mhStatus)); } logging::I("Dalamud.Boot Injectable, (c) 2021 XIVLauncher Contributors"); logging::I("Built at: " __DATE__ "@" __TIME__); if ((g_startInfo.BootWaitMessageBox & DalamudStartInfo::WaitMessageboxFlags::BeforeInitialize) != DalamudStartInfo::WaitMessageboxFlags::None) 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; const auto result = InitializeClrAndGetEntryPoint( g_hModule, g_startInfo.BootEnableEtw, !g_startInfo.BootDisableLegacyCorruptedStateExceptions, runtimeconfig_path, module_path, L"Dalamud.EntryPoint, Dalamud", L"Initialize", L"Dalamud.EntryPoint+InitDelegate, Dalamud", &entrypoint_vfn); if (FAILED(result)) return result; using custom_component_entry_point_fn = void (CORECLR_DELEGATE_CALLTYPE*)(LPVOID, HANDLE); const auto entrypoint_fn = reinterpret_cast(entrypoint_vfn); // ============================== VEH ======================================== // logging::I("Initializing VEH..."); if (g_startInfo.UnhandledException == DalamudStartInfo::UnhandledExceptionHandlingMode::None) { logging::W("=> Exception handlers are disabled from DalamudStartInfo."); } else if (g_startInfo.BootVehEnabled) { if (veh::add_handler(g_startInfo.BootVehFull, g_startInfo.WorkingDirectory)) logging::I("=> Done!"); else logging::I("=> Failed!"); } else { logging::I("VEH was disabled manually"); } // ============================== Dalamud ==================================== // if (static_cast(g_startInfo.BootWaitMessageBox) & static_cast(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 S_OK; } extern "C" 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(mhStatus)); __fastfail(logging::MinHookUnload); } veh::remove_handler(); //logging::log_file.close(); break; } return TRUE; }