diff --git a/BeefLibs/corlib/src/OperatingSystem.bf b/BeefLibs/corlib/src/OperatingSystem.bf index 4108e5e1..6b297c33 100644 --- a/BeefLibs/corlib/src/OperatingSystem.bf +++ b/BeefLibs/corlib/src/OperatingSystem.bf @@ -2,12 +2,268 @@ namespace System { class OperatingSystem { + #if BF_PLATFORM_WINDOWS + private const String Arch32 = "32-bit Edition"; + private const String Arch64 = "64-bit Edition"; + + private const uint8 VER_EQUAL = 1; + private const uint8 VER_PRODUCT_TYPE = 0x00000080; + private const uint8 VER_NT_WORKSTATION = 0x00000001; + private const uint8 PROCESSOR_ARCHITECTURE_AMD64 = 9; + private const uint8 SM_SERVERR2 = 89; + + [CRepr] + private struct OSVersionInfoA { + public uint32 dwOSVersionInfoSize; + public uint32 dwMajorVersion; + public uint32 dwMinorVersion; + public uint32 dwBuildNumber; + public uint32 dwPlatformId; + public uint8[128] szCSDVersion; // Maintenance UnicodeString for PSS usage + + } + [CRepr] + private struct OSVersionInfoExA : OSVersionInfoA { + public uint16 wServicePackMajor; + public uint16 wServicePackMinor; + public uint16 wSuiteMask; + public uint8 wProductType; + public uint8 wReserved; + } + + [CRepr] + private struct SystemInfo { + public uint16 wProcessorArchitecture; + public uint16 wReserved; + public uint32 dwPageSize; + public void* lpMinimumApplicationAddress; + public void* lpMaximumApplicationAddress; + public uint32* dwActiveProcessorMask; + public uint32 dwNumberOfProcessors; + public uint32 dwProcessorType; + public uint32 dwAllocationGranularity; + public int16 wProcessorLevel; + public int16 wProcessorRevision; + } + + [CRepr] + private struct WKSTA_INFO_100 + { + public uint32 wki100_platform_id; + public uint32 wki100_computername; + public uint32 wki100_langroup; + public uint32 wki100_ver_major; + public uint32 wki100_ver_minor; + } + typealias LPWKSTA_INFO_100 = WKSTA_INFO_100*; + + [CRepr] + private struct VSFixedFileInfo + { + public uint32 dwSignature; // e.g. $feef04bd + public uint32 dwStrucVersion; // e.g. $00000042 = "0.42" + public uint32 dwFileVersionMS; // e.g. $00030075 = "3.75" + public uint32 dwFileVersionLS; // e.g. $00000031 = "0.31" + public uint32 dwProductVersionMS; // e.g. $00030010 = "3.10" + public uint32 dwProductVersionLS; // e.g. $00000031 = "0.31" + public uint32 dwFileFlagsMask; // = $3F for version "0.42" + public uint32 dwFileFlags; // e.g. VFF_DEBUG | VFF_PRERELEASE + public uint32 dwFileOS; // e.g. VOS_DOS_WINDOWS16 + public uint32 dwFileType; // e.g. VFT_DRIVER + public uint32 dwFileSubtype; // e.g. VFT2_DRV_KEYBOARD + public uint32 dwFileDateMS; // e.g. 0 + public uint32 dwFileDateLS; // e.g. 0 + } + + [Import("Kernel32.lib"), CLink, StdCall] + private extern static bool GetVersionExA(OSVersionInfoExA* lpVersionInformation); + + [Import("Kernel32.lib"), CLink, StdCall] + private extern static bool VerifyVersionInfoA(OSVersionInfoExA* lpVersionInformation, uint32 dwTypeMask, uint64 dwlConditionMask); + + [Import("Kernel32.lib"), CLink, StdCall] + private extern static uint64 VerSetConditionMask(uint64 dwlConditionMask, uint32 dwTypeBitMask, uint8 dwConditionMask); + + [Import("Kernel32.lib"), CLink, StdCall] + private extern static void GetNativeSystemInfo(SystemInfo* lpSystemInformation); + [Import("Kernel32.lib"), CLink, StdCall] + private extern static void GetSystemInfo(SystemInfo* lpSystemInfo); + + [Import("netapi32.lib"), CLink, StdCall] + private extern static uint32 NetWkstaGetInfo(char16* ServerName, uint32 Level, LPWKSTA_INFO_100* BufPtr); + [Import("netapi32.lib"), CLink, StdCall] + private extern static int32 NetApiBufferFree(LPWKSTA_INFO_100 BufPtr); + + [Import("Kernel32.lib"), CLink, StdCall] + private extern static uint32 GetFileVersionInfoSizeA(char8* lptstrFilename, uint32* lpdwHandle); + + [Import("Version.lib"), CLink, StdCall] + private extern static bool GetFileVersionInfoA(char8* lptstrFilename, uint32* dwHandle, uint32 dwLen, void* lpData); + [Import("Version.lib"), CLink, StdCall] + private extern static bool VerQueryValueA(void* pBlock, char8* lpSubBlock, void** lplpBuffer, uint32* puLen); + + [Import("User32.lib"), CLink, StdCall] + private extern static int GetSystemMetrics(int nIndex); + #endif + public Version Version; - public PlatformID Platform = PlatformID.Win32NT; + public PlatformID Platform; + public String Name = new .() ~ delete _; + #if BF_PLATFORM_LINUX + public String PrettyName = new .() ~ delete _; + #endif public this() { + #if BF_PLATFORM_WINDOWS + bool isWinSrv() + { + OSVersionInfoExA osvi = .(); + uint64 dwlCondMask; + + osvi.wProductType = VER_NT_WORKSTATION; + + dwlCondMask = VerSetConditionMask(0, VER_PRODUCT_TYPE, VER_EQUAL); + + return VerifyVersionInfoA(&osvi, VER_PRODUCT_TYPE, dwlCondMask) == false; + } + + bool GetProductVersion(out uint32 major, out uint32 minor, out uint32 build) + { + bool result = false; + major = 0; + minor = 0; + build = 0; + uint32 VerSize, Wnd; + VSFixedFileInfo* FI = &VSFixedFileInfo(); + + uint32 InfoSize = GetFileVersionInfoSizeA("kernel32.dll", &Wnd); + + if (InfoSize != 0) { + void* VerBuf = Internal.StdMalloc(InfoSize); + + if (GetFileVersionInfoA("kernel32.dll", &Wnd, InfoSize, VerBuf)) { + if (VerQueryValueA(VerBuf, "\\", (void**)(&FI), &VerSize)) { + major = FI.dwProductVersionMS >> 16; + minor = (uint16)FI.dwProductVersionMS; + build = FI.dwProductVersionLS >> 16; + result = true; + } + } + + Internal.StdFree(VerBuf); + } + + return result; + } + + bool GetNetWkstaMajorMinor(out uint32 major, out uint32 minor) + { + LPWKSTA_INFO_100 LBuf = null; + bool result = NetWkstaGetInfo(null, 100, &LBuf) == 0; + + if (result) { + major = LBuf.wki100_ver_major; + minor = LBuf.wki100_ver_minor; + } else { + major = 0; + minor = 0; + } + + if (LBuf != null) + NetApiBufferFree(LBuf); + + return result; + } + + SystemInfo SysInfo = .(); + OSVersionInfoExA VerInfo = .(); + uint32 MajorNum, MinorNum, BuildNum; + + VerInfo.dwOSVersionInfoSize = sizeof(OSVersionInfoExA); + GetVersionExA(&VerInfo); + + Platform = .Win32NT; + Version.Major = VerInfo.dwMajorVersion; + Version.Minor = VerInfo.dwMinorVersion; + Version.Build = VerInfo.dwBuildNumber; + Version.Revision = VerInfo.wServicePackMajor; + + if (Version.Check(5, 1)) // GetNativeSystemInfo not supported on Windows 2000 + GetNativeSystemInfo(&SysInfo); + + if ((Version.Major > 6) || ((Version.Major == 6) && (Version.Minor > 1))) { + if (GetProductVersion(out MajorNum, out MinorNum, out BuildNum)) { + Version.Major = MajorNum; + Version.Minor = MinorNum; + Version.Build = BuildNum; + } else if (GetNetWkstaMajorMinor(out MajorNum, out MinorNum)) { + Version.Major = MajorNum; + Version.Minor = MinorNum; + } + } + + Name.Append("Windows"); + + switch(Version.Major) { + case 10: + switch(Version.Minor) { + case 0: Name.Append(!isWinSrv() ? " 10" : " Server 2016"); + // Server 2019 is also 10.0 + } + break; + case 6: + switch(Version.Minor) { + case 0: Name.Append(VerInfo.wProductType == VER_NT_WORKSTATION ? " Vista" : " Server 2008"); + case 1: Name.Append(VerInfo.wProductType == VER_NT_WORKSTATION ? " 7" : " Server 2008 R2"); + case 2: Name.Append(VerInfo.wProductType == VER_NT_WORKSTATION ? " 8" : " Server 2012"); + case 3: Name.Append(!isWinSrv() ? " 8.1" : " Server 2012 R2"); + } + break; + case 5: + switch(Version.Minor) { + case 0: Name.Append(" 2000"); + case 1: Name.Append(" XP"); + case 2: + { + if ((VerInfo.wProductType == VER_NT_WORKSTATION) && + (SysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64)) { + Name.Append(" XP"); + } else { + Name.Append(GetSystemMetrics(SM_SERVERR2) == 0 ? " Server 2003" : " Server 2003 R2"); + } + } + } + break; + } + #elif BF_PLATFORM_LINUX Version.Major = 5; //TODO: + Platform = PlatformID.Unix; + #else // MACOS and ANDROID + Version.Major = 5; //TODO: + Platform = PlatformID.MacOSX; + #endif + } + + public override void ToString(String outVar) + { + #if BF_PLATFORM_WINDOWS + #if BF_64_BIT + String arch = Arch64; + #else + String arch = Arch32; + #endif + + if (Version.Revision == 0) { + outVar.AppendF("{} (Version {}.{}, Build {}, {})", Name, Version.Major, Version.Minor, Version.Build, arch); + } else { + outVar.AppendF("{} Service Pack {} (Version {}.{}, Build {}, {})", Name, Version.Revision, Version.Major, Version.Minor, Version.Build, arch); + } + #elif BF_PLATFORM_LINUX + outVar.AppendF("{} {} (Version {}.{}.{})", PrettyName, Name, Version.Major, Version.Minor, Version.Revision); + #else // MACOS and ANDROID + outVar.AppendF("{} (Version {}.{}.{})", Name, Version.Major, Version.Minor, Version.Revision); + #endif } } }