mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-12 10:17:22 +01:00
Reduce usage of exceptions from Boot (#2373)
* wip * make pretty * Remove CRT version check from IM * fix * Simplify IsDebuggerPresent hook
This commit is contained in:
parent
f613b177a2
commit
9092e36b33
16 changed files with 494 additions and 295 deletions
|
|
@ -26,6 +26,38 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||||
|
|
||||||
RT_MANIFEST_THEMES RT_MANIFEST "themes.manifest"
|
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
|
#endif // English (United States) resources
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@
|
||||||
<WarningLevel>Level3</WarningLevel>
|
<WarningLevel>Level3</WarningLevel>
|
||||||
<SDLCheck>true</SDLCheck>
|
<SDLCheck>true</SDLCheck>
|
||||||
<ConformanceMode>true</ConformanceMode>
|
<ConformanceMode>true</ConformanceMode>
|
||||||
<LanguageStandard>stdcpp20</LanguageStandard>
|
<LanguageStandard>stdcpp23</LanguageStandard>
|
||||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||||
<PreprocessorDefinitions>CPPDLLTEMPLATE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>CPPDLLTEMPLATE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
|
@ -65,7 +65,7 @@
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
<IntrinsicFunctions>false</IntrinsicFunctions>
|
<IntrinsicFunctions>false</IntrinsicFunctions>
|
||||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||||
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Use</PrecompiledHeader>
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Use</PrecompiledHeader>
|
||||||
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">26812</DisableSpecificWarnings>
|
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">26812</DisableSpecificWarnings>
|
||||||
|
|
@ -80,7 +80,7 @@
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||||
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Use</PrecompiledHeader>
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Use</PrecompiledHeader>
|
||||||
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|x64'">26812</DisableSpecificWarnings>
|
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|x64'">26812</DisableSpecificWarnings>
|
||||||
|
|
@ -133,6 +133,10 @@
|
||||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="DalamudStartInfo.cpp" />
|
<ClCompile Include="DalamudStartInfo.cpp" />
|
||||||
|
<ClCompile Include="error_info.cpp">
|
||||||
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
|
||||||
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="hooks.cpp" />
|
<ClCompile Include="hooks.cpp" />
|
||||||
<ClCompile Include="logging.cpp">
|
<ClCompile Include="logging.cpp">
|
||||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
|
||||||
|
|
@ -176,6 +180,7 @@
|
||||||
<ClInclude Include="..\lib\TsudaKageyu-minhook\src\trampoline.h" />
|
<ClInclude Include="..\lib\TsudaKageyu-minhook\src\trampoline.h" />
|
||||||
<ClInclude Include="crashhandler_shared.h" />
|
<ClInclude Include="crashhandler_shared.h" />
|
||||||
<ClInclude Include="DalamudStartInfo.h" />
|
<ClInclude Include="DalamudStartInfo.h" />
|
||||||
|
<ClInclude Include="error_info.h" />
|
||||||
<ClInclude Include="hooks.h" />
|
<ClInclude Include="hooks.h" />
|
||||||
<ClInclude Include="logging.h" />
|
<ClInclude Include="logging.h" />
|
||||||
<ClInclude Include="ntdll.h" />
|
<ClInclude Include="ntdll.h" />
|
||||||
|
|
@ -206,4 +211,4 @@
|
||||||
<Copy SourceFiles="$(OutDir)$(TargetName).pdb" DestinationFolder="..\bin\$(Configuration)\" />
|
<Copy SourceFiles="$(OutDir)$(TargetName).pdb" DestinationFolder="..\bin\$(Configuration)\" />
|
||||||
<Copy SourceFiles="$(OutDir)nethost.dll" DestinationFolder="..\bin\$(Configuration)\" />
|
<Copy SourceFiles="$(OutDir)nethost.dll" DestinationFolder="..\bin\$(Configuration)\" />
|
||||||
</Target>
|
</Target>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
@ -76,6 +76,9 @@
|
||||||
<ClCompile Include="ntdll.cpp">
|
<ClCompile Include="ntdll.cpp">
|
||||||
<Filter>Dalamud.Boot DLL</Filter>
|
<Filter>Dalamud.Boot DLL</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="error_info.cpp">
|
||||||
|
<Filter>Common Boot</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="..\lib\CoreCLR\CoreCLR.h">
|
<ClInclude Include="..\lib\CoreCLR\CoreCLR.h">
|
||||||
|
|
@ -146,6 +149,9 @@
|
||||||
<ClInclude Include="ntdll.h">
|
<ClInclude Include="ntdll.h">
|
||||||
<Filter>Dalamud.Boot DLL</Filter>
|
<Filter>Dalamud.Boot DLL</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="error_info.h">
|
||||||
|
<Filter>Common Boot</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ResourceCompile Include="Dalamud.Boot.rc" />
|
<ResourceCompile Include="Dalamud.Boot.rc" />
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,12 @@
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "veh.h"
|
#include "veh.h"
|
||||||
#include "xivfixes.h"
|
#include "xivfixes.h"
|
||||||
|
#include "resource.h"
|
||||||
|
|
||||||
HMODULE g_hModule;
|
HMODULE g_hModule;
|
||||||
HINSTANCE g_hGameInstance = GetModuleHandleW(nullptr);
|
HINSTANCE g_hGameInstance = GetModuleHandleW(nullptr);
|
||||||
|
|
||||||
void CheckMsvcrtVersion() {
|
static void CheckMsvcrtVersion() {
|
||||||
// Commit introducing inline mutex ctor: tagged vs-2022-17.14 (2024-06-18)
|
// Commit introducing inline mutex ctor: tagged vs-2022-17.14 (2024-06-18)
|
||||||
// - https://github.com/microsoft/STL/commit/22a88260db4d754bbc067e2002430144d6ec5391
|
// - https://github.com/microsoft/STL/commit/22a88260db4d754bbc067e2002430144d6ec5391
|
||||||
// MSVC Redist versions:
|
// MSVC Redist versions:
|
||||||
|
|
@ -28,67 +29,102 @@ void CheckMsvcrtVersion() {
|
||||||
| (static_cast<uint64_t>(RequiredMsvcrtVersionComponents[2]) << 16)
|
| (static_cast<uint64_t>(RequiredMsvcrtVersionComponents[2]) << 16)
|
||||||
| (static_cast<uint64_t>(RequiredMsvcrtVersionComponents[3]) << 0);
|
| (static_cast<uint64_t>(RequiredMsvcrtVersionComponents[3]) << 0);
|
||||||
|
|
||||||
#ifdef _DEBUG
|
|
||||||
constexpr const wchar_t* RuntimeDllNames[] = {
|
constexpr const wchar_t* RuntimeDllNames[] = {
|
||||||
|
#ifdef _DEBUG
|
||||||
L"msvcp140d.dll",
|
L"msvcp140d.dll",
|
||||||
L"vcruntime140d.dll",
|
L"vcruntime140d.dll",
|
||||||
L"vcruntime140_1d.dll",
|
L"vcruntime140_1d.dll",
|
||||||
};
|
|
||||||
#else
|
#else
|
||||||
constexpr const wchar_t* RuntimeDllNames[] = {
|
|
||||||
L"msvcp140.dll",
|
L"msvcp140.dll",
|
||||||
L"vcruntime140.dll",
|
L"vcruntime140.dll",
|
||||||
L"vcruntime140_1.dll",
|
L"vcruntime140_1.dll",
|
||||||
};
|
|
||||||
#endif
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
uint64_t lowestVersion = 0;
|
uint64_t lowestVersion = 0;
|
||||||
for (const auto& runtimeDllName : RuntimeDllNames) {
|
for (const auto& runtimeDllName : RuntimeDllNames) {
|
||||||
const utils::loaded_module mod(GetModuleHandleW(runtimeDllName));
|
const utils::loaded_module mod(GetModuleHandleW(runtimeDllName));
|
||||||
if (!mod) {
|
if (!mod) {
|
||||||
logging::E("Runtime DLL not found: {}", runtimeDllName);
|
logging::E("MSVCRT DLL not found: {}", runtimeDllName);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
const auto path = mod.path()
|
||||||
const auto& versionFull = mod.get_file_version();
|
.transform([](const auto& p) { return p.wstring(); })
|
||||||
logging::I("Runtime DLL {} has version {}.", runtimeDllName, utils::format_file_version(versionFull));
|
.value_or(runtimeDllName);
|
||||||
|
|
||||||
const auto version = (static_cast<uint64_t>(versionFull.dwFileVersionMS) << 32) |
|
if (const auto versionResult = mod.get_file_version()) {
|
||||||
static_cast<uint64_t>(versionFull.dwFileVersionLS);
|
const auto& versionFull = versionResult->get();
|
||||||
|
logging::I("MSVCRT DLL {} has version {}.", path, utils::format_file_version(versionFull));
|
||||||
|
|
||||||
|
const auto version = 0ULL |
|
||||||
|
(static_cast<uint64_t>(versionFull.dwFileVersionMS) << 32) |
|
||||||
|
(static_cast<uint64_t>(versionFull.dwFileVersionLS) << 0);
|
||||||
|
|
||||||
if (version < RequiredMsvcrtVersion && (lowestVersion == 0 || lowestVersion > version))
|
if (version < RequiredMsvcrtVersion && (lowestVersion == 0 || lowestVersion > version))
|
||||||
lowestVersion = version;
|
lowestVersion = version;
|
||||||
} catch (const std::exception& e) {
|
} else {
|
||||||
logging::E("Failed to detect Runtime DLL version for {}: {}", runtimeDllName, e.what());
|
logging::E("Failed to detect MSVCRT DLL version for {}: {}", path, versionResult.error().describe());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lowestVersion) {
|
if (!lowestVersion)
|
||||||
switch (MessageBoxW(
|
return;
|
||||||
nullptr,
|
|
||||||
L"Microsoft Visual C++ Redistributable should be updated, or Dalamud may not work as expected."
|
enum IdTaskDialogAction {
|
||||||
L" Do you want to download and install the latest version from Microsoft?"
|
IdTaskDialogActionOpenDownload = 101,
|
||||||
L"\n"
|
IdTaskDialogActionIgnore,
|
||||||
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"
|
const TASKDIALOG_BUTTON buttons[]{
|
||||||
L"\nClick \"X64\" from the table in the download page, regardless of what CPU you have.",
|
{IdTaskDialogActionOpenDownload, MAKEINTRESOURCEW(IDS_MSVCRT_ACTION_OPENDOWNLOAD)},
|
||||||
L"Dalamud",
|
{IdTaskDialogActionIgnore, MAKEINTRESOURCEW(IDS_MSVCRT_ACTION_IGNORE)},
|
||||||
MB_YESNO | MB_ICONWARNING)) {
|
};
|
||||||
case IDYES:
|
|
||||||
ShellExecuteW(
|
const WORD lowestVersionComponents[]{
|
||||||
nullptr,
|
static_cast<WORD>(lowestVersion >> 48),
|
||||||
L"open",
|
static_cast<WORD>(lowestVersion >> 32),
|
||||||
L"https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-170#latest-microsoft-visual-c-redistributable-version",
|
static_cast<WORD>(lowestVersion >> 16),
|
||||||
nullptr,
|
static_cast<WORD>(lowestVersion >> 0),
|
||||||
nullptr,
|
};
|
||||||
SW_SHOW);
|
|
||||||
ExitProcess(0);
|
const auto dialogContent = std::vformat(
|
||||||
break;
|
utils::get_string_resource(IDS_MSVCRT_DIALOG_CONTENT),
|
||||||
case IDNO:
|
std::make_wformat_args(
|
||||||
break;
|
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)
|
if (g_startInfo.BootShowConsole)
|
||||||
ConsoleSetup(L"Dalamud Boot");
|
ConsoleSetup(utils::get_string_resource(IDS_APPNAME).c_str());
|
||||||
|
|
||||||
logging::update_dll_load_status(true);
|
logging::update_dll_load_status(true);
|
||||||
|
|
||||||
|
|
@ -240,7 +276,7 @@ HRESULT WINAPI InitializeImpl(LPVOID lpParam, HANDLE hMainThreadContinue) {
|
||||||
|
|
||||||
if (minHookLoaded) {
|
if (minHookLoaded) {
|
||||||
logging::I("Applying fixes...");
|
logging::I("Applying fixes...");
|
||||||
xivfixes::apply_all(true);
|
std::thread([] { xivfixes::apply_all(true); }).join();
|
||||||
logging::I("Fixes OK");
|
logging::I("Fixes OK");
|
||||||
} else {
|
} else {
|
||||||
logging::W("Skipping fixes, as MinHook has failed to load.");
|
logging::W("Skipping fixes, as MinHook has failed to load.");
|
||||||
|
|
@ -251,11 +287,14 @@ HRESULT WINAPI InitializeImpl(LPVOID lpParam, HANDLE hMainThreadContinue) {
|
||||||
while (!IsDebuggerPresent())
|
while (!IsDebuggerPresent())
|
||||||
Sleep(100);
|
Sleep(100);
|
||||||
logging::I("Debugger attached.");
|
logging::I("Debugger attached.");
|
||||||
|
__debugbreak();
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto fs_module_path = utils::get_module_path(g_hModule);
|
const auto fs_module_path = utils::loaded_module(g_hModule).path();
|
||||||
const auto runtimeconfig_path = std::filesystem::path(fs_module_path).replace_filename(L"Dalamud.runtimeconfig.json").wstring();
|
if (!fs_module_path)
|
||||||
const auto module_path = std::filesystem::path(fs_module_path).replace_filename(L"Dalamud.dll").wstring();
|
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 ========================================= //
|
// ============================== CLR ========================================= //
|
||||||
|
|
||||||
|
|
|
||||||
26
Dalamud.Boot/error_info.cpp
Normal file
26
Dalamud.Boot/error_info.cpp
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
#include "error_info.h"
|
||||||
|
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <Windows.h>
|
||||||
|
|
||||||
|
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)";
|
||||||
|
}
|
||||||
|
}
|
||||||
42
Dalamud.Boot/error_info.h
Normal file
42
Dalamud.Boot/error_info.h
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <expected>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
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<typename T>
|
||||||
|
using DalamudExpected = std::expected<
|
||||||
|
std::conditional_t<
|
||||||
|
std::is_reference_v<T>,
|
||||||
|
std::reference_wrapper<std::remove_reference_t<T>>,
|
||||||
|
T
|
||||||
|
>,
|
||||||
|
DalamudBootError
|
||||||
|
>;
|
||||||
|
|
||||||
|
using DalamudUnexpected = std::unexpected<DalamudBootError>;
|
||||||
|
|
@ -82,37 +82,14 @@ void hooks::getprocaddress_singleton_import_hook::initialize() {
|
||||||
s_dllChanged = 1;
|
s_dllChanged = 1;
|
||||||
if (notiReason == LDR_DLL_NOTIFICATION_REASON_LOADED) {
|
if (notiReason == LDR_DLL_NOTIFICATION_REASON_LOADED) {
|
||||||
const auto dllName = unicode::convert<std::string>(pData->Loaded.FullDllName->Buffer);
|
const auto dllName = unicode::convert<std::string>(pData->Loaded.FullDllName->Buffer);
|
||||||
std::wstring version = L"<unknown>";
|
|
||||||
std::wstring description = L"<unknown>";
|
|
||||||
DWORD versionSize = GetFileVersionInfoSizeA(dllName.c_str(), NULL);
|
|
||||||
|
|
||||||
if (versionSize > 0) {
|
utils::loaded_module mod(pData->Loaded.DllBase);
|
||||||
std::vector<BYTE> versionData(versionSize);
|
const auto version = mod.get_file_version()
|
||||||
if (GetFileVersionInfoA(dllName.c_str(), 0, versionSize, versionData.data())) {
|
.transform([](const auto& v) { return utils::format_file_version(v.get()); })
|
||||||
struct LANGANDCODEPAGE {
|
.value_or(L"<unknown>");
|
||||||
WORD wLanguage;
|
|
||||||
WORD wCodePage;
|
|
||||||
} *translate = nullptr;
|
|
||||||
|
|
||||||
UINT uLen = 0;
|
const auto description = mod.get_description()
|
||||||
LPVOID lpBuffer;
|
.value_or(L"<unknown>");
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
logging::I(R"({} "{}" ("{}" ver {}) has been loaded at 0x{:X} ~ 0x{:X} (0x{:X}); finding import table items to hook.)",
|
logging::I(R"({} "{}" ("{}" ver {}) has been loaded at 0x{:X} ~ 0x{:X} (0x{:X}); finding import table items to hook.)",
|
||||||
LogTag, dllName, description, version,
|
LogTag, dllName, description, version,
|
||||||
|
|
@ -142,7 +119,9 @@ void hooks::getprocaddress_singleton_import_hook::hook_module(const utils::loade
|
||||||
if (mod.is_current_process())
|
if (mod.is_current_process())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const auto path = unicode::convert<std::string>(mod.path().wstring());
|
const auto path = mod.path()
|
||||||
|
.transform([](const auto& p) { return unicode::convert<std::string>(p.wstring()); })
|
||||||
|
.value_or("<unknown>");
|
||||||
|
|
||||||
for (const auto& [hModule, targetFns] : m_targetFns) {
|
for (const auto& [hModule, targetFns] : m_targetFns) {
|
||||||
for (const auto& [targetFn, pfnThunk] : 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)) {
|
if (void* pGetProcAddressImport; mod.find_imported_function_pointer(dllName.c_str(), targetFn.c_str(), 0, pGetProcAddressImport)) {
|
||||||
auto& hook = m_hooks[hModule][targetFn][mod];
|
auto& hook = m_hooks[hModule][targetFn][mod];
|
||||||
if (!hook) {
|
if (!hook) {
|
||||||
logging::I("{} Hooking {}!{} imported by {}", LogTag, dllName, targetFn, unicode::convert<std::string>(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<void**>(pGetProcAddressImport), pfnThunk);
|
hook.emplace(std::format("getprocaddress_singleton_import_hook::hook_module({}!{})", dllName, targetFn), static_cast<void**>(pGetProcAddressImport), pfnThunk);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,7 @@
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <span>
|
#include <span>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
// https://www.akenotsuki.com/misc/srell/en/
|
// https://www.akenotsuki.com/misc/srell/en/
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,23 @@
|
||||||
// Used by Dalamud.Boot.rc
|
// Used by Dalamud.Boot.rc
|
||||||
//
|
//
|
||||||
#define IDI_ICON1 101
|
#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
|
// Next default values for new objects
|
||||||
//
|
//
|
||||||
#ifdef APSTUDIO_INVOKED
|
#ifdef APSTUDIO_INVOKED
|
||||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
#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_COMMAND_VALUE 40001
|
||||||
#define _APS_NEXT_CONTROL_VALUE 1001
|
#define _APS_NEXT_CONTROL_VALUE 1001
|
||||||
#define _APS_NEXT_SYMED_VALUE 101
|
#define _APS_NEXT_SYMED_VALUE 101
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
#include "resource.h"
|
||||||
|
|
||||||
HRESULT WINAPI InitializeImpl(LPVOID lpParam, HANDLE hMainThreadContinue);
|
HRESULT WINAPI InitializeImpl(LPVOID lpParam, HANDLE hMainThreadContinue);
|
||||||
|
|
||||||
|
|
@ -379,12 +380,50 @@ extern "C" void WINAPI RewrittenEntryPoint_AdjustedStack(RewrittenEntryPointPara
|
||||||
auto desc = err.Description();
|
auto desc = err.Description();
|
||||||
if (desc.length() == 0)
|
if (desc.length() == 0)
|
||||||
desc = err.ErrorMessage();
|
desc = err.ErrorMessage();
|
||||||
if (MessageBoxW(nullptr, std::format(
|
|
||||||
L"Failed to load Dalamud. Load game without Dalamud(yes) or abort(no)?\n\n{}\n{}",
|
enum IdTaskDialogAction {
|
||||||
last_operation,
|
IdTaskDialogActionAbort = 101,
|
||||||
desc.GetBSTR()).c_str(),
|
IdTaskDialogActionContinue,
|
||||||
L"Dalamud.Boot", MB_OK | MB_YESNO) == IDNO)
|
};
|
||||||
ExitProcess(-1);
|
|
||||||
|
const TASKDIALOG_BUTTON buttons[]{
|
||||||
|
{IdTaskDialogActionAbort, MAKEINTRESOURCEW(IDS_INITIALIZEFAIL_ACTION_ABORT)},
|
||||||
|
{IdTaskDialogActionContinue, MAKEINTRESOURCEW(IDS_INITIALIZEFAIL_ACTION_CONTINUE)},
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto hru32 = static_cast<uint32_t>(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) {
|
if (hMainThreadContinue) {
|
||||||
CloseHandle(hMainThreadContinue);
|
CloseHandle(hMainThreadContinue);
|
||||||
hMainThreadContinue = nullptr;
|
hMainThreadContinue = nullptr;
|
||||||
|
|
|
||||||
|
|
@ -3,22 +3,27 @@
|
||||||
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
std::filesystem::path utils::loaded_module::path() const {
|
DalamudExpected<std::filesystem::path> utils::loaded_module::path() const {
|
||||||
std::wstring buf(MAX_PATH, L'\0');
|
for (std::wstring buf(MAX_PATH, L'\0');; buf.resize(buf.size() * 2)) {
|
||||||
for (;;) {
|
if (const auto len = GetModuleFileNameW(m_hModule, &buf[0], static_cast<DWORD>(buf.size()));
|
||||||
if (const auto len = GetModuleFileNameExW(GetCurrentProcess(), m_hModule, &buf[0], static_cast<DWORD>(buf.size())); len != buf.size()) {
|
len != buf.size()) {
|
||||||
if (buf.empty())
|
if (!len) {
|
||||||
throw std::runtime_error(std::format("Failed to resolve module path: Win32 error {}", GetLastError()));
|
return DalamudUnexpected(
|
||||||
|
std::in_place,
|
||||||
|
DalamudBootErrorDescription::ModulePathResolutionFail,
|
||||||
|
HRESULT_FROM_WIN32(GetLastError()));
|
||||||
|
}
|
||||||
|
|
||||||
buf.resize(len);
|
buf.resize(len);
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buf.size() * 2 < PATHCCH_MAX_CCH)
|
if (buf.size() > PATHCCH_MAX_CCH) {
|
||||||
buf.resize(buf.size() * 2);
|
return DalamudUnexpected(
|
||||||
else if (auto p = std::filesystem::path(buf); exists(p))
|
std::in_place,
|
||||||
return p;
|
DalamudBootErrorDescription::ModulePathResolutionFail,
|
||||||
else
|
E_OUTOFMEMORY);
|
||||||
throw std::runtime_error("Failed to resolve module path: no amount of buffer size would fit the data");
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -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 : "<unnamed>", hintOrOrdinal));
|
throw std::runtime_error(std::format("Failed to find import for {}!{} ({}).", pcszDllName, pcszFunctionName ? pcszFunctionName : "<unnamed>", hintOrOrdinal));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<std::remove_pointer_t<HGLOBAL>, decltype(&FreeResource)> utils::loaded_module::get_resource(LPCWSTR lpName, LPCWSTR lpType) const {
|
DalamudExpected<std::unique_ptr<std::remove_pointer_t<HGLOBAL>, decltype(&FreeResource)>> utils::loaded_module::get_resource(LPCWSTR lpName, LPCWSTR lpType) const {
|
||||||
const auto hres = FindResourceW(m_hModule, lpName, lpType);
|
const auto hres = FindResourceW(m_hModule, lpName, lpType);
|
||||||
if (!hres)
|
if (!hres)
|
||||||
throw std::runtime_error("No such resource");
|
return DalamudUnexpected(std::in_place, DalamudBootErrorDescription::ModuleResourceLoadFail, GetLastError());
|
||||||
|
|
||||||
const auto hRes = LoadResource(m_hModule, hres);
|
const auto hRes = LoadResource(m_hModule, hres);
|
||||||
if (!hRes)
|
if (!hRes)
|
||||||
throw std::runtime_error("LoadResource failure");
|
return DalamudUnexpected(std::in_place, DalamudBootErrorDescription::ModuleResourceLoadFail, GetLastError());
|
||||||
|
|
||||||
return {hRes, &FreeResource};
|
return std::unique_ptr<std::remove_pointer_t<HGLOBAL>, decltype(&FreeResource)>(hRes, &FreeResource);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::wstring utils::loaded_module::get_description() const {
|
DalamudExpected<std::wstring> utils::loaded_module::get_description() const {
|
||||||
const auto rsrc = get_resource(MAKEINTRESOURCE(VS_VERSION_INFO), RT_VERSION);
|
auto rsrc = get_resource(MAKEINTRESOURCE(VS_VERSION_INFO), RT_VERSION);
|
||||||
const auto pBlock = LockResource(rsrc.get());
|
if (!rsrc)
|
||||||
|
return DalamudUnexpected(std::move(rsrc.error()));
|
||||||
|
|
||||||
|
const auto pBlock = LockResource(rsrc->get());
|
||||||
|
|
||||||
struct LANGANDCODEPAGE {
|
struct LANGANDCODEPAGE {
|
||||||
WORD wLanguage;
|
WORD wLanguage;
|
||||||
|
|
@ -166,44 +174,65 @@ std::wstring utils::loaded_module::get_description() const {
|
||||||
} * lpTranslate;
|
} * lpTranslate;
|
||||||
UINT cbTranslate;
|
UINT cbTranslate;
|
||||||
if (!VerQueryValueW(pBlock,
|
if (!VerQueryValueW(pBlock,
|
||||||
TEXT("\\VarFileInfo\\Translation"),
|
L"\\VarFileInfo\\Translation",
|
||||||
reinterpret_cast<LPVOID*>(&lpTranslate),
|
reinterpret_cast<LPVOID*>(&lpTranslate),
|
||||||
&cbTranslate)) {
|
&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++) {
|
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;
|
wchar_t* buf = nullptr;
|
||||||
UINT size = 0;
|
UINT size = 0;
|
||||||
if (!VerQueryValueW(pBlock,
|
if (!VerQueryValueW(pBlock, subblockNameBuf, reinterpret_cast<LPVOID*>(&buf), &size))
|
||||||
std::format(L"\\StringFileInfo\\{:04x}{:04x}\\FileDescription",
|
|
||||||
lpTranslate[i].wLanguage,
|
|
||||||
lpTranslate[i].wCodePage).c_str(),
|
|
||||||
reinterpret_cast<LPVOID*>(&buf),
|
|
||||||
&size)) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
auto currName = std::wstring_view(buf, size);
|
auto currName = std::wstring_view(buf, size);
|
||||||
while (!currName.empty() && currName.back() == L'\0')
|
if (const auto p = currName.find(L'\0'); p != std::string::npos)
|
||||||
currName = currName.substr(0, currName.size() - 1);
|
currName = currName.substr(0, p);
|
||||||
if (currName.empty())
|
if (currName.empty())
|
||||||
continue;
|
continue;
|
||||||
return std::wstring(currName);
|
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 {
|
std::expected<std::reference_wrapper<const VS_FIXEDFILEINFO>, DalamudBootError> utils::loaded_module::get_file_version() const {
|
||||||
const auto rsrc = get_resource(MAKEINTRESOURCE(VS_VERSION_INFO), RT_VERSION);
|
auto rsrc = get_resource(MAKEINTRESOURCE(VS_VERSION_INFO), RT_VERSION);
|
||||||
const auto pBlock = LockResource(rsrc.get());
|
if (!rsrc)
|
||||||
|
return DalamudUnexpected(std::move(rsrc.error()));
|
||||||
|
|
||||||
|
const auto pBlock = LockResource(rsrc->get());
|
||||||
UINT size = 0;
|
UINT size = 0;
|
||||||
LPVOID lpBuffer = nullptr;
|
LPVOID lpBuffer = nullptr;
|
||||||
if (!VerQueryValueW(pBlock, L"\\", &lpBuffer, &size))
|
if (!VerQueryValueW(pBlock, L"\\", &lpBuffer, &size)) {
|
||||||
throw std::runtime_error("Failed to query version information.");
|
return std::unexpected<DalamudBootError>(
|
||||||
|
std::in_place,
|
||||||
|
DalamudBootErrorDescription::ModuleResourceVersionReadFail,
|
||||||
|
HRESULT_FROM_WIN32(GetLastError()));
|
||||||
|
}
|
||||||
|
|
||||||
const VS_FIXEDFILEINFO& versionInfo = *static_cast<const VS_FIXEDFILEINFO*>(lpBuffer);
|
const VS_FIXEDFILEINFO& versionInfo = *static_cast<const VS_FIXEDFILEINFO*>(lpBuffer);
|
||||||
if (versionInfo.dwSignature != 0xfeef04bd)
|
if (versionInfo.dwSignature != 0xfeef04bd) {
|
||||||
throw std::runtime_error("Invalid version info found.");
|
return std::unexpected<DalamudBootError>(
|
||||||
|
std::in_place,
|
||||||
|
DalamudBootErrorDescription::ModuleResourceVersionSignatureFail);
|
||||||
|
}
|
||||||
|
|
||||||
return versionInfo;
|
return versionInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -589,17 +618,10 @@ bool utils::is_running_on_wine() {
|
||||||
return g_startInfo.Platform != "WINDOWS";
|
return g_startInfo.Platform != "WINDOWS";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::filesystem::path utils::get_module_path(HMODULE hModule) {
|
std::wstring utils::get_string_resource(uint32_t resId) {
|
||||||
std::wstring buf(MAX_PATH, L'\0');
|
LPCWSTR pstr;
|
||||||
while (true) {
|
const auto len = LoadStringW(g_hModule, resId, reinterpret_cast<LPWSTR>(&pstr), 0);
|
||||||
if (const auto res = GetModuleFileNameW(hModule, &buf[0], static_cast<int>(buf.size())); !res)
|
return std::wstring(pstr, len);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HWND utils::try_find_game_window() {
|
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);
|
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<decltype(&SetThreadDpiAwarenessContext)>(
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <expected>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <span>
|
#include <span>
|
||||||
|
|
@ -7,6 +8,7 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "error_info.h"
|
||||||
#include "unicode.h"
|
#include "unicode.h"
|
||||||
|
|
||||||
namespace utils {
|
namespace utils {
|
||||||
|
|
@ -18,7 +20,7 @@ namespace utils {
|
||||||
loaded_module(void* hModule) : m_hModule(reinterpret_cast<HMODULE>(hModule)) {}
|
loaded_module(void* hModule) : m_hModule(reinterpret_cast<HMODULE>(hModule)) {}
|
||||||
loaded_module(size_t hModule) : m_hModule(reinterpret_cast<HMODULE>(hModule)) {}
|
loaded_module(size_t hModule) : m_hModule(reinterpret_cast<HMODULE>(hModule)) {}
|
||||||
|
|
||||||
std::filesystem::path path() const;
|
DalamudExpected<std::filesystem::path> path() const;
|
||||||
|
|
||||||
bool is_current_process() const { return m_hModule == GetModuleHandleW(nullptr); }
|
bool is_current_process() const { return m_hModule == GetModuleHandleW(nullptr); }
|
||||||
bool owns_address(const void* pAddress) const;
|
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;
|
void* get_imported_function_pointer(const char* pcszDllName, const char* pcszFunctionName, uint32_t hintOrOrdinal) const;
|
||||||
template<typename TFn> TFn** get_imported_function_pointer(const char* pcszDllName, const char* pcszFunctionName, uint32_t hintOrOrdinal) { return reinterpret_cast<TFn**>(get_imported_function_pointer(pcszDllName, pcszFunctionName, hintOrOrdinal)); }
|
template<typename TFn> TFn** get_imported_function_pointer(const char* pcszDllName, const char* pcszFunctionName, uint32_t hintOrOrdinal) { return reinterpret_cast<TFn**>(get_imported_function_pointer(pcszDllName, pcszFunctionName, hintOrOrdinal)); }
|
||||||
|
|
||||||
[[nodiscard]] std::unique_ptr<std::remove_pointer_t<HGLOBAL>, decltype(&FreeResource)> get_resource(LPCWSTR lpName, LPCWSTR lpType) const;
|
[[nodiscard]] DalamudExpected<std::unique_ptr<std::remove_pointer_t<HGLOBAL>, decltype(&FreeResource)>> get_resource(LPCWSTR lpName, LPCWSTR lpType) const;
|
||||||
[[nodiscard]] std::wstring get_description() const;
|
[[nodiscard]] DalamudExpected<std::wstring> get_description() const;
|
||||||
[[nodiscard]] const VS_FIXEDFILEINFO& get_file_version() const;
|
[[nodiscard]] DalamudExpected<const VS_FIXEDFILEINFO&> get_file_version() const;
|
||||||
|
|
||||||
static loaded_module current_process();
|
static loaded_module current_process();
|
||||||
static std::vector<loaded_module> all_modules();
|
static std::vector<loaded_module> all_modules();
|
||||||
|
|
@ -268,7 +270,7 @@ namespace utils {
|
||||||
|
|
||||||
bool is_running_on_wine();
|
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.
|
/// @brief Find the game main window.
|
||||||
/// @return Handle to the game main window, or nullptr if it doesn't exist (yet).
|
/// @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 escape_shell_arg(const std::wstring& arg);
|
||||||
|
|
||||||
std::wstring format_win32_error(DWORD err);
|
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;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -102,9 +102,13 @@ bool is_ffxiv_address(const wchar_t* module_name, const DWORD64 address)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void append_injector_launch_args(std::vector<std::wstring>& args)
|
static DalamudExpected<void> append_injector_launch_args(std::vector<std::wstring>& 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) {
|
switch (g_startInfo.DalamudLoadMethod) {
|
||||||
case DalamudStartInfo::LoadMethod::Entrypoint:
|
case DalamudStartInfo::LoadMethod::Entrypoint:
|
||||||
args.emplace_back(L"--mode=entrypoint");
|
args.emplace_back(L"--mode=entrypoint");
|
||||||
|
|
@ -155,6 +159,8 @@ static void append_injector_launch_args(std::vector<std::wstring>& args)
|
||||||
args.emplace_back(szArgList[i]);
|
args.emplace_back(szArgList[i]);
|
||||||
LocalFree(szArgList);
|
LocalFree(szArgList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
LONG exception_handler(EXCEPTION_POINTERS* ex)
|
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<size_t>(hInheritableCurrentProcess)));
|
args.emplace_back(std::format(L"--process-handle={}", reinterpret_cast<size_t>(hInheritableCurrentProcess)));
|
||||||
args.emplace_back(std::format(L"--exception-info-pipe-read-handle={}", reinterpret_cast<size_t>(hReadPipeInheritable->get())));
|
args.emplace_back(std::format(L"--exception-info-pipe-read-handle={}", reinterpret_cast<size_t>(hReadPipeInheritable->get())));
|
||||||
args.emplace_back(std::format(L"--asset-directory={}", unicode::convert<std::wstring>(g_startInfo.AssetDirectory)));
|
args.emplace_back(std::format(L"--asset-directory={}", unicode::convert<std::wstring>(g_startInfo.AssetDirectory)));
|
||||||
args.emplace_back(std::format(L"--log-directory={}", g_startInfo.BootLogPath.empty()
|
if (const auto path = utils::loaded_module(g_hModule).path()) {
|
||||||
? utils::loaded_module(g_hModule).path().parent_path().wstring()
|
args.emplace_back(std::format(L"--log-directory={}", g_startInfo.BootLogPath.empty()
|
||||||
: std::filesystem::path(unicode::convert<std::wstring>(g_startInfo.BootLogPath)).parent_path().wstring()));
|
? path->parent_path().wstring()
|
||||||
|
: std::filesystem::path(unicode::convert<std::wstring>(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"--");
|
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)
|
for (const auto& arg : args)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,6 @@
|
||||||
#include "ntdll.h"
|
#include "ntdll.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
static std::span<T> assume_nonempty_span(std::span<T> 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) {
|
void xivfixes::unhook_dll(bool bApply) {
|
||||||
static const auto LogTag = "[xivfixes:unhook_dll]";
|
static const auto LogTag = "[xivfixes:unhook_dll]";
|
||||||
static const auto LogTagW = L"[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 mods = utils::loaded_module::all_modules();
|
||||||
|
|
||||||
const auto test_module = [&](size_t i, const utils::loaded_module & mod) {
|
for (size_t i = 0; i < mods.size(); i++) {
|
||||||
std::filesystem::path path;
|
const auto& mod = mods[i];
|
||||||
try {
|
const auto path = mod.path();
|
||||||
path = mod.path();
|
if (!path) {
|
||||||
std::wstring version, description;
|
logging::W(
|
||||||
try {
|
"{} [{}/{}] Module 0x{:X}: Failed to resolve path: {}",
|
||||||
version = utils::format_file_version(mod.get_file_version());
|
LogTag,
|
||||||
} catch (...) {
|
i + 1,
|
||||||
version = L"<unknown>";
|
mods.size(),
|
||||||
}
|
mod.address_int(),
|
||||||
|
path.error().describe());
|
||||||
try {
|
|
||||||
description = mod.get_description();
|
|
||||||
} catch (...) {
|
|
||||||
description = L"<unknown>";
|
|
||||||
}
|
|
||||||
|
|
||||||
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());
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto moduleName = unicode::convert<std::string>(path.filename().wstring());
|
const auto version = mod.get_file_version()
|
||||||
|
.transform([](const auto& v) { return utils::format_file_version(v.get()); })
|
||||||
|
.value_or(L"<unknown>");
|
||||||
|
|
||||||
std::vector<char> buf;
|
const auto description = mod.get_description()
|
||||||
std::string formatBuf;
|
.value_or(L"<unknown>");
|
||||||
|
|
||||||
|
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<std::string>(path->filename().wstring());
|
||||||
|
|
||||||
|
const auto& sectionHeader = mod.section_header(".text");
|
||||||
|
const auto section = mod.span_as<char>(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<void, decltype(&CloseHandle)>(hFsDllRaw, &CloseHandle);
|
||||||
|
std::vector<char> buf(section.size());
|
||||||
|
SetFilePointer(hFsDll.get(), sectionHeader.PointerToRawData, nullptr, FILE_CURRENT);
|
||||||
|
if (DWORD read{}; ReadFile(hFsDll.get(), &buf[0], static_cast<DWORD>(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<std::string>(path->filename().u8string()));
|
||||||
try {
|
try {
|
||||||
const auto& sectionHeader = mod.section_header(".text");
|
|
||||||
const auto section = assume_nonempty_span(mod.span_as<char>(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<void, decltype(CloseHandle)*>(hFsDllRaw, &CloseHandle);
|
|
||||||
|
|
||||||
buf.resize(section.size());
|
|
||||||
SetFilePointer(hFsDll.get(), sectionHeader.PointerToRawData, nullptr, FILE_CURRENT);
|
|
||||||
if (DWORD read{}; ReadFile(hFsDll.get(), &buf[0], static_cast<DWORD>(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<std::string>(path.filename().u8string()));
|
|
||||||
|
|
||||||
std::optional<utils::memory_tenderizer> tenderizer;
|
std::optional<utils::memory_tenderizer> tenderizer;
|
||||||
for (size_t i = 0, instructionLength = 1, printed = 0; i < buf.size(); i += instructionLength) {
|
std::string formatBuf;
|
||||||
if (section[i] == buf[i]) {
|
for (size_t inst = 0, instructionLength = 1, printed = 0; inst < buf.size(); inst += instructionLength) {
|
||||||
|
if (section[inst] == buf[inst]) {
|
||||||
instructionLength = 1;
|
instructionLength = 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto rva = sectionHeader.VirtualAddress + i;
|
const auto rva = sectionHeader.VirtualAddress + inst;
|
||||||
nmd_x86_instruction instruction{};
|
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;
|
instructionLength = 1;
|
||||||
if (printed < 64) {
|
if (printed < 64) {
|
||||||
logging::W("{} {}+0x{:0X}: dd {:02X}", LogTag, moduleName, rva, static_cast<uint8_t>(section[i]));
|
logging::W("{} {}+0x{:0X}: dd {:02X}", LogTag, moduleName, rva, static_cast<uint8_t>(section[inst]));
|
||||||
printed++;
|
printed++;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
instructionLength = instruction.length;
|
instructionLength = instruction.length;
|
||||||
if (printed < 64) {
|
if (printed < 64) {
|
||||||
formatBuf.resize(128);
|
formatBuf.resize(128);
|
||||||
nmd_x86_format(&instruction, &formatBuf[0], reinterpret_cast<size_t>(§ion[i]), NMD_X86_FORMAT_FLAGS_DEFAULT | NMD_X86_FORMAT_FLAGS_BYTES);
|
nmd_x86_format(&instruction, &formatBuf[0], reinterpret_cast<size_t>(§ion[inst]), NMD_X86_FORMAT_FLAGS_DEFAULT | NMD_X86_FORMAT_FLAGS_BYTES);
|
||||||
formatBuf.resize(strnlen(&formatBuf[0], formatBuf.size()));
|
formatBuf.resize(strnlen(&formatBuf[0], formatBuf.size()));
|
||||||
|
|
||||||
const auto& directory = mod.data_directory(IMAGE_DIRECTORY_ENTRY_EXPORT);
|
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<DWORD>(exportDirectory.AddressOfFunctions, exportDirectory.NumberOfFunctions);
|
const auto functions = mod.span_as<DWORD>(exportDirectory.AddressOfFunctions, exportDirectory.NumberOfFunctions);
|
||||||
|
|
||||||
std::string resolvedExportName;
|
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;
|
std::string_view name;
|
||||||
if (const char* pcszName = mod.address_as<char>(names[j]); pcszName < mod.address() || pcszName >= mod.address() + mod.image_size()) {
|
if (const char* pcszName = mod.address_as<char>(names[nameIndex]); pcszName < mod.address() || pcszName >= mod.address() + mod.image_size()) {
|
||||||
if (IsBadReadPtr(pcszName, 256)) {
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
name = std::string_view(pcszName, strnlen(pcszName, 256));
|
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()) {
|
if (ordinals[nameIndex] >= functions.size()) {
|
||||||
logging::W("{} Ordinal #{} points to function index #{} >= #{}. Skipping.", LogTag, j, ordinals[j], functions.size());
|
logging::W("{} Ordinal #{} points to function index #{} >= #{}. Skipping.", LogTag, nameIndex, ordinals[nameIndex], functions.size());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto rva = functions[ordinals[j]];
|
const auto rva = functions[ordinals[nameIndex]];
|
||||||
if (rva == §ion[i] - mod.address()) {
|
if (rva == §ion[inst] - mod.address()) {
|
||||||
resolvedExportName = std::format("[export:{}]", name);
|
resolvedExportName = std::format("[export:{}]", name);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -135,7 +142,7 @@ void xivfixes::unhook_dll(bool bApply) {
|
||||||
if (doRestore) {
|
if (doRestore) {
|
||||||
if (!tenderizer)
|
if (!tenderizer)
|
||||||
tenderizer.emplace(section, PAGE_EXECUTE_READWRITE);
|
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) {
|
} catch (const std::exception& e) {
|
||||||
logging::W("{} Error: {}", LogTag, e.what());
|
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* ();
|
using TFnGetInputDeviceManager = void* ();
|
||||||
|
|
@ -294,13 +287,11 @@ static bool is_xivalex(const std::filesystem::path& dllPath) {
|
||||||
static bool is_openprocess_already_dealt_with() {
|
static bool is_openprocess_already_dealt_with() {
|
||||||
static const auto s_value = [] {
|
static const auto s_value = [] {
|
||||||
for (const auto& mod : utils::loaded_module::all_modules()) {
|
for (const auto& mod : utils::loaded_module::all_modules()) {
|
||||||
try {
|
const auto path = mod.path().value_or({});
|
||||||
if (is_xivalex(mod.path()))
|
if (path.empty())
|
||||||
return true;
|
continue;
|
||||||
|
if (is_xivalex(path))
|
||||||
} catch (...) {
|
return true;
|
||||||
// pass
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}();
|
}();
|
||||||
|
|
@ -650,43 +641,22 @@ void xivfixes::symbol_load_patches(bool bApply) {
|
||||||
|
|
||||||
void xivfixes::disable_game_debugging_protection(bool bApply) {
|
void xivfixes::disable_game_debugging_protection(bool bApply) {
|
||||||
static const char* LogTag = "[xivfixes:disable_game_debugging_protection]";
|
static const char* LogTag = "[xivfixes:disable_game_debugging_protection]";
|
||||||
static const std::vector<uint8_t> patchBytes = {
|
static std::optional<hooks::import_hook<decltype(IsDebuggerPresent)>> s_hookIsDebuggerPresent;
|
||||||
0x31, 0xC0, // XOR EAX, EAX
|
|
||||||
0x90, // NOP
|
|
||||||
0x90, // NOP
|
|
||||||
0x90, // NOP
|
|
||||||
0x90 // NOP
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!bApply)
|
if (bApply) {
|
||||||
return;
|
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")) {
|
s_hookIsDebuggerPresent.emplace("kernel32.dll!IsDebuggerPresent", "kernel32.dll", "IsDebuggerPresent", 0);
|
||||||
logging::I("{} Turned off via environment variable.", LogTag);
|
s_hookIsDebuggerPresent->set_detour([]() { return false; });
|
||||||
return;
|
logging::I("{} Enable", LogTag);
|
||||||
}
|
|
||||||
|
|
||||||
// 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<void*>(static_cast<const void*>(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<uintptr_t>(address));
|
|
||||||
} else {
|
} else {
|
||||||
logging::E("{} Failed to change memory protection.", LogTag);
|
if (s_hookIsDebuggerPresent) {
|
||||||
|
logging::I("{} Disable", LogTag);
|
||||||
|
s_hookIsDebuggerPresent.reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@
|
||||||
<WarningLevel>Level3</WarningLevel>
|
<WarningLevel>Level3</WarningLevel>
|
||||||
<SDLCheck>true</SDLCheck>
|
<SDLCheck>true</SDLCheck>
|
||||||
<ConformanceMode>true</ConformanceMode>
|
<ConformanceMode>true</ConformanceMode>
|
||||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
<LanguageStandard>stdcpp23</LanguageStandard>
|
||||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||||
<PreprocessorDefinitions>CPPDLLTEMPLATE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>CPPDLLTEMPLATE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
|
@ -55,7 +55,7 @@
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
<IntrinsicFunctions>false</IntrinsicFunctions>
|
<IntrinsicFunctions>false</IntrinsicFunctions>
|
||||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||||
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
|
|
@ -67,7 +67,7 @@
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||||
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
|
|
@ -108,4 +108,4 @@
|
||||||
<Delete Files="$(OutDir)$(TargetName).lib" />
|
<Delete Files="$(OutDir)$(TargetName).lib" />
|
||||||
<Delete Files="$(OutDir)$(TargetName).exp" />
|
<Delete Files="$(OutDir)$(TargetName).exp" />
|
||||||
</Target>
|
</Target>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
@ -635,29 +635,6 @@ internal partial class InterfaceManager : IInternalDisposableService
|
||||||
Service<InterfaceManagerWithScene>.ProvideException(ex);
|
Service<InterfaceManagerWithScene>.ProvideException(ex);
|
||||||
Log.Error(ex, "Could not load ImGui dependencies.");
|
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);
|
Environment.Exit(-1);
|
||||||
|
|
||||||
// Doesn't reach here, but to make the compiler not complain
|
// Doesn't reach here, but to make the compiler not complain
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue