diff --git a/Dalamud.Boot/dllmain.cpp b/Dalamud.Boot/dllmain.cpp index 5e73962ec..1a8d70be9 100644 --- a/Dalamud.Boot/dllmain.cpp +++ b/Dalamud.Boot/dllmain.cpp @@ -13,9 +13,86 @@ HMODULE g_hModule; HINSTANCE g_hGameInstance = GetModuleHandleW(nullptr); +void CheckMsvcrtVersion() { + constexpr WORD RequiredMsvcrtVersionComponents[] = {14, 40, 33816, 0}; + constexpr auto RequiredMsvcrtVersion = 0ULL + | (static_cast(RequiredMsvcrtVersionComponents[0]) << 48) + | (static_cast(RequiredMsvcrtVersionComponents[1]) << 32) + | (static_cast(RequiredMsvcrtVersionComponents[2]) << 16) + | (static_cast(RequiredMsvcrtVersionComponents[3]) << 0); + +#ifdef _DEBUG + constexpr const wchar_t* RuntimeDllNames[] = { + 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); + continue; + } + + const auto& versionFull = mod.get_file_version(); + logging::I("Runtime DLL {} has version {}.", runtimeDllName, utils::format_file_version(versionFull)); + + const auto version = (static_cast(versionFull.dwFileVersionMS) << 32) | + static_cast(versionFull.dwFileVersionLS); + + // Commit introducing inline mutex ctor: tagged vs-2022-17.14 (2024-06-18) + // - https://github.com/microsoft/STL/commit/22a88260db4d754bbc067e2002430144d6ec5391 + // MSVC Redist versions: + // - https://github.com/abbodi1406/vcredist/blob/master/source_links/README.md + // - 14.40.33810.0 dsig 2024-04-28 + // - 14.40.33816.0 dsig 2024-09-11 + + if (version < RequiredMsvcrtVersion && (lowestVersion == 0 || lowestVersion > version)) + lowestVersion = version; + } + + 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; + } + } +} + HRESULT WINAPI InitializeImpl(LPVOID lpParam, HANDLE hMainThreadContinue) { g_startInfo.from_envvars(); + CheckMsvcrtVersion(); + std::string jsonParseError; try { from_json(nlohmann::json::parse(std::string_view(static_cast(lpParam))), g_startInfo); diff --git a/Dalamud.Boot/pch.h b/Dalamud.Boot/pch.h index c2194c157..e46927f76 100644 --- a/Dalamud.Boot/pch.h +++ b/Dalamud.Boot/pch.h @@ -11,6 +11,9 @@ #define WIN32_LEAN_AND_MEAN #define NOMINMAX +// https://developercommunity.visualstudio.com/t/Access-violation-with-std::mutex::lock-a/10664660 +#define _DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR + // Windows Header Files (1) #include @@ -21,6 +24,7 @@ #include #include #include +#include #include #include #include diff --git a/Dalamud.Boot/utils.cpp b/Dalamud.Boot/utils.cpp index dbfcf39ee..91be2fb31 100644 --- a/Dalamud.Boot/utils.cpp +++ b/Dalamud.Boot/utils.cpp @@ -148,7 +148,7 @@ std::unique_ptr, decltype(&FreeResource)> utils:: const auto hres = FindResourceW(m_hModule, lpName, lpType); if (!hres) throw std::runtime_error("No such resource"); - + const auto hRes = LoadResource(m_hModule, hres); if (!hRes) throw std::runtime_error("LoadResource failure"); @@ -159,7 +159,7 @@ std::unique_ptr, decltype(&FreeResource)> utils:: 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()); - + struct LANGANDCODEPAGE { WORD wLanguage; WORD wCodePage; @@ -190,11 +190,11 @@ std::wstring utils::loaded_module::get_description() const { continue; return std::wstring(currName); } - + throw std::runtime_error("Invalid version information (2)"); } -VS_FIXEDFILEINFO utils::loaded_module::get_file_version() const { +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()); UINT size = 0; @@ -353,7 +353,7 @@ const char* utils::signature_finder::result::resolve_jump_target(size_t instruct nmd_x86_instruction instruction{}; if (!nmd_x86_decode(&Match[instructionOffset], NMD_X86_MAXIMUM_INSTRUCTION_LENGTH, &instruction, NMD_X86_MODE_64, NMD_X86_DECODER_FLAGS_ALL)) throw std::runtime_error("Matched address does not have a valid assembly instruction"); - + size_t numExplicitOperands = 0; for (size_t i = 0; i < instruction.num_operands; i++) numExplicitOperands += instruction.operands[i].is_implicit ? 0 : 1; @@ -625,7 +625,7 @@ void utils::wait_for_game_window() { std::wstring utils::escape_shell_arg(const std::wstring& arg) { // https://docs.microsoft.com/en-us/archive/blogs/twistylittlepassagesallalike/everyone-quotes-command-line-arguments-the-wrong-way - + std::wstring res; if (!arg.empty() && arg.find_first_of(L" \t\n\v\"") == std::wstring::npos) { res.append(arg); diff --git a/Dalamud.Boot/utils.h b/Dalamud.Boot/utils.h index 2cdaf60a7..cbbbccee8 100644 --- a/Dalamud.Boot/utils.h +++ b/Dalamud.Boot/utils.h @@ -23,9 +23,8 @@ namespace utils { bool is_current_process() const { return m_hModule == GetModuleHandleW(nullptr); } bool owns_address(const void* pAddress) const; - operator HMODULE() const { - return m_hModule; - } + operator HMODULE() const { return m_hModule; } + operator bool() const { return m_hModule; } size_t address_int() const { return reinterpret_cast(m_hModule); } size_t image_size() const { return is_pe64() ? nt_header64().OptionalHeader.SizeOfImage : nt_header32().OptionalHeader.SizeOfImage; } @@ -60,7 +59,7 @@ namespace utils { [[nodiscard]] std::unique_ptr, decltype(&FreeResource)> get_resource(LPCWSTR lpName, LPCWSTR lpType) const; [[nodiscard]] std::wstring get_description() const; - [[nodiscard]] VS_FIXEDFILEINFO get_file_version() const; + [[nodiscard]] const VS_FIXEDFILEINFO& get_file_version() const; static loaded_module current_process(); static std::vector all_modules(); diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/UldWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/UldWidget.cs index 8d31168e2..bc12f4d28 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/UldWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/UldWidget.cs @@ -27,7 +27,7 @@ internal class UldWidget : IDataWindowWidget { // ULD styles can be hardcoded for now as they don't add new ones regularly. Can later try and find where to load these from in the game EXE. private static readonly string[] ThemeDisplayNames = ["Dark", "Light", "Classic FF", "Clear Blue"]; - private static readonly string[] ThemeBasePaths = ["ui/uld/", "ui/uld/light/", "ui/uld/third/", "ui/uld/fourth/"]; + private static readonly string[] ThemeBasePaths = ["ui/uld/", "ui/uld/img01/", "ui/uld/img02/", "ui/uld/img03/"]; // 48 8D 15 ?? ?? ?? ?? is the part of the signatures that contain the string location offset // 48 = 64 bit register prefix