diff --git a/rpcs3/main.cpp b/rpcs3/main.cpp index a6078bf9e5..1f6a96dfec 100644 --- a/rpcs3/main.cpp +++ b/rpcs3/main.cpp @@ -61,15 +61,6 @@ DYNAMIC_IMPORT("ntdll.dll", NtSetTimerResolution, NTSTATUS(ULONG DesiredResoluti #if defined(__APPLE__) #include -#if defined (__x86_64__) -// sysinfo_darwin.mm -namespace Darwin_Version -{ - extern int getNSmajorVersion(); - extern int getNSminorVersion(); - extern int getNSpatchVersion(); -} -#endif #endif #include "Utilities/Config.h" @@ -618,12 +609,10 @@ int main(int argc, char** argv) #endif #if defined(__APPLE__) && defined(__x86_64__) - const int osx_ver_major = Darwin_Version::getNSmajorVersion(); - const int osx_ver_minor = Darwin_Version::getNSminorVersion(); - if ((osx_ver_major == 14 && osx_ver_minor < 3) && (utils::get_cpu_brand().rfind("VirtualApple", 0) == 0)) + if (const utils::OS_version os = utils::get_OS_version(); + os.version_major == 14 && os.version_minor < 3 && (utils::get_cpu_brand().rfind("VirtualApple", 0) == 0)) { - const int osx_ver_patch = Darwin_Version::getNSpatchVersion(); - report_fatal_error(fmt::format("RPCS3 requires macOS 14.3.0 or later.\nYou're currently using macOS %i.%i.%i.\nPlease update macOS from System Settings.\n\n", osx_ver_major, osx_ver_minor, osx_ver_patch)); + report_fatal_error(fmt::format("RPCS3 requires macOS 14.3.0 or later.\nYou're currently using macOS %i.%i.%i.\nPlease update macOS from System Settings.\n\n", os.version_major, os.version_minor, os.version_patch)); } #endif @@ -659,7 +648,7 @@ int main(int argc, char** argv) // Write OS version logs::stored_message os{sys_log.always()}; - os.text = utils::get_OS_version(); + os.text = utils::get_OS_version_string(); // Write Qt version logs::stored_message qt{(strcmp(QT_VERSION_STR, qVersion()) != 0) ? sys_log.error : sys_log.notice}; diff --git a/rpcs3/rpcs3qt/update_manager.cpp b/rpcs3/rpcs3qt/update_manager.cpp index cdd7b4a0e1..9c4e2104d1 100644 --- a/rpcs3/rpcs3qt/update_manager.cpp +++ b/rpcs3/rpcs3qt/update_manager.cpp @@ -11,6 +11,7 @@ #include "Crypto/utils.h" #include "util/logs.hpp" #include "util/types.hpp" +#include "util/sysinfo.hpp" #include #include @@ -45,16 +46,6 @@ #include #endif -#if defined(__APPLE__) -// sysinfo_darwin.mm -namespace Darwin_Version -{ - extern int getNSmajorVersion(); - extern int getNSminorVersion(); - extern int getNSpatchVersion(); -} -#endif - LOG_CHANNEL(update_log, "UPDATER"); update_manager::update_manager(QObject* parent, std::shared_ptr gui_settings) @@ -116,26 +107,11 @@ void update_manager::check_for_updates(bool automatic, bool check_only, bool aut Q_EMIT signal_update_available(result_json && !m_update_message.isEmpty()); }); -#if defined(__APPLE__) - const std::string url = fmt::format("https://update.rpcs3.net/" - "?api=v3" - "&c=%s" - "&os_type=macos" - "&os_arch=" -#if defined(ARCH_X64) - "x64" -#elif defined(ARCH_ARM64) - "arm64" -#endif - "&os_version=%i.%i.%i", - rpcs3::get_commit_and_hash().second, - Darwin_Version::getNSmajorVersion(), - Darwin_Version::getNSminorVersion(), - Darwin_Version::getNSpatchVersion()); -#else - const std::string url = "https://update.rpcs3.net/?api=v2&c=" + rpcs3::get_commit_and_hash().second; -#endif - + const utils::OS_version os = utils::get_OS_version(); + + const std::string url = fmt::format("https://update.rpcs3.net/?api=v3&c=%s&os_type=%s&os_arch=%s&os_version=%i.%i.%i", + rpcs3::get_commit_and_hash().second, os.type, os.arch, os.version_major, os.version_minor, os.version_patch); + m_downloader->start(url, true, !automatic, tr("Checking For Updates"), true); } @@ -199,11 +175,25 @@ bool update_manager::handle_json(bool automatic, bool check_only, bool auto_acce #endif // Check that every bit of info we need is there - if (!latest[os].isObject() || !latest[os]["download"].isString() || !latest[os]["size"].isDouble() || !latest[os]["checksum"].isString() || !latest["version"].isString() || - !latest["datetime"].isString() || - (hash_found && (!current.isObject() || !current["version"].isString() || !current["datetime"].isString()))) + const auto check_json = [](bool cond, std::string_view msg) -> bool + { + if (cond) return true; + update_log.error("%s", msg); + return false; + }; + if (!(check_json(latest[os].isObject(), fmt::format("Node 'latest_build: %s' not found", os)) && + check_json(latest[os]["download"].isString(), fmt::format("Node 'latest_build: %s: download' not found or not a string", os)) && + check_json(latest[os]["size"].isDouble(), fmt::format("Node 'latest_build: %s: size' not found or not a double", os)) && + check_json(latest[os]["checksum"].isString(), fmt::format("Node 'latest_build: %s: checksum' not found or not a string", os)) && + check_json(latest["version"].isString(), "Node 'latest_build: version' not found or not a string") && + check_json(latest["datetime"].isString(), "Node 'latest_build: datetime' not found or not a string") + ) || + (hash_found && !( + check_json(current.isObject(), "JSON doesn't contain current_build section") && + check_json(current["version"].isString(), "Node 'current_build: datetime' not found or not a string") && + check_json(current["datetime"].isString(), "Node 'current_build: version' not found or not a string") + ))) { - update_log.error("Some information seems unavailable"); return false; } diff --git a/rpcs3/util/cpu_stats.cpp b/rpcs3/util/cpu_stats.cpp index acf965ddbc..6e69f645d2 100644 --- a/rpcs3/util/cpu_stats.cpp +++ b/rpcs3/util/cpu_stats.cpp @@ -129,20 +129,6 @@ namespace utils per_core_usage.resize(utils::get_thread_count()); std::fill(per_core_usage.begin(), per_core_usage.end(), 0.0); -#if defined(_WIN32) || defined(__linux__) - const auto string_to_number = [](const std::string& str) -> std::pair - { - std::add_pointer_t eval; - const size_t number = std::strtol(str.c_str(), &eval, 10); - - if (str.c_str() + str.size() == eval) - { - return { true, number }; - } - - return { false, 0 }; - }; - #ifdef _WIN32 if (!m_cpu_cores || !m_cpu_query) { @@ -304,7 +290,6 @@ namespace utils { perf_log.error("Failed to open /proc/stat (%s)", strerror(errno)); } -#endif #else total_usage = get_usage(); #endif diff --git a/rpcs3/util/sysinfo.cpp b/rpcs3/util/sysinfo.cpp index 270e45ae46..dc5ade1dc2 100755 --- a/rpcs3/util/sysinfo.cpp +++ b/rpcs3/util/sysinfo.cpp @@ -74,45 +74,45 @@ namespace Darwin_ProcessInfo namespace utils { #ifdef _WIN32 + // Some helpers for sanity + const auto read_reg_dword = [](HKEY hKey, std::string_view value_name) -> std::pair + { + DWORD val = 0; + 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 + { + constexpr usz MAX_SZ_LEN = 255; + char sz[MAX_SZ_LEN + 1] {}; + DWORD sz_len = MAX_SZ_LEN; + + // Safety; null terminate + sz[0] = 0; + sz[MAX_SZ_LEN] = 0; + + // Read string + if (ERROR_SUCCESS != RegQueryValueExA(hKey, value_name.data(), nullptr, nullptr, reinterpret_cast(sz), &sz_len)) + { + return { false, "" }; + } + + // Safety, force null terminator + if (sz_len < MAX_SZ_LEN) + { + sz[sz_len] = 0; + } + return { true, sz }; + }; + // 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 - { - constexpr usz MAX_SZ_LEN = 255; - char sz[MAX_SZ_LEN + 1]; - DWORD sz_len = MAX_SZ_LEN; - - // Safety; null terminate - sz[0] = 0; - sz[MAX_SZ_LEN] = 0; - - // Read string - if (ERROR_SUCCESS != RegQueryValueExA(hKey, value_name.data(), nullptr, nullptr, reinterpret_cast(sz), &sz_len)) - { - return { false, "" }; - } - - // Safety, force null terminator - if (sz_len < MAX_SZ_LEN) - { - 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)) { @@ -661,14 +661,88 @@ std::string utils::get_firmware_version() return {}; } -std::string utils::get_OS_version() +utils::OS_version utils::get_OS_version() +{ + OS_version res {}; + +#if _WIN32 + res.type = "windows"; +#elif __linux__ + res.type = "linux"; +#elif __APPLE__ + res.type = "macos"; +#elif __FreeBSD__ + res.type = "freebsd"; +#else + res.type = "unknown"; +#endif + +#if defined(ARCH_X64) + res.arch = "x64"; +#elif defined(ARCH_ARM64) + res.arch = "arm64"; +#else + res.arch = "unknown"; +#endif + +#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. +#if defined(ARCH_X64) + constexpr DWORD peb_offset = 0x60; + const INT_PTR peb = __readgsqword(peb_offset); + res.version_major = *reinterpret_cast(peb + 0x118); + res.version_minor = *reinterpret_cast(peb + 0x11c); + res.version_patch = *reinterpret_cast(peb + 0x120); +#else + HKEY hKey; + if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", 0, KEY_READ, &hKey) == ERROR_SUCCESS) + { + 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, version_patch] = read_reg_sz(hKey, "CurrentBuildNumber"); + + if (check_major) res.version_major = version_major; + if (check_minor) res.version_minor = version_minor; + if (check_build) res.version_patch = stoi(version_patch); + + RegCloseKey(hKey); + } +#endif +#elif defined (__APPLE__) + res.version_major = Darwin_Version::getNSmajorVersion(); + res.version_minor = Darwin_Version::getNSminorVersion(); + res.version_patch = Darwin_Version::getNSpatchVersion(); +#else + if (struct utsname details = {}; !uname(&details)) + { + const std::vector version_list = fmt::split(details.release, { "." }); + const auto get_version_part = [&version_list](usz i) -> usz + { + if (version_list.size() <= i) return 0; + if (const auto [success, version_part] = string_to_number(version_list[i]); success) + { + return version_part; + } + return 0; + }; + res.version_major = get_version_part(0); + res.version_minor = get_version_part(1); + res.version_patch = get_version_part(2); + } +#endif + + return res; +} + +std::string utils::get_OS_version_string() { std::string output; #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. #if defined(ARCH_X64) - const DWORD peb_offset = 0x60; + constexpr DWORD peb_offset = 0x60; const INT_PTR peb = __readgsqword(peb_offset); const DWORD version_major = *reinterpret_cast(peb + 0x118); const DWORD version_minor = *reinterpret_cast(peb + 0x11c); @@ -1014,10 +1088,20 @@ u32 utils::get_cpu_model() #endif } -namespace utils +u64 utils::_get_main_tid() { - u64 _get_main_tid() - { - return thread_ctrl::get_tid(); - } + return thread_ctrl::get_tid(); +} + +std::pair utils::string_to_number(std::string_view str) +{ + std::add_pointer_t eval; + const usz number = std::strtol(str.data(), &eval, 10); + + if (str.data() + str.size() == eval) + { + return { true, number }; + } + + return { false, 0 }; } diff --git a/rpcs3/util/sysinfo.hpp b/rpcs3/util/sysinfo.hpp index 4081b3a9cf..ff78288b15 100755 --- a/rpcs3/util/sysinfo.hpp +++ b/rpcs3/util/sysinfo.hpp @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include "util/types.hpp" #include @@ -67,7 +67,17 @@ namespace utils std::string get_firmware_version(); - std::string get_OS_version(); + struct OS_version + { + std::string type; + std::string arch; + int version_major = 0; + int version_minor = 0; + int version_patch = 0; + }; + OS_version get_OS_version(); + + std::string get_OS_version_string(); int get_maxfiles(); @@ -94,4 +104,6 @@ namespace utils { return s_tsc_freq; } + + std::pair string_to_number(std::string_view str); }