/* * mptOS.cpp * --------- * Purpose: Operating system version information. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "mptOS.h" #include "mpt/binary/hex.hpp" #if MPT_OS_WINDOWS #include #endif OPENMPT_NAMESPACE_BEGIN namespace mpt { namespace OS { namespace Windows { #if MPT_OS_WINDOWS namespace { struct WindowsVersionCache { mpt::osinfo::windows::Version version; WindowsVersionCache() noexcept : version(mpt::osinfo::windows::Version::Current()) { } }; } static mpt::osinfo::windows::Version GatherWindowsVersionFromCache() noexcept { static WindowsVersionCache gs_WindowsVersionCache; return gs_WindowsVersionCache.version; } #endif // MPT_OS_WINDOWS mpt::osinfo::windows::Version Version::Current() noexcept { #if MPT_OS_WINDOWS #ifdef MODPLUG_TRACKER return GatherWindowsVersionFromCache(); #else // !MODPLUG_TRACKER return mpt::osinfo::windows::Version::Current(); #endif // MODPLUG_TRACKER #else // !MPT_OS_WINDOWS return mpt::osinfo::windows::Version::NoWindows(); #endif // MPT_OS_WINDOWS } static constexpr struct { mpt::osinfo::windows::Version version; const mpt::uchar * name; bool showDetails; } versionMap[] = { { mpt::osinfo::windows::Version{ mpt::osinfo::windows::Version::WinNewer, mpt::osinfo::windows::Version::ServicePack{ 0, 0 }, 22000, 0 }, UL_("Windows 11 (or newer)"), false }, { mpt::osinfo::windows::Version{ mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack{ 0, 0 }, 22000, 0 }, UL_("Windows 11"), true }, { mpt::osinfo::windows::Version{ mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack{ 0, 0 }, 19044, 0 }, UL_("Windows 10 21H2"), true }, { mpt::osinfo::windows::Version{ mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack{ 0, 0 }, 19043, 0 }, UL_("Windows 10 21H1"), true }, { mpt::osinfo::windows::Version{ mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack{ 0, 0 }, 19042, 0 }, UL_("Windows 10 20H2"), true }, { mpt::osinfo::windows::Version{ mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack{ 0, 0 }, 19041, 0 }, UL_("Windows 10 2004"), true }, { mpt::osinfo::windows::Version{ mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack{ 0, 0 }, 18363, 0 }, UL_("Windows 10 1909"), true }, { mpt::osinfo::windows::Version{ mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack{ 0, 0 }, 18362, 0 }, UL_("Windows 10 1903"), true }, { mpt::osinfo::windows::Version{ mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack{ 0, 0 }, 17763, 0 }, UL_("Windows 10 1809"), true }, { mpt::osinfo::windows::Version{ mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack{ 0, 0 }, 17134, 0 }, UL_("Windows 10 1803"), true }, { mpt::osinfo::windows::Version{ mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack{ 0, 0 }, 16299, 0 }, UL_("Windows 10 1709"), true }, { mpt::osinfo::windows::Version{ mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack{ 0, 0 }, 15063, 0 }, UL_("Windows 10 1703"), true }, { mpt::osinfo::windows::Version{ mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack{ 0, 0 }, 14393, 0 }, UL_("Windows 10 1607"), true }, { mpt::osinfo::windows::Version{ mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack{ 0, 0 }, 10586, 0 }, UL_("Windows 10 1511"), true }, { mpt::osinfo::windows::Version{ mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack{ 0, 0 }, 10240, 0 }, UL_("Windows 10 1507"), true }, { mpt::osinfo::windows::Version{ mpt::osinfo::windows::Version::Win81, mpt::osinfo::windows::Version::ServicePack{ 0, 0 }, 0, 0 }, UL_("Windows 8.1"), true }, { mpt::osinfo::windows::Version{ mpt::osinfo::windows::Version::Win8, mpt::osinfo::windows::Version::ServicePack{ 0, 0 }, 0, 0 }, UL_("Windows 8"), true }, { mpt::osinfo::windows::Version{ mpt::osinfo::windows::Version::Win7, mpt::osinfo::windows::Version::ServicePack{ 0, 0 }, 0, 0 }, UL_("Windows 7"), true }, { mpt::osinfo::windows::Version{ mpt::osinfo::windows::Version::WinVista, mpt::osinfo::windows::Version::ServicePack{ 0, 0 }, 0, 0 }, UL_("Windows Vista"), true }, { mpt::osinfo::windows::Version{ mpt::osinfo::windows::Version::WinXP64, mpt::osinfo::windows::Version::ServicePack{ 0, 0 }, 0, 0 }, UL_("Windows XP x64 / Windows Server 2003"), true }, { mpt::osinfo::windows::Version{ mpt::osinfo::windows::Version::WinXP, mpt::osinfo::windows::Version::ServicePack{ 0, 0 }, 0, 0 }, UL_("Windows XP"), true }, { mpt::osinfo::windows::Version{ mpt::osinfo::windows::Version::Win2000, mpt::osinfo::windows::Version::ServicePack{ 0, 0 }, 0, 0 }, UL_("Windows 2000"), true }, { mpt::osinfo::windows::Version{ mpt::osinfo::windows::Version::WinNT4, mpt::osinfo::windows::Version::ServicePack{ 0, 0 }, 0, 0 }, UL_("Windows NT4"), true } }; mpt::ustring Version::GetName(mpt::osinfo::windows::Version version) { mpt::ustring name = U_("Generic Windows NT"); bool showDetails = false; for(const auto &v : versionMap) { if(version.IsAtLeast(v.version)) { name = v.name; showDetails = v.showDetails; break; } } name += U_(" ("); name += MPT_UFORMAT("Version {}.{}")(version.GetSystem().Major, version.GetSystem().Minor); if(showDetails) { if(version.GetServicePack().HasServicePack()) { if(version.GetServicePack().Minor) { name += MPT_UFORMAT(" Service Pack {}.{}")(version.GetServicePack().Major, version.GetServicePack().Minor); } else { name += MPT_UFORMAT(" Service Pack {}")(version.GetServicePack().Major); } } if(version.GetBuild() != 0) { name += MPT_UFORMAT(" (Build {})")(version.GetBuild()); } } name += U_(")"); mpt::ustring result = name; #if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS if(mpt::OS::Windows::IsWine()) { mpt::OS::Wine::VersionContext v; if(v.Version().IsValid()) { result = MPT_UFORMAT("Wine {} ({})")( v.Version().AsString() , name ); } else { result = MPT_UFORMAT("Wine (unknown version: '{}') ({})")( mpt::ToUnicode(mpt::Charset::UTF8, v.RawVersion()) , name ); } } #endif // MODPLUG_TRACKER && MPT_OS_WINDOWS return result; } mpt::osinfo::windows::Version Version::GetMinimumKernelLevel() noexcept { uint64 minimumKernelVersion = 0; #if MPT_OS_WINDOWS && MPT_COMPILER_MSVC #if defined(MPT_BUILD_RETRO) minimumKernelVersion = std::max(minimumKernelVersion, static_cast(mpt::osinfo::windows::Version::WinXP)); #else minimumKernelVersion = std::max(minimumKernelVersion, static_cast(mpt::osinfo::windows::Version::WinVista)); #endif #endif return mpt::osinfo::windows::Version(mpt::osinfo::windows::Version::System(minimumKernelVersion), mpt::osinfo::windows::Version::ServicePack(0, 0), 0, 0); } mpt::osinfo::windows::Version Version::GetMinimumAPILevel() noexcept { #if MPT_OS_WINDOWS return mpt::osinfo::windows::Version::FromSDK(); #else // !MPT_OS_WINDOWS return mpt::osinfo::windows::Version::NoWindows(); #endif // MPT_OS_WINDOWS } #if MPT_OS_WINDOWS #ifndef PROCESSOR_ARCHITECTURE_NEUTRAL #define PROCESSOR_ARCHITECTURE_NEUTRAL 11 #endif #ifndef PROCESSOR_ARCHITECTURE_ARM64 #define PROCESSOR_ARCHITECTURE_ARM64 12 #endif #ifndef PROCESSOR_ARCHITECTURE_ARM32_ON_WIN64 #define PROCESSOR_ARCHITECTURE_ARM32_ON_WIN64 13 #endif #ifndef PROCESSOR_ARCHITECTURE_IA32_ON_ARM64 #define PROCESSOR_ARCHITECTURE_IA32_ON_ARM64 14 #endif struct OSArchitecture { uint16 ProcessorArchitectur; Architecture Host; Architecture Process; }; static constexpr OSArchitecture architectures [] = { { PROCESSOR_ARCHITECTURE_INTEL , Architecture::x86 , Architecture::x86 }, { PROCESSOR_ARCHITECTURE_AMD64 , Architecture::amd64 , Architecture::amd64 }, { PROCESSOR_ARCHITECTURE_IA32_ON_WIN64 , Architecture::amd64 , Architecture::x86 }, { PROCESSOR_ARCHITECTURE_ARM , Architecture::arm , Architecture::arm }, { PROCESSOR_ARCHITECTURE_ARM64 , Architecture::arm64 , Architecture::arm64 }, { PROCESSOR_ARCHITECTURE_ARM32_ON_WIN64, Architecture::arm64 , Architecture::arm }, { PROCESSOR_ARCHITECTURE_IA32_ON_ARM64 , Architecture::arm64 , Architecture::x86 }, { PROCESSOR_ARCHITECTURE_MIPS , Architecture::mips , Architecture::mips }, { PROCESSOR_ARCHITECTURE_PPC , Architecture::ppc , Architecture::ppc }, { PROCESSOR_ARCHITECTURE_SHX , Architecture::shx , Architecture::shx }, { PROCESSOR_ARCHITECTURE_ALPHA , Architecture::alpha , Architecture::alpha }, { PROCESSOR_ARCHITECTURE_ALPHA64 , Architecture::alpha64, Architecture::alpha64 }, { PROCESSOR_ARCHITECTURE_IA64 , Architecture::ia64 , Architecture::ia64 }, { PROCESSOR_ARCHITECTURE_MSIL , Architecture::unknown, Architecture::unknown }, { PROCESSOR_ARCHITECTURE_NEUTRAL , Architecture::unknown, Architecture::unknown }, { PROCESSOR_ARCHITECTURE_UNKNOWN , Architecture::unknown, Architecture::unknown } }; struct HostArchitecture { Architecture Host; Architecture Process; EmulationLevel Emulation; }; static constexpr HostArchitecture hostArchitectureCanRun [] = { { Architecture::x86 , Architecture::x86 , EmulationLevel::Native }, { Architecture::amd64 , Architecture::amd64 , EmulationLevel::Native }, { Architecture::amd64 , Architecture::x86 , EmulationLevel::Virtual }, { Architecture::arm , Architecture::arm , EmulationLevel::Native }, { Architecture::arm64 , Architecture::arm64 , EmulationLevel::Native }, { Architecture::arm64 , Architecture::arm , EmulationLevel::Virtual }, { Architecture::arm64 , Architecture::x86 , EmulationLevel::Software }, { Architecture::arm64 , Architecture::amd64 , EmulationLevel::Software }, { Architecture::mips , Architecture::mips , EmulationLevel::Native }, { Architecture::ppc , Architecture::ppc , EmulationLevel::Native }, { Architecture::shx , Architecture::shx , EmulationLevel::Native }, { Architecture::alpha , Architecture::alpha , EmulationLevel::Native }, { Architecture::alpha64, Architecture::alpha64, EmulationLevel::Native }, { Architecture::alpha64, Architecture::alpha , EmulationLevel::Virtual }, { Architecture::ia64 , Architecture::ia64 , EmulationLevel::Native }, { Architecture::ia64 , Architecture::x86 , EmulationLevel::Hardware } }; struct ArchitectureInfo { Architecture Arch; int Bitness; const mpt::uchar * Name; }; static constexpr ArchitectureInfo architectureInfo [] = { { Architecture::x86 , 32, UL_("x86") }, { Architecture::amd64 , 64, UL_("amd64") }, { Architecture::arm , 32, UL_("arm") }, { Architecture::arm64 , 64, UL_("arm64") }, { Architecture::mips , 32, UL_("mips") }, { Architecture::ppc , 32, UL_("ppc") }, { Architecture::shx , 32, UL_("shx") }, { Architecture::alpha , 32, UL_("alpha") }, { Architecture::alpha64, 64, UL_("alpha64") }, { Architecture::ia64 , 64, UL_("ia64") } }; int Bitness(Architecture arch) noexcept { for(const auto &info : architectureInfo) { if(arch == info.Arch) { return info.Bitness; } } return 0; } mpt::ustring Name(Architecture arch) { for(const auto &info : architectureInfo) { if(arch == info.Arch) { return info.Name; } } return mpt::ustring(); } Architecture GetHostArchitecture() noexcept { SYSTEM_INFO systemInfo = {}; GetNativeSystemInfo(&systemInfo); for(const auto &arch : architectures) { if(systemInfo.wProcessorArchitecture == arch.ProcessorArchitectur) { return arch.Host; } } return Architecture::unknown; } Architecture GetProcessArchitecture() noexcept { SYSTEM_INFO systemInfo = {}; GetSystemInfo(&systemInfo); for(const auto &arch : architectures) { if(systemInfo.wProcessorArchitecture == arch.ProcessorArchitectur) { return arch.Process; } } return Architecture::unknown; } EmulationLevel HostCanRun(Architecture host, Architecture process) noexcept { for(const auto & can : hostArchitectureCanRun) { if(can.Host == host && can.Process == process) { return can.Emulation; } } return EmulationLevel::NA; } std::vector GetSupportedProcessArchitectures(Architecture host) { std::vector result; for(const auto & entry : hostArchitectureCanRun) { if(entry.Host == host) { result.push_back(entry.Process); } } return result; } uint64 GetSystemMemorySize() { MEMORYSTATUSEX memoryStatus = {}; memoryStatus.dwLength = sizeof(MEMORYSTATUSEX); if(GlobalMemoryStatusEx(&memoryStatus) == 0) { return 0; } return memoryStatus.ullTotalPhys; } #endif // MPT_OS_WINDOWS #if defined(MODPLUG_TRACKER) #if MPT_OS_WINDOWS static bool GatherSystemIsWine() { bool SystemIsWine = false; std::optional NTDLL = mpt::library::load({ mpt::library::path_search::system, mpt::library::path_prefix::none, MPT_PATH("ntdll.dll"), mpt::library::path_suffix::none }); if(NTDLL) { SystemIsWine = (NTDLL->get_address("wine_get_version") != nullptr); } return SystemIsWine; } namespace { struct SystemIsWineCache { bool SystemIsWine; SystemIsWineCache() : SystemIsWine(GatherSystemIsWine()) { return; } SystemIsWineCache(bool isWine) : SystemIsWine(isWine) { return; } }; } #endif // MPT_OS_WINDOWS static bool SystemIsWine(bool allowDetection = true) { #if MPT_OS_WINDOWS static SystemIsWineCache gs_SystemIsWineCache = allowDetection ? SystemIsWineCache() : SystemIsWineCache(false); if(!allowDetection) { // catch too late calls of PreventWineDetection MPT_ASSERT(!gs_SystemIsWineCache.SystemIsWine); } return gs_SystemIsWineCache.SystemIsWine; #else MPT_UNREFERENCED_PARAMETER(allowDetection); return false; #endif } void PreventWineDetection() { SystemIsWine(false); } bool IsOriginal() { return mpt::OS::Windows::Version::Current().IsWindows() && !SystemIsWine(); } bool IsWine() { return mpt::OS::Windows::Version::Current().IsWindows() && SystemIsWine(); } #endif // MODPLUG_TRACKER } // namespace Windows } // namespace OS } // namespace mpt namespace mpt { namespace OS { namespace Wine { Version::Version() { return; } Version::Version(const mpt::ustring &rawVersion) : mpt::osinfo::windows::wine::version() { if(rawVersion.empty()) { return; } std::vector version = mpt::String::Split(rawVersion, U_(".")); if(version.size() < 2) { return; } mpt::ustring parsedVersion = mpt::String::Combine(version, U_(".")); std::size_t len = std::min(parsedVersion.length(), rawVersion.length()); if(len == 0) { return; } if(parsedVersion.substr(0, len) != rawVersion.substr(0, len)) { return; } valid = true; vmajor = version[0]; vminor = version[1]; vupdate = (version.size() >= 3) ? version[2] : 0; } Version::Version(uint8 vmajor, uint8 vminor, uint8 vupdate) : mpt::osinfo::windows::wine::version(vmajor, vminor, vupdate) { return; } mpt::ustring Version::AsString() const { return mpt::ufmt::dec(GetMajor()) + U_(".") + mpt::ufmt::dec(GetMinor()) + U_(".") + mpt::ufmt::dec(GetUpdate()); } mpt::OS::Wine::Version GetMinimumWineVersion() { mpt::OS::Wine::Version minimumWineVersion = mpt::OS::Wine::Version(0,0,0); #if MPT_OS_WINDOWS && MPT_COMPILER_MSVC minimumWineVersion = mpt::OS::Wine::Version(1,8,0); #endif return minimumWineVersion; } VersionContext::VersionContext() : m_IsWine(false) , m_HostClass(mpt::osinfo::osclass::Unknown) { #if MPT_OS_WINDOWS m_IsWine = mpt::OS::Windows::IsWine(); if(!m_IsWine) { return; } std::optional NTDLL = mpt::library::load({mpt::library::path_search::system, mpt::library::path_prefix::none, MPT_PATH("ntdll.dll"), mpt::library::path_suffix::none}); if(NTDLL) { const char * (__cdecl * wine_get_version)(void) = nullptr; const char * (__cdecl * wine_get_build_id)(void) = nullptr; void (__cdecl * wine_get_host_version)(const char * *, const char * *) = nullptr; NTDLL->bind(wine_get_version, "wine_get_version"); NTDLL->bind(wine_get_build_id, "wine_get_build_id"); NTDLL->bind(wine_get_host_version, "wine_get_host_version"); const char * wine_version = nullptr; const char * wine_build_id = nullptr; const char * wine_host_sysname = nullptr; const char * wine_host_release = nullptr; wine_version = wine_get_version ? wine_get_version() : ""; wine_build_id = wine_get_build_id ? wine_get_build_id() : ""; if(wine_get_host_version) { wine_get_host_version(&wine_host_sysname, &wine_host_release); } m_RawVersion = wine_version ? wine_version : ""; m_RawBuildID = wine_build_id ? wine_build_id : ""; m_RawHostSysName = wine_host_sysname ? wine_host_sysname : ""; m_RawHostRelease = wine_host_release ? wine_host_release : ""; } m_Version = mpt::OS::Wine::Version(mpt::ToUnicode(mpt::Charset::UTF8, m_RawVersion)); m_HostClass = mpt::osinfo::get_class_from_sysname(m_RawHostSysName); #endif // MPT_OS_WINDOWS } } // namespace Wine } // namespace OS } // namespace mpt OPENMPT_NAMESPACE_END