diff --git a/Dalamud.Boot/Dalamud.Boot.rc b/Dalamud.Boot/Dalamud.Boot.rc
index b46e81caf..655df27e1 100644
--- a/Dalamud.Boot/Dalamud.Boot.rc
+++ b/Dalamud.Boot/Dalamud.Boot.rc
@@ -26,6 +26,38 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
RT_MANIFEST_THEMES RT_MANIFEST "themes.manifest"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE
+BEGIN
+ IDS_APPNAME "Dalamud Boot"
+ IDS_MSVCRT_ACTION_OPENDOWNLOAD
+ "Download Microsoft Visual C++ Redistributable 2022\nExit the game and download the latest setup file from Microsoft."
+ IDS_MSVCRT_ACTION_IGNORE
+ "Ignore and Continue\nAttempt to continue with the currently installed version.\nDalamud or plugins may fail to load."
+ IDS_MSVCRT_DIALOG_MAININSTRUCTION
+ "Outdated Microsoft Visual C++ Redistributable"
+ IDS_MSVCRT_DIALOG_CONTENT
+ "The Microsoft Visual C++ Redistributable version detected on this computer (v{0}.{1}.{2}.{3}) is out of date and may not work with Dalamud."
+ IDS_MSVCRT_DOWNLOADURL "https://aka.ms/vs/17/release/vc_redist.x64.exe"
+ IDS_INITIALIZEFAIL_ACTION_ABORT "Abort\nExit the game."
+ IDS_INITIALIZEFAIL_ACTION_CONTINUE
+ "Load game without Dalamud\nThe game will launch without Dalamud enabled."
+ IDS_INITIALIZEFAIL_DIALOG_MAININSTRUCTION "Failed to load Dalamud."
+ IDS_INITIALIZEFAIL_DIALOG_CONTENT
+ "An error is preventing Dalamud from being loaded along with the game."
+END
+
+STRINGTABLE
+BEGIN
+ IDS_INITIALIZEFAIL_DIALOG_FOOTER
+ "Last operation: {0}\nHRESULT: 0x{1:08X}\nDescription: {2}"
+END
+
#endif // English (United States) resources
/////////////////////////////////////////////////////////////////////////////
diff --git a/Dalamud.Boot/Dalamud.Boot.vcxproj b/Dalamud.Boot/Dalamud.Boot.vcxproj
index a15601af4..0a4a9c563 100644
--- a/Dalamud.Boot/Dalamud.Boot.vcxproj
+++ b/Dalamud.Boot/Dalamud.Boot.vcxproj
@@ -48,7 +48,7 @@
Level3
true
true
- stdcpp20
+ stdcpp23
pch.h
ProgramDatabase
CPPDLLTEMPLATE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
@@ -65,7 +65,7 @@
true
false
- MultiThreadedDebugDLL
+ MultiThreadedDebugDLL
_DEBUG;%(PreprocessorDefinitions)
Use
26812
@@ -80,7 +80,7 @@
true
true
- MultiThreadedDLL
+ MultiThreadedDLL
NDEBUG;%(PreprocessorDefinitions)
Use
26812
@@ -133,6 +133,10 @@
NotUsing
+
+ NotUsing
+ NotUsing
+
NotUsing
@@ -176,6 +180,7 @@
+
@@ -206,4 +211,4 @@
-
+
\ No newline at end of file
diff --git a/Dalamud.Boot/Dalamud.Boot.vcxproj.filters b/Dalamud.Boot/Dalamud.Boot.vcxproj.filters
index 7c26b28ff..15e3eb8b3 100644
--- a/Dalamud.Boot/Dalamud.Boot.vcxproj.filters
+++ b/Dalamud.Boot/Dalamud.Boot.vcxproj.filters
@@ -76,6 +76,9 @@
Dalamud.Boot DLL
+
+ Common Boot
+
@@ -146,6 +149,9 @@
Dalamud.Boot DLL
+
+ Common Boot
+
diff --git a/Dalamud.Boot/dllmain.cpp b/Dalamud.Boot/dllmain.cpp
index eee3953ad..80a16f89a 100644
--- a/Dalamud.Boot/dllmain.cpp
+++ b/Dalamud.Boot/dllmain.cpp
@@ -9,11 +9,12 @@
#include "utils.h"
#include "veh.h"
#include "xivfixes.h"
+#include "resource.h"
HMODULE g_hModule;
HINSTANCE g_hGameInstance = GetModuleHandleW(nullptr);
-void CheckMsvcrtVersion() {
+static void CheckMsvcrtVersion() {
// Commit introducing inline mutex ctor: tagged vs-2022-17.14 (2024-06-18)
// - https://github.com/microsoft/STL/commit/22a88260db4d754bbc067e2002430144d6ec5391
// MSVC Redist versions:
@@ -28,67 +29,102 @@ void CheckMsvcrtVersion() {
| (static_cast(RequiredMsvcrtVersionComponents[2]) << 16)
| (static_cast(RequiredMsvcrtVersionComponents[3]) << 0);
-#ifdef _DEBUG
constexpr const wchar_t* RuntimeDllNames[] = {
+#ifdef _DEBUG
L"msvcp140d.dll",
L"vcruntime140d.dll",
L"vcruntime140_1d.dll",
- };
#else
- constexpr const wchar_t* RuntimeDllNames[] = {
L"msvcp140.dll",
L"vcruntime140.dll",
L"vcruntime140_1.dll",
- };
#endif
+ };
uint64_t lowestVersion = 0;
for (const auto& runtimeDllName : RuntimeDllNames) {
const utils::loaded_module mod(GetModuleHandleW(runtimeDllName));
if (!mod) {
- logging::E("Runtime DLL not found: {}", runtimeDllName);
+ logging::E("MSVCRT DLL not found: {}", runtimeDllName);
continue;
}
- try {
- const auto& versionFull = mod.get_file_version();
- logging::I("Runtime DLL {} has version {}.", runtimeDllName, utils::format_file_version(versionFull));
+ const auto path = mod.path()
+ .transform([](const auto& p) { return p.wstring(); })
+ .value_or(runtimeDllName);
- const auto version = (static_cast(versionFull.dwFileVersionMS) << 32) |
- static_cast(versionFull.dwFileVersionLS);
+ if (const auto versionResult = mod.get_file_version()) {
+ const auto& versionFull = versionResult->get();
+ logging::I("MSVCRT DLL {} has version {}.", path, utils::format_file_version(versionFull));
+
+ const auto version = 0ULL |
+ (static_cast(versionFull.dwFileVersionMS) << 32) |
+ (static_cast(versionFull.dwFileVersionLS) << 0);
if (version < RequiredMsvcrtVersion && (lowestVersion == 0 || lowestVersion > version))
lowestVersion = version;
- } catch (const std::exception& e) {
- logging::E("Failed to detect Runtime DLL version for {}: {}", runtimeDllName, e.what());
+ } else {
+ logging::E("Failed to detect MSVCRT DLL version for {}: {}", path, versionResult.error().describe());
}
}
- if (lowestVersion) {
- switch (MessageBoxW(
- nullptr,
- L"Microsoft Visual C++ Redistributable should be updated, or Dalamud may not work as expected."
- L" Do you want to download and install the latest version from Microsoft?"
- L"\n"
- L"\n* Clicking \"Yes\" will exit the game and open the download page from Microsoft."
- L"\n* Clicking \"No\" will continue loading the game with Dalamud. This may fail."
- L"\n"
- L"\nClick \"X64\" from the table in the download page, regardless of what CPU you have.",
- L"Dalamud",
- MB_YESNO | MB_ICONWARNING)) {
- case IDYES:
- ShellExecuteW(
- nullptr,
- L"open",
- L"https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-170#latest-microsoft-visual-c-redistributable-version",
- nullptr,
- nullptr,
- SW_SHOW);
- ExitProcess(0);
- break;
- case IDNO:
- break;
- }
+ if (!lowestVersion)
+ return;
+
+ enum IdTaskDialogAction {
+ IdTaskDialogActionOpenDownload = 101,
+ IdTaskDialogActionIgnore,
+ };
+
+ const TASKDIALOG_BUTTON buttons[]{
+ {IdTaskDialogActionOpenDownload, MAKEINTRESOURCEW(IDS_MSVCRT_ACTION_OPENDOWNLOAD)},
+ {IdTaskDialogActionIgnore, MAKEINTRESOURCEW(IDS_MSVCRT_ACTION_IGNORE)},
+ };
+
+ const WORD lowestVersionComponents[]{
+ static_cast(lowestVersion >> 48),
+ static_cast(lowestVersion >> 32),
+ static_cast(lowestVersion >> 16),
+ static_cast(lowestVersion >> 0),
+ };
+
+ const auto dialogContent = std::vformat(
+ utils::get_string_resource(IDS_MSVCRT_DIALOG_CONTENT),
+ std::make_wformat_args(
+ lowestVersionComponents[0],
+ lowestVersionComponents[1],
+ lowestVersionComponents[2],
+ lowestVersionComponents[3]));
+
+ const TASKDIALOGCONFIG config{
+ .cbSize = sizeof config,
+ .hInstance = g_hModule,
+ .dwFlags = TDF_CAN_BE_MINIMIZED | TDF_ALLOW_DIALOG_CANCELLATION | TDF_USE_COMMAND_LINKS,
+ .pszWindowTitle = MAKEINTRESOURCEW(IDS_APPNAME),
+ .pszMainIcon = MAKEINTRESOURCEW(IDI_ICON1),
+ .pszMainInstruction = MAKEINTRESOURCEW(IDS_MSVCRT_DIALOG_MAININSTRUCTION),
+ .pszContent = dialogContent.c_str(),
+ .cButtons = _countof(buttons),
+ .pButtons = buttons,
+ .nDefaultButton = IdTaskDialogActionOpenDownload,
+ };
+
+ int buttonPressed;
+ if (utils::scoped_dpi_awareness_context ctx;
+ FAILED(TaskDialogIndirect(&config, &buttonPressed, nullptr, nullptr)))
+ buttonPressed = IdTaskDialogActionOpenDownload;
+
+ switch (buttonPressed) {
+ case IdTaskDialogActionOpenDownload:
+ ShellExecuteW(
+ nullptr,
+ L"open",
+ utils::get_string_resource(IDS_MSVCRT_DOWNLOADURL).c_str(),
+ nullptr,
+ nullptr,
+ SW_SHOW);
+ ExitProcess(0);
+ break;
}
}
@@ -103,7 +139,7 @@ HRESULT WINAPI InitializeImpl(LPVOID lpParam, HANDLE hMainThreadContinue) {
}
if (g_startInfo.BootShowConsole)
- ConsoleSetup(L"Dalamud Boot");
+ ConsoleSetup(utils::get_string_resource(IDS_APPNAME).c_str());
logging::update_dll_load_status(true);
@@ -240,7 +276,7 @@ HRESULT WINAPI InitializeImpl(LPVOID lpParam, HANDLE hMainThreadContinue) {
if (minHookLoaded) {
logging::I("Applying fixes...");
- xivfixes::apply_all(true);
+ std::thread([] { xivfixes::apply_all(true); }).join();
logging::I("Fixes OK");
} else {
logging::W("Skipping fixes, as MinHook has failed to load.");
@@ -251,11 +287,14 @@ HRESULT WINAPI InitializeImpl(LPVOID lpParam, HANDLE hMainThreadContinue) {
while (!IsDebuggerPresent())
Sleep(100);
logging::I("Debugger attached.");
+ __debugbreak();
}
- 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();
+ const auto fs_module_path = utils::loaded_module(g_hModule).path();
+ if (!fs_module_path)
+ return fs_module_path.error();
+ 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 ========================================= //
diff --git a/Dalamud.Boot/error_info.cpp b/Dalamud.Boot/error_info.cpp
new file mode 100644
index 000000000..02356b730
--- /dev/null
+++ b/Dalamud.Boot/error_info.cpp
@@ -0,0 +1,26 @@
+#include "error_info.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include
+
+DalamudBootError::DalamudBootError(DalamudBootErrorDescription dalamudErrorDescription, long hresult) noexcept
+ : m_dalamudErrorDescription(dalamudErrorDescription)
+ , m_hresult(hresult) {
+}
+
+DalamudBootError::DalamudBootError(DalamudBootErrorDescription dalamudErrorDescription) noexcept
+ : DalamudBootError(dalamudErrorDescription, E_FAIL) {
+}
+
+const char* DalamudBootError::describe() const {
+ switch (m_dalamudErrorDescription) {
+ case DalamudBootErrorDescription::ModuleResourceLoadFail:
+ return "Failed to load resource.";
+ case DalamudBootErrorDescription::ModuleResourceVersionReadFail:
+ return "Failed to query version information.";
+ case DalamudBootErrorDescription::ModuleResourceVersionSignatureFail:
+ return "Invalid version info found.";
+ default:
+ return "(unavailable)";
+ }
+}
diff --git a/Dalamud.Boot/error_info.h b/Dalamud.Boot/error_info.h
new file mode 100644
index 000000000..b5862d0dd
--- /dev/null
+++ b/Dalamud.Boot/error_info.h
@@ -0,0 +1,42 @@
+#pragma once
+
+#include
+#include
+
+typedef unsigned long DWORD;
+typedef _Return_type_success_(return >= 0) long HRESULT;
+
+enum class DalamudBootErrorDescription {
+ None,
+ ModulePathResolutionFail,
+ ModuleResourceLoadFail,
+ ModuleResourceVersionReadFail,
+ ModuleResourceVersionSignatureFail,
+};
+
+class DalamudBootError {
+ DalamudBootErrorDescription m_dalamudErrorDescription;
+ long m_hresult;
+
+public:
+ DalamudBootError(DalamudBootErrorDescription dalamudErrorDescription, long hresult) noexcept;
+ DalamudBootError(DalamudBootErrorDescription dalamudErrorDescription) noexcept;
+
+ const char* describe() const;
+
+ operator HRESULT() const {
+ return m_hresult;
+ }
+};
+
+template
+using DalamudExpected = std::expected<
+ std::conditional_t<
+ std::is_reference_v,
+ std::reference_wrapper>,
+ T
+ >,
+ DalamudBootError
+>;
+
+using DalamudUnexpected = std::unexpected;
diff --git a/Dalamud.Boot/hooks.cpp b/Dalamud.Boot/hooks.cpp
index bb11572a1..3443a5f8a 100644
--- a/Dalamud.Boot/hooks.cpp
+++ b/Dalamud.Boot/hooks.cpp
@@ -82,37 +82,14 @@ void hooks::getprocaddress_singleton_import_hook::initialize() {
s_dllChanged = 1;
if (notiReason == LDR_DLL_NOTIFICATION_REASON_LOADED) {
const auto dllName = unicode::convert(pData->Loaded.FullDllName->Buffer);
- std::wstring version = L"";
- std::wstring description = L"";
- DWORD versionSize = GetFileVersionInfoSizeA(dllName.c_str(), NULL);
- if (versionSize > 0) {
- std::vector versionData(versionSize);
- if (GetFileVersionInfoA(dllName.c_str(), 0, versionSize, versionData.data())) {
- struct LANGANDCODEPAGE {
- WORD wLanguage;
- WORD wCodePage;
- } *translate = nullptr;
+ utils::loaded_module mod(pData->Loaded.DllBase);
+ const auto version = mod.get_file_version()
+ .transform([](const auto& v) { return utils::format_file_version(v.get()); })
+ .value_or(L"");
- UINT uLen = 0;
- LPVOID lpBuffer;
- if (VerQueryValueW(versionData.data(), L"\\VarFileInfo\\Translation", (LPVOID*)&translate, &uLen) && uLen >= sizeof(LANGANDCODEPAGE)) {
- // Use the first language/codepage
- wchar_t subBlock[256];
- swprintf(subBlock, 256, L"\\StringFileInfo\\%04x%04x\\FileDescription", translate[0].wLanguage, translate[0].wCodePage);
-
- if (VerQueryValueW(versionData.data(), subBlock, &lpBuffer, &uLen)) {
- description = std::wstring((wchar_t *)lpBuffer, uLen - 1);
- }
-
- swprintf(subBlock, 256, L"\\StringFileInfo\\%04x%04x\\FileVersion", translate[0].wLanguage, translate[0].wCodePage);
-
- if (VerQueryValueW(versionData.data(), subBlock, &lpBuffer, &uLen)) {
- version = std::wstring((wchar_t*)lpBuffer, uLen - 1);
- }
- }
- }
- }
+ const auto description = mod.get_description()
+ .value_or(L"");
logging::I(R"({} "{}" ("{}" ver {}) has been loaded at 0x{:X} ~ 0x{:X} (0x{:X}); finding import table items to hook.)",
LogTag, dllName, description, version,
@@ -142,7 +119,9 @@ void hooks::getprocaddress_singleton_import_hook::hook_module(const utils::loade
if (mod.is_current_process())
return;
- const auto path = unicode::convert(mod.path().wstring());
+ const auto path = mod.path()
+ .transform([](const auto& p) { return unicode::convert(p.wstring()); })
+ .value_or("");
for (const auto& [hModule, targetFns] : m_targetFns) {
for (const auto& [targetFn, pfnThunk] : targetFns) {
@@ -150,7 +129,7 @@ void hooks::getprocaddress_singleton_import_hook::hook_module(const utils::loade
if (void* pGetProcAddressImport; mod.find_imported_function_pointer(dllName.c_str(), targetFn.c_str(), 0, pGetProcAddressImport)) {
auto& hook = m_hooks[hModule][targetFn][mod];
if (!hook) {
- logging::I("{} Hooking {}!{} imported by {}", LogTag, dllName, targetFn, unicode::convert(mod.path().wstring()));
+ logging::I("{} Hooking {}!{} imported by {}", LogTag, dllName, targetFn, path);
hook.emplace(std::format("getprocaddress_singleton_import_hook::hook_module({}!{})", dllName, targetFn), static_cast(pGetProcAddressImport), pfnThunk);
}
diff --git a/Dalamud.Boot/pch.h b/Dalamud.Boot/pch.h
index e46927f76..ac1394e57 100644
--- a/Dalamud.Boot/pch.h
+++ b/Dalamud.Boot/pch.h
@@ -55,6 +55,7 @@
#include
#include
#include
+#include
#include
// https://www.akenotsuki.com/misc/srell/en/
diff --git a/Dalamud.Boot/resource.h b/Dalamud.Boot/resource.h
index 51acf37df..2a1cde6e2 100644
--- a/Dalamud.Boot/resource.h
+++ b/Dalamud.Boot/resource.h
@@ -3,12 +3,23 @@
// Used by Dalamud.Boot.rc
//
#define IDI_ICON1 101
+#define IDS_APPNAME 102
+#define IDS_MSVCRT_ACTION_OPENDOWNLOAD 103
+#define IDS_MSVCRT_ACTION_IGNORE 104
+#define IDS_MSVCRT_DIALOG_MAININSTRUCTION 105
+#define IDS_MSVCRT_DIALOG_CONTENT 106
+#define IDS_MSVCRT_DOWNLOADURL 107
+#define IDS_INITIALIZEFAIL_ACTION_ABORT 108
+#define IDS_INITIALIZEFAIL_ACTION_CONTINUE 109
+#define IDS_INITIALIZEFAIL_DIALOG_MAININSTRUCTION 110
+#define IDS_INITIALIZEFAIL_DIALOG_CONTENT 111
+#define IDS_INITIALIZEFAIL_DIALOG_FOOTER 112
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
-#define _APS_NEXT_RESOURCE_VALUE 102
+#define _APS_NEXT_RESOURCE_VALUE 103
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
diff --git a/Dalamud.Boot/rewrite_entrypoint.cpp b/Dalamud.Boot/rewrite_entrypoint.cpp
index 3a1672af7..69a4ec818 100644
--- a/Dalamud.Boot/rewrite_entrypoint.cpp
+++ b/Dalamud.Boot/rewrite_entrypoint.cpp
@@ -2,6 +2,7 @@
#include "logging.h"
#include "utils.h"
+#include "resource.h"
HRESULT WINAPI InitializeImpl(LPVOID lpParam, HANDLE hMainThreadContinue);
@@ -379,12 +380,50 @@ extern "C" void WINAPI RewrittenEntryPoint_AdjustedStack(RewrittenEntryPointPara
auto desc = err.Description();
if (desc.length() == 0)
desc = err.ErrorMessage();
- if (MessageBoxW(nullptr, std::format(
- L"Failed to load Dalamud. Load game without Dalamud(yes) or abort(no)?\n\n{}\n{}",
- last_operation,
- desc.GetBSTR()).c_str(),
- L"Dalamud.Boot", MB_OK | MB_YESNO) == IDNO)
- ExitProcess(-1);
+
+ enum IdTaskDialogAction {
+ IdTaskDialogActionAbort = 101,
+ IdTaskDialogActionContinue,
+ };
+
+ const TASKDIALOG_BUTTON buttons[]{
+ {IdTaskDialogActionAbort, MAKEINTRESOURCEW(IDS_INITIALIZEFAIL_ACTION_ABORT)},
+ {IdTaskDialogActionContinue, MAKEINTRESOURCEW(IDS_INITIALIZEFAIL_ACTION_CONTINUE)},
+ };
+
+ const auto hru32 = static_cast(hr);
+ const auto footer = std::vformat(
+ utils::get_string_resource(IDS_INITIALIZEFAIL_DIALOG_FOOTER),
+ std::make_wformat_args(
+ last_operation,
+ hru32,
+ desc.GetBSTR()));
+
+ const TASKDIALOGCONFIG config{
+ .cbSize = sizeof config,
+ .hInstance = g_hModule,
+ .dwFlags = TDF_CAN_BE_MINIMIZED | TDF_ALLOW_DIALOG_CANCELLATION | TDF_USE_COMMAND_LINKS | TDF_EXPAND_FOOTER_AREA,
+ .pszWindowTitle = MAKEINTRESOURCEW(IDS_APPNAME),
+ .pszMainIcon = MAKEINTRESOURCEW(IDI_ICON1),
+ .pszMainInstruction = MAKEINTRESOURCEW(IDS_INITIALIZEFAIL_DIALOG_MAININSTRUCTION),
+ .pszContent = MAKEINTRESOURCEW(IDS_INITIALIZEFAIL_DIALOG_CONTENT),
+ .cButtons = _countof(buttons),
+ .pButtons = buttons,
+ .nDefaultButton = IdTaskDialogActionAbort,
+ .pszFooter = footer.c_str(),
+ };
+
+ int buttonPressed;
+ if (utils::scoped_dpi_awareness_context ctx;
+ FAILED(TaskDialogIndirect(&config, &buttonPressed, nullptr, nullptr)))
+ buttonPressed = IdTaskDialogActionAbort;
+
+ switch (buttonPressed) {
+ case IdTaskDialogActionAbort:
+ ExitProcess(-1);
+ break;
+ }
+
if (hMainThreadContinue) {
CloseHandle(hMainThreadContinue);
hMainThreadContinue = nullptr;
diff --git a/Dalamud.Boot/utils.cpp b/Dalamud.Boot/utils.cpp
index 91be2fb31..9820e5b7f 100644
--- a/Dalamud.Boot/utils.cpp
+++ b/Dalamud.Boot/utils.cpp
@@ -3,22 +3,27 @@
#include "utils.h"
-std::filesystem::path utils::loaded_module::path() const {
- std::wstring buf(MAX_PATH, L'\0');
- for (;;) {
- if (const auto len = GetModuleFileNameExW(GetCurrentProcess(), m_hModule, &buf[0], static_cast(buf.size())); len != buf.size()) {
- if (buf.empty())
- throw std::runtime_error(std::format("Failed to resolve module path: Win32 error {}", GetLastError()));
+DalamudExpected utils::loaded_module::path() const {
+ for (std::wstring buf(MAX_PATH, L'\0');; buf.resize(buf.size() * 2)) {
+ if (const auto len = GetModuleFileNameW(m_hModule, &buf[0], static_cast(buf.size()));
+ len != buf.size()) {
+ if (!len) {
+ return DalamudUnexpected(
+ std::in_place,
+ DalamudBootErrorDescription::ModulePathResolutionFail,
+ HRESULT_FROM_WIN32(GetLastError()));
+ }
+
buf.resize(len);
return buf;
}
- if (buf.size() * 2 < PATHCCH_MAX_CCH)
- buf.resize(buf.size() * 2);
- else if (auto p = std::filesystem::path(buf); exists(p))
- return p;
- else
- throw std::runtime_error("Failed to resolve module path: no amount of buffer size would fit the data");
+ if (buf.size() > PATHCCH_MAX_CCH) {
+ return DalamudUnexpected(
+ std::in_place,
+ DalamudBootErrorDescription::ModulePathResolutionFail,
+ E_OUTOFMEMORY);
+ }
}
}
@@ -144,21 +149,24 @@ void* utils::loaded_module::get_imported_function_pointer(const char* pcszDllNam
throw std::runtime_error(std::format("Failed to find import for {}!{} ({}).", pcszDllName, pcszFunctionName ? pcszFunctionName : "", hintOrOrdinal));
}
-std::unique_ptr, decltype(&FreeResource)> utils::loaded_module::get_resource(LPCWSTR lpName, LPCWSTR lpType) const {
+DalamudExpected, decltype(&FreeResource)>> utils::loaded_module::get_resource(LPCWSTR lpName, LPCWSTR lpType) const {
const auto hres = FindResourceW(m_hModule, lpName, lpType);
if (!hres)
- throw std::runtime_error("No such resource");
+ return DalamudUnexpected(std::in_place, DalamudBootErrorDescription::ModuleResourceLoadFail, GetLastError());
const auto hRes = LoadResource(m_hModule, hres);
if (!hRes)
- throw std::runtime_error("LoadResource failure");
+ return DalamudUnexpected(std::in_place, DalamudBootErrorDescription::ModuleResourceLoadFail, GetLastError());
- return {hRes, &FreeResource};
+ return std::unique_ptr, decltype(&FreeResource)>(hRes, &FreeResource);
}
-std::wstring utils::loaded_module::get_description() const {
- const auto rsrc = get_resource(MAKEINTRESOURCE(VS_VERSION_INFO), RT_VERSION);
- const auto pBlock = LockResource(rsrc.get());
+DalamudExpected utils::loaded_module::get_description() const {
+ auto rsrc = get_resource(MAKEINTRESOURCE(VS_VERSION_INFO), RT_VERSION);
+ if (!rsrc)
+ return DalamudUnexpected(std::move(rsrc.error()));
+
+ const auto pBlock = LockResource(rsrc->get());
struct LANGANDCODEPAGE {
WORD wLanguage;
@@ -166,44 +174,65 @@ std::wstring utils::loaded_module::get_description() const {
} * lpTranslate;
UINT cbTranslate;
if (!VerQueryValueW(pBlock,
- TEXT("\\VarFileInfo\\Translation"),
+ L"\\VarFileInfo\\Translation",
reinterpret_cast(&lpTranslate),
&cbTranslate)) {
- throw std::runtime_error("Invalid version information (1)");
+ return DalamudUnexpected(
+ std::in_place,
+ DalamudBootErrorDescription::ModuleResourceVersionReadFail,
+ HRESULT_FROM_WIN32(GetLastError()));
}
for (size_t i = 0; i < (cbTranslate / sizeof(LANGANDCODEPAGE)); i++) {
+ wchar_t subblockNameBuf[64];
+ *std::format_to_n(
+ subblockNameBuf,
+ _countof(subblockNameBuf),
+ L"\\StringFileInfo\\{:04x}{:04x}\\FileDescription",
+ lpTranslate[i].wLanguage,
+ lpTranslate[i].wCodePage).out = 0;;
+
wchar_t* buf = nullptr;
UINT size = 0;
- if (!VerQueryValueW(pBlock,
- std::format(L"\\StringFileInfo\\{:04x}{:04x}\\FileDescription",
- lpTranslate[i].wLanguage,
- lpTranslate[i].wCodePage).c_str(),
- reinterpret_cast(&buf),
- &size)) {
+ if (!VerQueryValueW(pBlock, subblockNameBuf, reinterpret_cast(&buf), &size))
continue;
- }
+
auto currName = std::wstring_view(buf, size);
- while (!currName.empty() && currName.back() == L'\0')
- currName = currName.substr(0, currName.size() - 1);
+ if (const auto p = currName.find(L'\0'); p != std::string::npos)
+ currName = currName.substr(0, p);
if (currName.empty())
continue;
return std::wstring(currName);
}
- throw std::runtime_error("Invalid version information (2)");
+ return DalamudUnexpected(
+ std::in_place,
+ DalamudBootErrorDescription::ModuleResourceVersionReadFail,
+ HRESULT_FROM_WIN32(ERROR_NOT_FOUND));
}
-const VS_FIXEDFILEINFO& utils::loaded_module::get_file_version() const {
- const auto rsrc = get_resource(MAKEINTRESOURCE(VS_VERSION_INFO), RT_VERSION);
- const auto pBlock = LockResource(rsrc.get());
+std::expected, DalamudBootError> utils::loaded_module::get_file_version() const {
+ auto rsrc = get_resource(MAKEINTRESOURCE(VS_VERSION_INFO), RT_VERSION);
+ if (!rsrc)
+ return DalamudUnexpected(std::move(rsrc.error()));
+
+ const auto pBlock = LockResource(rsrc->get());
UINT size = 0;
LPVOID lpBuffer = nullptr;
- if (!VerQueryValueW(pBlock, L"\\", &lpBuffer, &size))
- throw std::runtime_error("Failed to query version information.");
+ if (!VerQueryValueW(pBlock, L"\\", &lpBuffer, &size)) {
+ return std::unexpected(
+ std::in_place,
+ DalamudBootErrorDescription::ModuleResourceVersionReadFail,
+ HRESULT_FROM_WIN32(GetLastError()));
+ }
+
const VS_FIXEDFILEINFO& versionInfo = *static_cast(lpBuffer);
- if (versionInfo.dwSignature != 0xfeef04bd)
- throw std::runtime_error("Invalid version info found.");
+ if (versionInfo.dwSignature != 0xfeef04bd) {
+ return std::unexpected(
+ std::in_place,
+ DalamudBootErrorDescription::ModuleResourceVersionSignatureFail);
+ }
+
return versionInfo;
}
@@ -589,17 +618,10 @@ bool utils::is_running_on_wine() {
return g_startInfo.Platform != "WINDOWS";
}
-std::filesystem::path utils::get_module_path(HMODULE hModule) {
- std::wstring buf(MAX_PATH, L'\0');
- while (true) {
- if (const auto res = GetModuleFileNameW(hModule, &buf[0], static_cast(buf.size())); !res)
- throw std::runtime_error(std::format("GetModuleFileName failure: 0x{:X}", GetLastError()));
- else if (res < buf.size()) {
- buf.resize(res);
- return buf;
- } else
- buf.resize(buf.size() * 2);
- }
+std::wstring utils::get_string_resource(uint32_t resId) {
+ LPCWSTR pstr;
+ const auto len = LoadStringW(g_hModule, resId, reinterpret_cast(&pstr), 0);
+ return std::wstring(pstr, len);
}
HWND utils::try_find_game_window() {
@@ -677,3 +699,22 @@ std::wstring utils::format_win32_error(DWORD err) {
return std::format(L"Win32 error ({}=0x{:X})", err, err);
}
+
+utils::scoped_dpi_awareness_context::scoped_dpi_awareness_context()
+ : scoped_dpi_awareness_context(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) {
+}
+
+utils::scoped_dpi_awareness_context::scoped_dpi_awareness_context(DPI_AWARENESS_CONTEXT context) {
+ const auto user32 = GetModuleHandleW(L"user32.dll");
+ m_setThreadDpiAwarenessContext =
+ user32
+ ? reinterpret_cast(
+ GetProcAddress(user32, "SetThreadDpiAwarenessContext"))
+ : nullptr;
+ m_old = m_setThreadDpiAwarenessContext ? m_setThreadDpiAwarenessContext(context) : DPI_AWARENESS_CONTEXT_UNAWARE;
+}
+
+utils::scoped_dpi_awareness_context::~scoped_dpi_awareness_context() {
+ if (m_setThreadDpiAwarenessContext)
+ m_setThreadDpiAwarenessContext(m_old);
+}
diff --git a/Dalamud.Boot/utils.h b/Dalamud.Boot/utils.h
index cbbbccee8..c5833722b 100644
--- a/Dalamud.Boot/utils.h
+++ b/Dalamud.Boot/utils.h
@@ -1,5 +1,6 @@
#pragma once
+#include
#include
#include
#include
@@ -7,6 +8,7 @@
#include
#include
+#include "error_info.h"
#include "unicode.h"
namespace utils {
@@ -18,7 +20,7 @@ namespace utils {
loaded_module(void* hModule) : m_hModule(reinterpret_cast(hModule)) {}
loaded_module(size_t hModule) : m_hModule(reinterpret_cast(hModule)) {}
- std::filesystem::path path() const;
+ DalamudExpected path() const;
bool is_current_process() const { return m_hModule == GetModuleHandleW(nullptr); }
bool owns_address(const void* pAddress) const;
@@ -57,9 +59,9 @@ namespace utils {
void* get_imported_function_pointer(const char* pcszDllName, const char* pcszFunctionName, uint32_t hintOrOrdinal) const;
template TFn** get_imported_function_pointer(const char* pcszDllName, const char* pcszFunctionName, uint32_t hintOrOrdinal) { return reinterpret_cast(get_imported_function_pointer(pcszDllName, pcszFunctionName, hintOrOrdinal)); }
- [[nodiscard]] std::unique_ptr, decltype(&FreeResource)> get_resource(LPCWSTR lpName, LPCWSTR lpType) const;
- [[nodiscard]] std::wstring get_description() const;
- [[nodiscard]] const VS_FIXEDFILEINFO& get_file_version() const;
+ [[nodiscard]] DalamudExpected, decltype(&FreeResource)>> get_resource(LPCWSTR lpName, LPCWSTR lpType) const;
+ [[nodiscard]] DalamudExpected get_description() const;
+ [[nodiscard]] DalamudExpected get_file_version() const;
static loaded_module current_process();
static std::vector all_modules();
@@ -268,7 +270,7 @@ namespace utils {
bool is_running_on_wine();
- std::filesystem::path get_module_path(HMODULE hModule);
+ std::wstring get_string_resource(uint32_t resId);
/// @brief Find the game main window.
/// @return Handle to the game main window, or nullptr if it doesn't exist (yet).
@@ -279,4 +281,18 @@ namespace utils {
std::wstring escape_shell_arg(const std::wstring& arg);
std::wstring format_win32_error(DWORD err);
+
+ class scoped_dpi_awareness_context {
+ DPI_AWARENESS_CONTEXT m_old;
+ decltype(&SetThreadDpiAwarenessContext) m_setThreadDpiAwarenessContext;
+
+ public:
+ scoped_dpi_awareness_context();
+ scoped_dpi_awareness_context(DPI_AWARENESS_CONTEXT);
+ ~scoped_dpi_awareness_context();
+ scoped_dpi_awareness_context(const scoped_dpi_awareness_context&) = delete;
+ scoped_dpi_awareness_context(scoped_dpi_awareness_context&&) = delete;
+ scoped_dpi_awareness_context& operator=(const scoped_dpi_awareness_context&) = delete;
+ scoped_dpi_awareness_context& operator=(scoped_dpi_awareness_context&&) = delete;
+ };
}
diff --git a/Dalamud.Boot/veh.cpp b/Dalamud.Boot/veh.cpp
index 85d58eb9d..b0ec1cefa 100644
--- a/Dalamud.Boot/veh.cpp
+++ b/Dalamud.Boot/veh.cpp
@@ -102,9 +102,13 @@ bool is_ffxiv_address(const wchar_t* module_name, const DWORD64 address)
return false;
}
-static void append_injector_launch_args(std::vector& args)
+static DalamudExpected append_injector_launch_args(std::vector& args)
{
- args.emplace_back(L"--game=\"" + utils::loaded_module::current_process().path().wstring() + L"\"");
+ if (auto path = utils::loaded_module::current_process().path())
+ args.emplace_back(L"--game=\"" + path->wstring() + L"\"");
+ else
+ return DalamudUnexpected(std::in_place, std::move(path.error()));
+
switch (g_startInfo.DalamudLoadMethod) {
case DalamudStartInfo::LoadMethod::Entrypoint:
args.emplace_back(L"--mode=entrypoint");
@@ -155,6 +159,8 @@ static void append_injector_launch_args(std::vector& args)
args.emplace_back(szArgList[i]);
LocalFree(szArgList);
}
+
+ return {};
}
LONG exception_handler(EXCEPTION_POINTERS* ex)
@@ -358,11 +364,20 @@ bool veh::add_handler(bool doFullDump, const std::string& workingDirectory)
args.emplace_back(std::format(L"--process-handle={}", reinterpret_cast(hInheritableCurrentProcess)));
args.emplace_back(std::format(L"--exception-info-pipe-read-handle={}", reinterpret_cast(hReadPipeInheritable->get())));
args.emplace_back(std::format(L"--asset-directory={}", unicode::convert(g_startInfo.AssetDirectory)));
- args.emplace_back(std::format(L"--log-directory={}", g_startInfo.BootLogPath.empty()
- ? utils::loaded_module(g_hModule).path().parent_path().wstring()
- : std::filesystem::path(unicode::convert(g_startInfo.BootLogPath)).parent_path().wstring()));
+ if (const auto path = utils::loaded_module(g_hModule).path()) {
+ args.emplace_back(std::format(L"--log-directory={}", g_startInfo.BootLogPath.empty()
+ ? path->parent_path().wstring()
+ : std::filesystem::path(unicode::convert(g_startInfo.BootLogPath)).parent_path().wstring()));
+ } else {
+ logging::W("Failed to read path of the Dalamud Boot module: {}", path.error().describe());
+ return false;
+ }
+
args.emplace_back(L"--");
- append_injector_launch_args(args);
+ if (auto r = append_injector_launch_args(args); !r) {
+ logging::W("Failed to generate injector launch args: {}", r.error().describe());
+ return false;
+ }
for (const auto& arg : args)
{
diff --git a/Dalamud.Boot/xivfixes.cpp b/Dalamud.Boot/xivfixes.cpp
index eb0f7df56..7f9e92225 100644
--- a/Dalamud.Boot/xivfixes.cpp
+++ b/Dalamud.Boot/xivfixes.cpp
@@ -8,12 +8,6 @@
#include "ntdll.h"
#include "utils.h"
-template
-static std::span assume_nonempty_span(std::span t, const char* descr) {
- if (t.empty())
- throw std::runtime_error(std::format("Unexpected empty span found: {}", descr));
- return t;
-}
void xivfixes::unhook_dll(bool bApply) {
static const auto LogTag = "[xivfixes:unhook_dll]";
static const auto LogTagW = L"[xivfixes:unhook_dll]";
@@ -23,77 +17,90 @@ void xivfixes::unhook_dll(bool bApply) {
const auto mods = utils::loaded_module::all_modules();
- const auto test_module = [&](size_t i, const utils::loaded_module & mod) {
- std::filesystem::path path;
- try {
- path = mod.path();
- std::wstring version, description;
- try {
- version = utils::format_file_version(mod.get_file_version());
- } catch (...) {
- version = L"";
- }
-
- try {
- description = mod.get_description();
- } catch (...) {
- description = L"";
- }
-
- logging::I(R"({} [{}/{}] Module 0x{:X} ~ 0x{:X} (0x{:X}): "{}" ("{}" ver {}))", LogTagW, i + 1, mods.size(), mod.address_int(), mod.address_int() + mod.image_size(), mod.image_size(), path.wstring(), description, version);
- } catch (const std::exception& e) {
- logging::W("{} [{}/{}] Module 0x{:X}: Failed to resolve path: {}", LogTag, i + 1, mods.size(), mod.address_int(), e.what());
+ for (size_t i = 0; i < mods.size(); i++) {
+ const auto& mod = mods[i];
+ const auto path = mod.path();
+ if (!path) {
+ logging::W(
+ "{} [{}/{}] Module 0x{:X}: Failed to resolve path: {}",
+ LogTag,
+ i + 1,
+ mods.size(),
+ mod.address_int(),
+ path.error().describe());
return;
}
- const auto moduleName = unicode::convert(path.filename().wstring());
+ const auto version = mod.get_file_version()
+ .transform([](const auto& v) { return utils::format_file_version(v.get()); })
+ .value_or(L"");
- std::vector buf;
- std::string formatBuf;
+ const auto description = mod.get_description()
+ .value_or(L"");
+
+ logging::I(
+ R"({} [{}/{}] Module 0x{:X} ~ 0x{:X} (0x{:X}): "{}" ("{}" ver {}))",
+ LogTagW,
+ i + 1,
+ mods.size(),
+ mod.address_int(),
+ mod.address_int() + mod.image_size(),
+ mod.image_size(),
+ path->wstring(),
+ description,
+ version);
+
+ const auto moduleName = unicode::convert(path->filename().wstring());
+
+ const auto& sectionHeader = mod.section_header(".text");
+ const auto section = mod.span_as(sectionHeader.VirtualAddress, sectionHeader.Misc.VirtualSize);
+ if (section.empty()) {
+ logging::W("{} Error: .text[VA:VA + VS] is empty", LogTag);
+ return;
+ }
+
+ auto hFsDllRaw = CreateFileW(path->c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr);
+ if (hFsDllRaw == INVALID_HANDLE_VALUE) {
+ logging::W("{} Module loaded in current process but could not open file: Win32 error {}", LogTag, GetLastError());
+ return;
+ }
+
+ auto hFsDll = std::unique_ptr(hFsDllRaw, &CloseHandle);
+ std::vector buf(section.size());
+ SetFilePointer(hFsDll.get(), sectionHeader.PointerToRawData, nullptr, FILE_CURRENT);
+ if (DWORD read{}; ReadFile(hFsDll.get(), &buf[0], static_cast(buf.size()), &read, nullptr)) {
+ if (read < section.size_bytes()) {
+ logging::W("{} ReadFile: read {} bytes < requested {} bytes", LogTagW, read, section.size_bytes());
+ return;
+ }
+ } else {
+ logging::I("{} ReadFile: Win32 error {}", LogTagW, GetLastError());
+ return;
+ }
+
+ const auto doRestore = g_startInfo.BootUnhookDlls.contains(unicode::convert(path->filename().u8string()));
try {
- const auto& sectionHeader = mod.section_header(".text");
- const auto section = assume_nonempty_span(mod.span_as(sectionHeader.VirtualAddress, sectionHeader.Misc.VirtualSize), ".text[VA:VA+VS]");
- auto hFsDllRaw = CreateFileW(path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr);
- if (hFsDllRaw == INVALID_HANDLE_VALUE) {
- logging::W("{} Module loaded in current process but could not open file: Win32 error {}", LogTag, GetLastError());
- return;
- }
- auto hFsDll = std::unique_ptr(hFsDllRaw, &CloseHandle);
-
- buf.resize(section.size());
- SetFilePointer(hFsDll.get(), sectionHeader.PointerToRawData, nullptr, FILE_CURRENT);
- if (DWORD read{}; ReadFile(hFsDll.get(), &buf[0], static_cast(buf.size()), &read, nullptr)) {
- if (read < section.size_bytes()) {
- logging::W("{} ReadFile: read {} bytes < requested {} bytes", LogTagW, read, section.size_bytes());
- return;
- }
- } else {
- logging::I("{} ReadFile: Win32 error {}", LogTagW, GetLastError());
- return;
- }
-
- const auto doRestore = g_startInfo.BootUnhookDlls.contains(unicode::convert(path.filename().u8string()));
-
std::optional tenderizer;
- for (size_t i = 0, instructionLength = 1, printed = 0; i < buf.size(); i += instructionLength) {
- if (section[i] == buf[i]) {
+ std::string formatBuf;
+ for (size_t inst = 0, instructionLength = 1, printed = 0; inst < buf.size(); inst += instructionLength) {
+ if (section[inst] == buf[inst]) {
instructionLength = 1;
continue;
}
- const auto rva = sectionHeader.VirtualAddress + i;
+ const auto rva = sectionHeader.VirtualAddress + inst;
nmd_x86_instruction instruction{};
- if (!nmd_x86_decode(§ion[i], section.size() - i, &instruction, NMD_X86_MODE_64, NMD_X86_DECODER_FLAGS_ALL)) {
+ if (!nmd_x86_decode(§ion[inst], section.size() - inst, &instruction, NMD_X86_MODE_64, NMD_X86_DECODER_FLAGS_ALL)) {
instructionLength = 1;
if (printed < 64) {
- logging::W("{} {}+0x{:0X}: dd {:02X}", LogTag, moduleName, rva, static_cast(section[i]));
+ logging::W("{} {}+0x{:0X}: dd {:02X}", LogTag, moduleName, rva, static_cast(section[inst]));
printed++;
}
} else {
instructionLength = instruction.length;
if (printed < 64) {
formatBuf.resize(128);
- nmd_x86_format(&instruction, &formatBuf[0], reinterpret_cast(§ion[i]), NMD_X86_FORMAT_FLAGS_DEFAULT | NMD_X86_FORMAT_FLAGS_BYTES);
+ nmd_x86_format(&instruction, &formatBuf[0], reinterpret_cast(§ion[inst]), NMD_X86_FORMAT_FLAGS_DEFAULT | NMD_X86_FORMAT_FLAGS_BYTES);
formatBuf.resize(strnlen(&formatBuf[0], formatBuf.size()));
const auto& directory = mod.data_directory(IMAGE_DIRECTORY_ENTRY_EXPORT);
@@ -103,25 +110,25 @@ void xivfixes::unhook_dll(bool bApply) {
const auto functions = mod.span_as(exportDirectory.AddressOfFunctions, exportDirectory.NumberOfFunctions);
std::string resolvedExportName;
- for (size_t j = 0; j < names.size(); ++j) {
+ for (size_t nameIndex = 0; nameIndex < names.size(); ++nameIndex) {
std::string_view name;
- if (const char* pcszName = mod.address_as(names[j]); pcszName < mod.address() || pcszName >= mod.address() + mod.image_size()) {
+ if (const char* pcszName = mod.address_as(names[nameIndex]); pcszName < mod.address() || pcszName >= mod.address() + mod.image_size()) {
if (IsBadReadPtr(pcszName, 256)) {
- logging::W("{} Name #{} points to an invalid address outside the executable. Skipping.", LogTag, j);
+ logging::W("{} Name #{} points to an invalid address outside the executable. Skipping.", LogTag, nameIndex);
continue;
}
name = std::string_view(pcszName, strnlen(pcszName, 256));
- logging::W("{} Name #{} points to a seemingly valid address outside the executable: {}", LogTag, j, name);
+ logging::W("{} Name #{} points to a seemingly valid address outside the executable: {}", LogTag, nameIndex, name);
}
- if (ordinals[j] >= functions.size()) {
- logging::W("{} Ordinal #{} points to function index #{} >= #{}. Skipping.", LogTag, j, ordinals[j], functions.size());
+ if (ordinals[nameIndex] >= functions.size()) {
+ logging::W("{} Ordinal #{} points to function index #{} >= #{}. Skipping.", LogTag, nameIndex, ordinals[nameIndex], functions.size());
continue;
}
- const auto rva = functions[ordinals[j]];
- if (rva == §ion[i] - mod.address()) {
+ const auto rva = functions[ordinals[nameIndex]];
+ if (rva == §ion[inst] - mod.address()) {
resolvedExportName = std::format("[export:{}]", name);
break;
}
@@ -135,7 +142,7 @@ void xivfixes::unhook_dll(bool bApply) {
if (doRestore) {
if (!tenderizer)
tenderizer.emplace(section, PAGE_EXECUTE_READWRITE);
- memcpy(§ion[i], &buf[i], instructionLength);
+ memcpy(§ion[inst], &buf[inst], instructionLength);
}
}
@@ -147,21 +154,7 @@ void xivfixes::unhook_dll(bool bApply) {
} catch (const std::exception& e) {
logging::W("{} Error: {}", LogTag, e.what());
}
- };
-
- // This is needed since try and __try cannot be used in the same function. Lambdas circumvent the limitation.
- const auto windows_exception_handler = [&]() {
- for (size_t i = 0; i < mods.size(); i++) {
- const auto& mod = mods[i];
- __try {
- test_module(i, mod);
- } __except (EXCEPTION_EXECUTE_HANDLER) {
- logging::W("{} Error: Access Violation", LogTag);
- }
- }
- };
-
- windows_exception_handler();
+ }
}
using TFnGetInputDeviceManager = void* ();
@@ -294,13 +287,11 @@ static bool is_xivalex(const std::filesystem::path& dllPath) {
static bool is_openprocess_already_dealt_with() {
static const auto s_value = [] {
for (const auto& mod : utils::loaded_module::all_modules()) {
- try {
- if (is_xivalex(mod.path()))
- return true;
-
- } catch (...) {
- // pass
- }
+ const auto path = mod.path().value_or({});
+ if (path.empty())
+ continue;
+ if (is_xivalex(path))
+ return true;
}
return false;
}();
@@ -650,43 +641,22 @@ void xivfixes::symbol_load_patches(bool bApply) {
void xivfixes::disable_game_debugging_protection(bool bApply) {
static const char* LogTag = "[xivfixes:disable_game_debugging_protection]";
- static const std::vector patchBytes = {
- 0x31, 0xC0, // XOR EAX, EAX
- 0x90, // NOP
- 0x90, // NOP
- 0x90, // NOP
- 0x90 // NOP
- };
+ static std::optional> s_hookIsDebuggerPresent;
- if (!bApply)
- return;
+ if (bApply) {
+ if (!g_startInfo.BootEnabledGameFixes.contains("disable_game_debugging_protection")) {
+ logging::I("{} Turned off via environment variable.", LogTag);
+ return;
+ }
- if (!g_startInfo.BootEnabledGameFixes.contains("disable_game_debugging_protection")) {
- logging::I("{} Turned off via environment variable.", LogTag);
- return;
- }
-
- // Find IsDebuggerPresent in Framework.Tick()
- const char* matchPtr = utils::signature_finder()
- .look_in(utils::loaded_module(g_hGameInstance), ".text")
- .look_for_hex("FF 15 ?? ?? ?? ?? 85 C0 74 13 41")
- .find_one()
- .Match.data();
-
- if (!matchPtr) {
- logging::E("{} Failed to find signature.", LogTag);
- return;
- }
-
- void* address = const_cast(static_cast(matchPtr));
-
- DWORD oldProtect;
- if (VirtualProtect(address, patchBytes.size(), PAGE_EXECUTE_READWRITE, &oldProtect)) {
- memcpy(address, patchBytes.data(), patchBytes.size());
- VirtualProtect(address, patchBytes.size(), oldProtect, &oldProtect);
- logging::I("{} Patch applied at address 0x{:X}.", LogTag, reinterpret_cast(address));
+ s_hookIsDebuggerPresent.emplace("kernel32.dll!IsDebuggerPresent", "kernel32.dll", "IsDebuggerPresent", 0);
+ s_hookIsDebuggerPresent->set_detour([]() { return false; });
+ logging::I("{} Enable", LogTag);
} else {
- logging::E("{} Failed to change memory protection.", LogTag);
+ if (s_hookIsDebuggerPresent) {
+ logging::I("{} Disable", LogTag);
+ s_hookIsDebuggerPresent.reset();
+ }
}
}
diff --git a/Dalamud.Injector.Boot/Dalamud.Injector.Boot.vcxproj b/Dalamud.Injector.Boot/Dalamud.Injector.Boot.vcxproj
index 1c89f4ff7..7f8de3843 100644
--- a/Dalamud.Injector.Boot/Dalamud.Injector.Boot.vcxproj
+++ b/Dalamud.Injector.Boot/Dalamud.Injector.Boot.vcxproj
@@ -38,7 +38,7 @@
Level3
true
true
- stdcpplatest
+ stdcpp23
pch.h
ProgramDatabase
CPPDLLTEMPLATE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
@@ -55,7 +55,7 @@
true
false
- MultiThreadedDebugDLL
+ MultiThreadedDebugDLL
_DEBUG;%(PreprocessorDefinitions)
@@ -67,7 +67,7 @@
true
true
- MultiThreadedDLL
+ MultiThreadedDLL
NDEBUG;%(PreprocessorDefinitions)
@@ -108,4 +108,4 @@
-
+
\ No newline at end of file
diff --git a/Dalamud/Interface/Internal/InterfaceManager.cs b/Dalamud/Interface/Internal/InterfaceManager.cs
index e94501d92..d68bc8bef 100644
--- a/Dalamud/Interface/Internal/InterfaceManager.cs
+++ b/Dalamud/Interface/Internal/InterfaceManager.cs
@@ -635,29 +635,6 @@ internal partial class InterfaceManager : IInternalDisposableService
Service.ProvideException(ex);
Log.Error(ex, "Could not load ImGui dependencies.");
- fixed (void* lpText =
- "Dalamud plugins require the Microsoft Visual C++ Redistributable to be installed.\nPlease install the runtime from the official Microsoft website or disable Dalamud.\n\nDo you want to download the redistributable now?")
- {
- fixed (void* lpCaption = "Dalamud Error")
- {
- var res = MessageBoxW(
- default,
- (ushort*)lpText,
- (ushort*)lpCaption,
- MB.MB_YESNO | MB.MB_TOPMOST | MB.MB_ICONERROR);
-
- if (res == IDYES)
- {
- var psi = new ProcessStartInfo
- {
- FileName = "https://aka.ms/vs/16/release/vc_redist.x64.exe",
- UseShellExecute = true,
- };
- Process.Start(psi);
- }
- }
- }
-
Environment.Exit(-1);
// Doesn't reach here, but to make the compiler not complain