From d19d0cb70d59794e406ad7a1efb4ea05ab641593 Mon Sep 17 00:00:00 2001 From: kd-11 Date: Sun, 8 Sep 2024 00:46:12 +0000 Subject: [PATCH] windows/arm64: Implement fallback OS version detection --- rpcs3/util/sysinfo.cpp | 86 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 74 insertions(+), 12 deletions(-) diff --git a/rpcs3/util/sysinfo.cpp b/rpcs3/util/sysinfo.cpp index bad59aba8f..d16e53c19f 100755 --- a/rpcs3/util/sysinfo.cpp +++ b/rpcs3/util/sysinfo.cpp @@ -68,14 +68,76 @@ namespace Darwin_ProcessInfo } #endif -#if defined(ARCH_ARM64) && defined (_WIN32) && !defined(_MSC_VER) -static inline u64 __readx18qword(int offset) +namespace utils { - u64* peb = nullptr; - __asm__ volatile("mov %0, x18" : "=r"(peb)); - return peb[offset]; -} +#ifdef _WIN32 + // Alternative way to read OS version using the registry. + static std::string get_fallback_windows_version() + { + // Some helpers for sanity + const auto read_reg_dword = [](HKEY hKey, std::string_view value_name) -> std::pair + { + DWORD val; + DWORD len = sizeof(val); + if (ERROR_SUCCESS != RegQueryValueExA(hKey, value_name.data(), nullptr, nullptr, reinterpret_cast(&val), &len)) + { + return { false, 0 }; + } + return { true, val }; + }; + + const auto read_reg_sz = [](HKEY hKey, std::string_view value_name) -> std::pair + { + char sz[256]; + DWORD sz_len = sizeof(sz); + if (ERROR_SUCCESS != RegQueryValueExA(hKey, value_name.data(), nullptr, nullptr, reinterpret_cast(sz), &sz_len)) + { + return { false, "" }; + } + + if (sz_len < sizeof(sz)) + { + // Safety, force null terminator + sz[sz_len] = 0; + } + return { true, sz }; + }; + + HKEY hKey; + if (ERROR_SUCCESS != RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", 0, KEY_READ, &hKey)) + { + return "Unknown Windows"; + } + + // Read ProductName (string), CurrentMajorVersionNumber (dword), CurrentMinorVersionNumber (dword) and CurrentBuildNumber (string) as well as CurrentVersion (string) + const auto [product_valid, product_name] = read_reg_sz(hKey, "ProductName"); + if (!product_valid) + { + return "Unknown Windows"; + } + + const auto [check_major, version_major] = read_reg_dword(hKey, "CurrentMajorVersionNumber"); + const auto [check_minor, version_minor] = read_reg_dword(hKey, "CurrentMinorVersionNumber"); + const auto [check_build_no, build_no] = read_reg_sz(hKey, "CurrentBuildNumber"); + const auto [check_nt_ver, nt_ver] = read_reg_sz(hKey, "CurrentVersion"); + + // Close the registry key + RegCloseKey(hKey); + + std::string version_id = "Unknown"; + if (check_major && check_minor && check_build_no) + { + version_id = fmt::format("%u.%u.%s", version_major, version_minor, build_no); + if (check_nt_ver) + { + version_id += " NT" + nt_ver; + } + } + + return fmt::format("Operating system: %s, Version %s", product_name, version_id); + } #endif +} bool utils::has_ssse3() { @@ -590,14 +652,9 @@ std::string utils::get_OS_version() #ifdef _WIN32 // GetVersionEx is deprecated, RtlGetVersion is kernel-mode only and AnalyticsInfo is UWP only. // So we're forced to read PEB instead to get Windows version info. It's ugly but works. - - const DWORD peb_offset = 0x60; #if defined(ARCH_X64) + const DWORD peb_offset = 0x60; const INT_PTR peb = __readgsqword(peb_offset); -#elif defined(_M_ARM64) - const INT_PTR peb = __readx18qword(peb_offset); -#endif - const DWORD version_major = *reinterpret_cast(peb + 0x118); const DWORD version_minor = *reinterpret_cast(peb + 0x11c); const WORD build = *reinterpret_cast(peb + 0x120); @@ -615,6 +672,11 @@ std::string utils::get_OS_version() fmt::append(output, "Operating system: Windows, Major: %lu, Minor: %lu, Build: %u, Service Pack: %s, Compatibility mode: %llu", version_major, version_minor, build, has_sp ? holder.data() : "none", compatibility_mode); +#else + // PEB cannot be easily accessed on ARM64, fall back to registry + static const auto s_windows_version = utils::get_fallback_windows_version(); + return s_windows_version; +#endif #elif defined (__APPLE__) const int major_version = Darwin_Version::getNSmajorVersion(); const int minor_version = Darwin_Version::getNSminorVersion();