diff --git a/Dalamud.Boot/Dalamud.Boot.rc b/Dalamud.Boot/Dalamud.Boot.rc
new file mode 100644
index 000000000..daa41a282
--- /dev/null
+++ b/Dalamud.Boot/Dalamud.Boot.rc
@@ -0,0 +1,71 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "winres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (United Kingdom) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG)
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK
+#pragma code_page(1252)
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""winres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_ICON1 ICON "dalamud.ico"
+
+#endif // English (United Kingdom) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/Dalamud.Boot/Dalamud.Boot.vcxproj b/Dalamud.Boot/Dalamud.Boot.vcxproj
index e71750f47..3733fe388 100644
--- a/Dalamud.Boot/Dalamud.Boot.vcxproj
+++ b/Dalamud.Boot/Dalamud.Boot.vcxproj
@@ -165,14 +165,21 @@
+
+
+
+
+
+
+
-
\ No newline at end of file
+
diff --git a/Dalamud.Boot/Dalamud.Boot.vcxproj.filters b/Dalamud.Boot/Dalamud.Boot.vcxproj.filters
index b527ec60c..27483eeed 100644
--- a/Dalamud.Boot/Dalamud.Boot.vcxproj.filters
+++ b/Dalamud.Boot/Dalamud.Boot.vcxproj.filters
@@ -138,5 +138,12 @@
MinHook
+
-
\ No newline at end of file
+
+
+
+
+
+
+
diff --git a/Dalamud.Boot/dalamud.ico b/Dalamud.Boot/dalamud.ico
new file mode 100644
index 000000000..1cd63765d
Binary files /dev/null and b/Dalamud.Boot/dalamud.ico differ
diff --git a/Dalamud.Boot/resource.h b/Dalamud.Boot/resource.h
new file mode 100644
index 000000000..51acf37df
--- /dev/null
+++ b/Dalamud.Boot/resource.h
@@ -0,0 +1,16 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by Dalamud.Boot.rc
+//
+#define IDI_ICON1 101
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 102
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/Dalamud.Boot/veh.cpp b/Dalamud.Boot/veh.cpp
index 887a189bb..6979b2564 100644
--- a/Dalamud.Boot/veh.cpp
+++ b/Dalamud.Boot/veh.cpp
@@ -1,10 +1,26 @@
#include "pch.h"
+#include "resource.h"
+
#include "veh.h"
+#include
+
#include "logging.h"
#include "utils.h"
+#pragma comment(lib, "comctl32.lib")
+
+#if defined _M_IX86
+#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"")
+#elif defined _M_IA64
+#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='ia64' publicKeyToken='6595b64144ccf1df' language='*'\"")
+#elif defined _M_X64
+#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"")
+#else
+#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
+#endif
+
PVOID g_veh_handle = nullptr;
bool g_veh_do_full_dump = false;
@@ -69,11 +85,11 @@ bool get_module_file_and_base(const DWORD64 address, DWORD64& module_base, std::
}
-bool is_ffxiv_address(const DWORD64 address)
+bool is_ffxiv_address(const wchar_t* module_name, const DWORD64 address)
{
DWORD64 module_base;
if (std::filesystem::path module_path; get_module_file_and_base(address, module_base, module_path))
- return _wcsicmp(module_path.filename().c_str(), L"ffxiv_dx11.exe") == 0;
+ return _wcsicmp(module_path.filename().c_str(), module_name) == 0;
return false;
}
@@ -118,53 +134,9 @@ std::wstring to_address_string(const DWORD64 address, const bool try_ptrderef =
return value != 0 ? std::format(L"{} [{}]", addr_str, to_address_string(value, false)) : addr_str;
}
-
-void print_exception_info(const EXCEPTION_POINTERS* ex, std::wostringstream& log)
+void print_exception_info_extended(const EXCEPTION_POINTERS* ex, std::wostringstream& log)
{
- size_t rec_index = 0;
- for (auto rec = ex->ExceptionRecord; rec; rec = rec->ExceptionRecord)
- {
- log << std::format(L"\nException Info #{}\n", ++rec_index);
- log << std::format(L"Address: {:X}\n", rec->ExceptionCode);
- log << std::format(L"Flags: {:X}\n", rec->ExceptionFlags);
- log << std::format(L"Address: {:X}\n", reinterpret_cast(rec->ExceptionAddress));
- if (!rec->NumberParameters)
- continue;
- log << L"Parameters: ";
- for (DWORD i = 0; i < rec->NumberParameters; ++i)
- {
- if (i != 0)
- log << L", ";
- log << std::format(L"{:X}", rec->ExceptionInformation[i]);
- }
- }
-
- log << L"\nCall Stack\n{";
-
- STACKFRAME64 sf;
- sf.AddrPC.Offset = ex->ContextRecord->Rip;
- sf.AddrPC.Mode = AddrModeFlat;
- sf.AddrStack.Offset = ex->ContextRecord->Rsp;
- sf.AddrStack.Mode = AddrModeFlat;
- sf.AddrFrame.Offset = ex->ContextRecord->Rbp;
- sf.AddrFrame.Mode = AddrModeFlat;
CONTEXT ctx = *ex->ContextRecord;
- int frame_index = 0;
-
- log << std::format(L"\n [{}]\t{}", frame_index++, to_address_string(sf.AddrPC.Offset, false));
-
- do
- {
- if (!StackWalk64(IMAGE_FILE_MACHINE_AMD64, GetCurrentProcess(), GetCurrentThread(), &sf, &ctx, nullptr, SymFunctionTableAccess64, SymGetModuleBase64, nullptr))
- break;
-
- log << std::format(L"\n [{}]\t{}", frame_index++, to_address_string(sf.AddrPC.Offset, false));
-
- } while (sf.AddrReturn.Offset != 0 && sf.AddrPC.Offset != sf.AddrReturn.Offset);
-
- log << L"\n}\n";
-
- ctx = *ex->ContextRecord;
log << L"\nRegisters\n{";
@@ -219,6 +191,70 @@ void print_exception_info(const EXCEPTION_POINTERS* ex, std::wostringstream& log
log << L"\n}\n";
}
+void print_exception_info(const EXCEPTION_POINTERS* ex, std::wostringstream& log)
+{
+ size_t rec_index = 0;
+ for (auto rec = ex->ExceptionRecord; rec; rec = rec->ExceptionRecord)
+ {
+ log << std::format(L"\nException Info #{}\n", ++rec_index);
+ log << std::format(L"Address: {:X}\n", rec->ExceptionCode);
+ log << std::format(L"Flags: {:X}\n", rec->ExceptionFlags);
+ log << std::format(L"Address: {:X}\n", reinterpret_cast(rec->ExceptionAddress));
+ if (!rec->NumberParameters)
+ continue;
+ log << L"Parameters: ";
+ for (DWORD i = 0; i < rec->NumberParameters; ++i)
+ {
+ if (i != 0)
+ log << L", ";
+ log << std::format(L"{:X}", rec->ExceptionInformation[i]);
+ }
+ }
+
+ log << L"\nCall Stack\n{";
+
+ STACKFRAME64 sf;
+ sf.AddrPC.Offset = ex->ContextRecord->Rip;
+ sf.AddrPC.Mode = AddrModeFlat;
+ sf.AddrStack.Offset = ex->ContextRecord->Rsp;
+ sf.AddrStack.Mode = AddrModeFlat;
+ sf.AddrFrame.Offset = ex->ContextRecord->Rbp;
+ sf.AddrFrame.Mode = AddrModeFlat;
+ CONTEXT ctx = *ex->ContextRecord;
+ int frame_index = 0;
+
+ log << std::format(L"\n [{}]\t{}", frame_index++, to_address_string(sf.AddrPC.Offset, false));
+
+ do
+ {
+ if (!StackWalk64(IMAGE_FILE_MACHINE_AMD64, GetCurrentProcess(), GetCurrentThread(), &sf, &ctx, nullptr, SymFunctionTableAccess64, SymGetModuleBase64, nullptr))
+ break;
+
+ log << std::format(L"\n [{}]\t{}", frame_index++, to_address_string(sf.AddrPC.Offset, false));
+
+ } while (sf.AddrReturn.Offset != 0 && sf.AddrPC.Offset != sf.AddrReturn.Offset);
+
+ log << L"\n}\n";
+}
+
+HRESULT CALLBACK TaskDialogCallbackProc(HWND hwnd,
+ UINT uNotification,
+ WPARAM wParam,
+ LPARAM lParam,
+ LONG_PTR dwRefData)
+{
+ HRESULT hr = S_OK;
+
+ switch (uNotification)
+ {
+ case TDN_CREATED:
+ SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+ break;
+ }
+
+ return hr;
+}
+
LONG exception_handler(EXCEPTION_POINTERS* ex)
{
static std::recursive_mutex s_exception_handler_mutex;
@@ -226,7 +262,8 @@ LONG exception_handler(EXCEPTION_POINTERS* ex)
if (!is_whitelist_exception(ex->ExceptionRecord->ExceptionCode))
return EXCEPTION_CONTINUE_SEARCH;
- if (!is_ffxiv_address(ex->ContextRecord->Rip))
+ if (!is_ffxiv_address(L"ffxiv_dx11.exe", ex->ContextRecord->Rip) &&
+ !is_ffxiv_address(L"cimgui.dll", ex->ContextRecord->Rip))
return EXCEPTION_CONTINUE_SEARCH;
// block any other exceptions hitting the veh while the messagebox is open
@@ -248,6 +285,8 @@ LONG exception_handler(EXCEPTION_POINTERS* ex)
SymRefreshModuleList(GetCurrentProcess());
print_exception_info(ex, log);
+ auto window_log_str = log.str();
+ print_exception_info_extended(ex, log);
MINIDUMP_EXCEPTION_INFORMATION ex_info;
ex_info.ClientPointers = false;
@@ -257,9 +296,9 @@ LONG exception_handler(EXCEPTION_POINTERS* ex)
auto miniDumpType = MiniDumpWithDataSegs;
if (g_veh_do_full_dump)
miniDumpType = MiniDumpWithFullMemory;
-
+
HANDLE file = CreateFileW(dmp_path.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
- MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), file, miniDumpType, &ex_info, nullptr, nullptr);
+ //MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), file, miniDumpType, &ex_info, nullptr, nullptr);
CloseHandle(file);
std::wstring message;
@@ -289,7 +328,58 @@ LONG exception_handler(EXCEPTION_POINTERS* ex)
logging::E(std::format(L"Trapped in VEH handler: {}", message));
// show in another thread to prevent messagebox from pumping messages of current thread
- std::thread([&]() { MessageBoxW(nullptr, message.c_str(), L"Dalamud Error", MB_ICONERROR | MB_TOPMOST | MB_OK); }).join();
+ std::thread([&]()
+ {
+ int nButtonPressed = 0;
+ TASKDIALOGCONFIG config = {0};
+ const TASKDIALOG_BUTTON buttons[] = {
+ {IDOK, L"Disable all plugins"},
+ {IDABORT, L"Open help page"},
+ };
+ config.cbSize = sizeof(config);
+ config.hInstance = g_hModule;
+ config.dwCommonButtons = TDCBF_CLOSE_BUTTON;
+ config.pszMainIcon = MAKEINTRESOURCE(IDI_ICON1);
+ //config.hMainIcon = dalamud_icon;
+ config.pszMainInstruction = L"An error occurred";
+ config.pszContent = message.c_str();
+ config.pButtons = buttons;
+ config.cButtons = ARRAYSIZE(buttons);
+ config.pszExpandedInformation = window_log_str.c_str();
+ config.pszWindowTitle = L"Dalamud Error";
+ config.nDefaultButton = IDCLOSE;
+ config.cxWidth = 300;
+
+ // Can't do this, xiv stops pumping messages here
+ //config.hwndParent = FindWindowA("FFXIVGAME", NULL);
+
+ config.pfCallback = TaskDialogCallbackProc;
+
+ TaskDialogIndirect(&config, &nButtonPressed, NULL, NULL);
+ switch (nButtonPressed)
+ {
+ case IDOK:
+ TCHAR szPath[MAX_PATH];
+ if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, szPath)))
+ {
+ auto appdata = std::filesystem::path(szPath);
+ auto safemode_file_path = ( appdata / "XIVLauncher" / ".dalamud_safemode" );
+
+ std::ofstream ofs(safemode_file_path);
+ ofs << "STAY SAFE!!!";
+ ofs.close();
+ }
+
+ break;
+ case IDABORT:
+ ShellExecute(0, 0, L"https://goatcorp.github.io/faq/", 0, 0 , SW_SHOW );
+ break;
+ case IDCANCEL:
+ break;
+ default:
+ break;
+ }
+ }).join();
return EXCEPTION_CONTINUE_SEARCH;
}
diff --git a/Dalamud.Boot/xivfixes.cpp b/Dalamud.Boot/xivfixes.cpp
index 13315669c..338938fc1 100644
--- a/Dalamud.Boot/xivfixes.cpp
+++ b/Dalamud.Boot/xivfixes.cpp
@@ -497,6 +497,7 @@ void xivfixes::clr_failfast_hijack(bool bApply)
{
static const char* LogTag = "[xivfixes:clr_failfast_hijack]";
static std::optional> s_HookClrFatalError;
+ static std::optional> s_HookSetUnhandledExceptionFilter;
if (bApply)
{
@@ -506,7 +507,8 @@ void xivfixes::clr_failfast_hijack(bool bApply)
}
s_HookClrFatalError.emplace("kernel32.dll!RaiseFailFastException (import, backup_userdata_save)", "kernel32.dll", "RaiseFailFastException", 0);
-
+ s_HookSetUnhandledExceptionFilter.emplace("kernel32.dll!SetUnhandledExceptionFilter (lpTopLevelExceptionFilter)", "kernel32.dll", "SetUnhandledExceptionFilter", 0);
+
s_HookClrFatalError->set_detour([](PEXCEPTION_RECORD pExceptionRecord,
_In_opt_ PCONTEXT pContextRecord,
_In_ DWORD dwFlags)
@@ -516,6 +518,12 @@ void xivfixes::clr_failfast_hijack(bool bApply)
return s_HookClrFatalError->call_original(pExceptionRecord, pContextRecord, dwFlags);
});
+ s_HookSetUnhandledExceptionFilter->set_detour([](LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter) -> LPTOP_LEVEL_EXCEPTION_FILTER
+ {
+ logging::I("{} SetUnhandledExceptionFilter", LogTag);
+ return nullptr;
+ });
+
logging::I("{} Enable", LogTag);
}
else
@@ -523,6 +531,7 @@ void xivfixes::clr_failfast_hijack(bool bApply)
if (s_HookClrFatalError) {
logging::I("{} Disable ClrFatalError", LogTag);
s_HookClrFatalError.reset();
+ s_HookSetUnhandledExceptionFilter.reset();
}
}
}
diff --git a/Dalamud.Injector/EntryPoint.cs b/Dalamud.Injector/EntryPoint.cs
index 7bd4cdbe9..88a240c65 100644
--- a/Dalamud.Injector/EntryPoint.cs
+++ b/Dalamud.Injector/EntryPoint.cs
@@ -322,7 +322,8 @@ namespace Dalamud.Injector
startInfo.BootWaitMessageBox |= args.Contains("--msgbox1") ? 1 : 0;
startInfo.BootWaitMessageBox |= args.Contains("--msgbox2") ? 2 : 0;
startInfo.BootWaitMessageBox |= args.Contains("--msgbox3") ? 4 : 0;
- startInfo.BootVehEnabled = args.Contains("--veh");
+ // startInfo.BootVehEnabled = args.Contains("--veh");
+ startInfo.BootVehEnabled = true;
startInfo.BootVehFull = args.Contains("--veh-full");
startInfo.NoLoadPlugins = args.Contains("--no-plugin");
startInfo.NoLoadThirdPartyPlugins = args.Contains("--no-third-plugin");
diff --git a/Dalamud/EntryPoint.cs b/Dalamud/EntryPoint.cs
index f0b5b884d..cfc934f36 100644
--- a/Dalamud/EntryPoint.cs
+++ b/Dalamud/EntryPoint.cs
@@ -81,12 +81,8 @@ namespace Dalamud
stackTrace = "Fail: " + e.ToString();
}
- var msg = "An error within the game has occurred.\n\n"
- + "This may be caused by a faulty plugin, a broken TexTools modification, any other third-party tool or simply a bug in the game.\n"
- + "Please try \"Start Over\" or \"Download Index Backup\" in TexTools, an integrity check in the XIVLauncher settings, and disabling plugins you don't need.\n\n"
- + "The log file is located at:\n"
- + "{1}\n\n"
- + "Press OK to exit the application.\n\nStack trace:\n{2}";
+ var msg = "This may be caused by a faulty plugin, a broken TexTools modification, any other third-party tool or simply a bug in the game.\n\n"
+ + "Please attempt an integrity check in the XIVLauncher settings, and disabling plugins you don't need.";
try
{
diff --git a/Dalamud/Plugin/Internal/PluginManager.cs b/Dalamud/Plugin/Internal/PluginManager.cs
index 8f8a2a963..fc98709ee 100644
--- a/Dalamud/Plugin/Internal/PluginManager.cs
+++ b/Dalamud/Plugin/Internal/PluginManager.cs
@@ -69,6 +69,17 @@ internal partial class PluginManager : IDisposable, IServiceType
this.devPluginDirectory.Create();
this.SafeMode = EnvironmentConfiguration.DalamudNoPlugins || this.configuration.PluginSafeMode || this.startInfo.NoLoadPlugins;
+
+ try
+ {
+ var appdata = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
+ this.SafeMode = this.SafeMode || File.Exists(Path.Combine(appdata, "XIVLauncher", ".dalamud_safemode"));
+ }
+ catch (Exception ex)
+ {
+ Log.Error(ex, "Couldn't check safe mode file");
+ }
+
if (this.SafeMode)
{
this.configuration.PluginSafeMode = false;