diff --git a/Engine/Source/Applications/Editor/xmake.lua b/Engine/Source/Applications/Editor/xmake.lua index fdf1dd7..3ec546e 100644 --- a/Engine/Source/Applications/Editor/xmake.lua +++ b/Engine/Source/Applications/Editor/xmake.lua @@ -2,4 +2,5 @@ local module_name = 'Editor' corvus_application_target(module_name) add_deps('Core') + add_deps('Platform') corvus_target_end() \ No newline at end of file diff --git a/Engine/Source/Runtime/Core/Public/Core/Assertion/Assertion.hpp b/Engine/Source/Runtime/Core/Public/Core/Assertion/Assertion.hpp index 5fbfbeb..edc722b 100644 --- a/Engine/Source/Runtime/Core/Public/Core/Assertion/Assertion.hpp +++ b/Engine/Source/Runtime/Core/Public/Core/Assertion/Assertion.hpp @@ -2,12 +2,13 @@ #pragma once +#include "ExceptionHandler.hpp" + #include "Core/Containers/String.hpp" #include "Core/Logging/LogChannel.hpp" #include "Core/Utility/SourceLocation.hpp" #include "Core/Utility/StringUtils.hpp" -struct FExceptionMetadata; CORE_API DECLARE_LOG_CHANNEL_EXTERN(Assert) namespace Assertion diff --git a/Engine/Source/Runtime/Core/Public/Core/CoreDefinitions.hpp b/Engine/Source/Runtime/Core/Public/Core/CoreDefinitions.hpp index f5c1ca0..c179a9f 100644 --- a/Engine/Source/Runtime/Core/Public/Core/CoreDefinitions.hpp +++ b/Engine/Source/Runtime/Core/Public/Core/CoreDefinitions.hpp @@ -50,8 +50,8 @@ DEFAULT_MOVEABLE_PREFIX(Class,) #define DEFAULT_COPY_MOVEABLE_PREFIX(Class, Prefix) \ - DEFAULT_COPYABLE(Class, Prefix) \ - DEFAULT_MOVEABLE(Class, Prefix) + DEFAULT_COPYABLE_PREFIX(Class, Prefix) \ + DEFAULT_MOVEABLE_PREFIX(Class, Prefix) #define INNER_APPEND(X, Y) X##Y #define APPEND(X, Y) INNER_APPEND(X, Y) diff --git a/Engine/Source/Runtime/Platform/Private/Platform/CPUDetection.cpp b/Engine/Source/Runtime/Platform/Private/Platform/CPUDetection.cpp new file mode 100644 index 0000000..f019426 --- /dev/null +++ b/Engine/Source/Runtime/Platform/Private/Platform/CPUDetection.cpp @@ -0,0 +1,181 @@ +// RavenStorm Copyright @ 2025-2025 + +#include "Platform/CPUDetection.hpp" + +#include "Core/Logging/LogChannel.hpp" +#include "Core/Logging/LogManager.hpp" + +#include + +#include + +#include "Core/Memory/Memory.hpp" + +DEFINE_LOG_CHANNEL(CPUDetection, All) + +namespace +{ + struct FCPUIDResult + { + int32 EAX; + int32 EBX; + int32 ECX; + int32 EDX; + }; + + FCPUIDResult CallCPUID(const int32 FunctionId, const int32 SubFunctionId = 0) + { + FCPUIDResult Result; + int32 CPUInfo[4]; + __cpuidex(CPUInfo, FunctionId, SubFunctionId); + Result.EAX = CPUInfo[0]; + Result.EBX = CPUInfo[1]; + Result.ECX = CPUInfo[2]; + Result.EDX = CPUInfo[3]; + return Result; + } +} + +FCPUInfo FCPUDetection::CPUInfo = {}; + +void FCPUDetection::Initialize() +{ + DetectVendor(); + DetectBrand(); + DetectCPUTopology(); + + CVLOG(LogCPUDetection, Info, "CPU: {}", CPUInfo.BrandString); + CVLOG(LogCPUDetection, Info, "\t- Vendor: {}", CPUInfo.VendorString); + CVLOG(LogCPUDetection, Info, "\t- Family: {}", CPUInfo.Family); + CVLOG(LogCPUDetection, Info, "\t- Model: {}", CPUInfo.Model); + CVLOG(LogCPUDetection, Info, "\t- Stepping: {}", CPUInfo.Stepping); + CVLOG(LogCPUDetection, Info, "\t- Physical Cores: {}, Logical Cores: {}", CPUInfo.PhysicalCores, CPUInfo.LogicalCores); + CVLOG(LogCPUDetection, Info, "\t- Logical Cores: {}", CPUInfo.LogicalCores); + CVLOG(LogCPUDetection, Info, "\t- Cache Line Size: {} bytes", CPUInfo.CacheLine); + CVLOG(LogCPUDetection, Info, "\t- L1 Cache: {} KB", CPUInfo.L1CacheSize / 1024); + CVLOG(LogCPUDetection, Info, "\t- L2 Cache: {} MB", CPUInfo.L2CacheSize / (1024 * 1024)); + CVLOG(LogCPUDetection, Info, "\t- L3 Cache: {} MB", CPUInfo.L3CacheSize / (1024 * 1024)); +} + +FCPUInfo FCPUDetection::GetCPUInfo() noexcept +{ + return CPUInfo; +} + +void FCPUDetection::DetectVendor() +{ + const FCPUIDResult Result = CallCPUID(0, 0); + + char Vendor[13] = {}; + *reinterpret_cast(Vendor) = Result.EBX; + *reinterpret_cast(Vendor + 4) = Result.EDX; + *reinterpret_cast(Vendor + 8) = Result.ECX; + Vendor[12] = '\0'; + + CPUInfo.VendorString = FAnsiString(Vendor); + CPUInfo.IsIntel = CPUInfo.VendorString == "GenuineIntel"; + CPUInfo.IsAMD = CPUInfo.VendorString == "AuthenticAMD"; +} + +void FCPUDetection::DetectBrand() +{ + char Brand[49] = {}; + + const FCPUIDResult Result = CallCPUID(static_cast(0x80000000), 0); + if (Result.EAX >= static_cast(0x80000004)) + { + const FCPUIDResult Brand0 = CallCPUID(static_cast(0x80000002), 0); + const FCPUIDResult Brand1 = CallCPUID(static_cast(0x80000003), 0); + const FCPUIDResult Brand2 = CallCPUID(static_cast(0x80000004), 0); + + std::memcpy(Brand, &Brand0, 16); + std::memcpy(Brand + 16, &Brand1, 16); + std::memcpy(Brand + 32, &Brand2, 16); + Brand[48] = '\0'; + + // Trim leading spaces + char* Start = Brand; + while (*Start == ' ') ++Start; + + CPUInfo.BrandString = FAnsiString(Start); + } +} + +void FCPUDetection::DetectCPUTopology() +{ + const FCPUIDResult Result1 = CallCPUID(1, 0); + CPUInfo.Family = ((Result1.EAX >> 8) & 0xF) + ((Result1.EAX >> 20) & 0xFF); + CPUInfo.Model = ((Result1.EAX >> 4) & 0xF) | ((Result1.EAX >> 12) & 0xF0); + CPUInfo.Stepping = Result1.EAX & 0xF; + + SYSTEM_INFO SystemInfo; + GetSystemInfo(&SystemInfo); + CPUInfo.LogicalCores = static_cast(SystemInfo.dwNumberOfProcessors); + + DWORD BufferSize = 0; + GetLogicalProcessorInformation(nullptr, &BufferSize); + if (BufferSize > 0) + { + const PSYSTEM_LOGICAL_PROCESSOR_INFORMATION Buffer = static_cast(FMemory::Allocate(BufferSize)); + if (GetLogicalProcessorInformation(Buffer, &BufferSize)) + { + const DWORD NumEntries = BufferSize / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); + int32 PhysicalCores = 0; + int32 CacheLine = 0; + int32 L1CacheSize = 0; + int32 L2CacheSize = 0; + int32 L3CacheSize = 0; + for (DWORD Index = 0; Index < NumEntries; ++Index) + { + if (Buffer[Index].Relationship == RelationProcessorCore) + { + ++PhysicalCores; + } + else if (Buffer[Index].Relationship == RelationCache) + { + if (const CACHE_DESCRIPTOR& Cache = Buffer[Index].Cache; Cache.Level == 1) + { + if (CacheLine == 0) + { + CacheLine = static_cast(Cache.LineSize); + } + if (Cache.Type == CacheData || Cache.Type == CacheUnified) + { + L1CacheSize += static_cast(Cache.Size); + } + else if (Cache.Type == CacheInstruction) + { + L1CacheSize += static_cast(Cache.Size); + } + } + else if (Cache.Level == 2) + { + L2CacheSize += static_cast(Cache.Size); + } + else if (Cache.Level == 3) + { + if (L3CacheSize == 0) + { + L3CacheSize = static_cast(Cache.Size); + } + } + } + } + CPUInfo.PhysicalCores = PhysicalCores; + CPUInfo.CacheLine = CacheLine > 0 ? CacheLine : 64; + CPUInfo.L1CacheSize = L1CacheSize; + CPUInfo.L2CacheSize = L2CacheSize; + CPUInfo.L3CacheSize = L3CacheSize; + } + FMemory::Free(Buffer); + } + else + { + CPUInfo.PhysicalCores = CPUInfo.LogicalCores; + CPUInfo.CacheLine = 64; + CPUInfo.L1CacheSize = 0; + CPUInfo.L2CacheSize = 0; + CPUInfo.L3CacheSize = 0; + } + CPUInfo.IsHyperThreaded = CPUInfo.LogicalCores > CPUInfo.PhysicalCores; +} diff --git a/Engine/Source/Runtime/Platform/Private/Platform/Platform.cpp b/Engine/Source/Runtime/Platform/Private/Platform/Platform.cpp new file mode 100644 index 0000000..ca81763 --- /dev/null +++ b/Engine/Source/Runtime/Platform/Private/Platform/Platform.cpp @@ -0,0 +1,128 @@ +// RavenStorm Copyright @ 2025-2025 + +#include "Platform/Platform.hpp" +#include "Core/Logging/LogManager.hpp" +#include "Platform/CPUDetection.hpp" + +#include + +#include + +DEFINE_LOG_CHANNEL(Platform, All) + +FSystemInfo FPlatform::SystemInfo = {}; + +void FPlatform::Initialize() +{ + DetectOperatingSystem(); + DetectComputerInfo(); + + CVLOG(LogPlatform, Info, "OS: {} ({})", SystemInfo.OSName, SystemInfo.OSVersion); + CVLOG(LogPlatform, Info, "\t- Build: {}", SystemInfo.OSBuildNumber); + CVLOG(LogPlatform, Info, "\t- Computer Name: {}", SystemInfo.ComputerName); + + FCPUDetection::Initialize(); +} + +void FPlatform::Shutdown() +{ +} + +bool8 FPlatform::HasEnvVariable(const FString& VariableName) noexcept +{ + return GetEnvironmentVariable(VariableName.c_str(), nullptr, 0) > 0; +} + +FString FPlatform::GetEnvVariable(const FString& VariableName) noexcept +{ + const DWORD BufferSize = GetEnvironmentVariable(VariableName.c_str(), nullptr, 0); + if (BufferSize == 0) + { + return {}; + } + FString Result(BufferSize, '\0'); + GetEnvironmentVariable(VariableName.c_str(), Result.data(), BufferSize); + return Result; +} + +FString FPlatform::GetEnvVariable(const FString& VariableName, const FString& DefaultValue) noexcept +{ + FString Value = GetEnvVariable(VariableName); + return Value.empty() ? DefaultValue : Value; +} + +bool8 FPlatform::SetEnvVariable(const FString& VariableName, const FString& Value) noexcept +{ + return SetEnvironmentVariable(VariableName.c_str(), Value.c_str()) != 0; +} + +FCPUInfo FPlatform::GetCPUInfo() noexcept +{ + return FCPUDetection::GetCPUInfo(); +} + +FSystemInfo FPlatform::GetSystemInfo() noexcept +{ + return SystemInfo; +} + +void FPlatform::DetectOperatingSystem() +{ + using RtlGetVersionPtr = LONG(WINAPI*)(PRTL_OSVERSIONINFOW); + + RTL_OSVERSIONINFOW VersionInfo = {}; + VersionInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOW); + + if (const HMODULE NtDllModule = GetModuleHandleW(L"ntdll.dll")) + { + if (const auto RtlGetVersion = reinterpret_cast(GetProcAddress(NtDllModule, "RtlGetVersion"))) + { + RtlGetVersion(&VersionInfo); + } + } + + const uint32 MajorVersion = VersionInfo.dwMajorVersion; + const uint32 MinorVersion = VersionInfo.dwMinorVersion; + const uint32 BuildNumber = VersionInfo.dwBuildNumber; + + SystemInfo.WindowsVersion = DetermineWindowsVersion(MajorVersion, BuildNumber); + SystemInfo.OSName = GetWindowsVersionString(MajorVersion, BuildNumber); + SystemInfo.OSVersion = std::format("{}.{}.{}", MajorVersion, MinorVersion, BuildNumber); + SystemInfo.OSBuildNumber = std::format("{}", BuildNumber); +} + +void FPlatform::DetectComputerInfo() +{ + CHAR ComputerNameBuffer[MAX_COMPUTERNAME_LENGTH + 1]; + DWORD ComputerNameSize = MAX_COMPUTERNAME_LENGTH + 1; + if (GetComputerName(ComputerNameBuffer, &ComputerNameSize)) + { + SystemInfo.ComputerName = FString(ComputerNameBuffer); + } +} + +EWindowsVersion FPlatform::DetermineWindowsVersion(const uint32 MajorVersion, const uint32 BuildNumber) +{ + if (MajorVersion == 10) + { + if (BuildNumber >= 22000) + { + return EWindowsVersion::Windows11; + } + return EWindowsVersion::Windows10; + } + return EWindowsVersion::Unknown; +} + +FString FPlatform::GetWindowsVersionString(const uint32 MajorVersion, const uint32 BuildNumber) +{ + if (MajorVersion == 10) + { + if (BuildNumber >= 22000) + { + return "Windows 11"; + } + return "Windows 10"; + } + return "Windows (Unknown Version)"; +} diff --git a/Engine/Source/Runtime/Platform/Private/Platform/Registry.cpp b/Engine/Source/Runtime/Platform/Private/Platform/Registry.cpp new file mode 100644 index 0000000..41c5ec2 --- /dev/null +++ b/Engine/Source/Runtime/Platform/Private/Platform/Registry.cpp @@ -0,0 +1,524 @@ +// RavenStorm Copyright @ 2025-2025 + +#include "Platform/Registry.hpp" + +#include "Core/Assertion/Assertion.hpp" +#include "Core/Logging/LogManager.hpp" + +DEFINE_LOG_CHANNEL(Registry, All) + +namespace +{ + HKEY GetHiveHandle(const ERegistryHive Hive) + { + switch (Hive) + { + case ERegistryHive::ClassesRoot: return HKEY_CLASSES_ROOT; + case ERegistryHive::CurrentUser: return HKEY_CURRENT_USER; + case ERegistryHive::CurrentConfig: return HKEY_CURRENT_CONFIG; + case ERegistryHive::LocalMachine: return HKEY_LOCAL_MACHINE; + case ERegistryHive::Users: return HKEY_USERS; + case ERegistryHive::Unknown: + break; + } + return nullptr; + } + + FString GetHiveName(const ERegistryHive Hive) + { + switch (Hive) + { + case ERegistryHive::ClassesRoot: return TEXT("HKEY_CLASSES_ROOT"); + case ERegistryHive::CurrentUser: return TEXT("HKEY_CURRENT_USER"); + case ERegistryHive::CurrentConfig: return TEXT("HKEY_CURRENT_CONFIG"); + case ERegistryHive::LocalMachine: return TEXT("HKEY_LOCAL_MACHINE"); + case ERegistryHive::Users: return TEXT("HKEY_USERS"); + case ERegistryHive::Unknown: + break; + } + return TEXT("Unknown"); + } +} + +FString FRegistryValue::AsString() const +{ + if (Type == ERegistryValueType::String || Type == ERegistryValueType::ExpandString) + { + return {reinterpret_cast(Data.GetData()), Data.Num() / sizeof(FChar)}; + } + return {}; +} + +uint32 FRegistryValue::AsDWord() const +{ + if (Type == ERegistryValueType::DWord && Data.Num() >= sizeof(uint32)) + { + return *reinterpret_cast(Data.GetData()); + } + return 0; +} + +uint64 FRegistryValue::AsQWord() const +{ + if (Type == ERegistryValueType::QWord && Data.Num() >= sizeof(uint64)) + { + return *reinterpret_cast(Data.GetData()); + } + return 0; +} + +TArray FRegistryValue::AsMultiString() const +{ + TArray Result; + if (Type == ERegistryValueType::MultiString) + { + const FChar* Current = reinterpret_cast(Data.GetData()); + const FChar* End = Current + (Data.Num() / sizeof(FChar)); + while (Current < End && *Current) + { + FString String(Current); + Current += String.length() + 1; + Result.PushBack(std::move(String)); + } + } + return Result; +} + +TArray FRegistryValue::AsBinary() const +{ + return Data; +} + +FRegistryKey::FRegistryKey(const ERegistryHive InHive, FString InSubKey) + : Hive(InHive), SubKey(std::move(InSubKey)) +{ +} + +FRegistryKey::~FRegistryKey() +{ + Close(); +} + +bool8 FRegistryKey::IsValid() const noexcept +{ + return Hive != ERegistryHive::Unknown && !SubKey.empty(); +} + +bool8 FRegistryKey::Exists() const noexcept +{ + if (!IsValid()) + { + return false; + } + HKEY TempKey; + const LONG Result = RegOpenKeyEx(GetHiveHandle(), SubKey.c_str(), 0, KEY_READ, &TempKey); + if (Result == ERROR_SUCCESS) + { + RegCloseKey(TempKey); + return true; + } + return false; +} + +bool8 FRegistryKey::Create() +{ + if (!IsValid()) + { + CVLOG(LogRegistry, Error, TEXT("Cannot create invalid registry key")); + return false; + } + if (IsOpen) + { + Close(); + } + DWORD Disposition; + const LONG Result = RegCreateKeyEx(GetHiveHandle(), SubKey.c_str(), 0, nullptr, REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE, nullptr, &KeyHandle, &Disposition); + if (Result == ERROR_SUCCESS) + { + IsOpen = true; + if (Disposition == REG_CREATED_NEW_KEY) + { + CVLOG(LogRegistry, Info, TEXT("Created new registry key: {}\\{}"), GetHiveName(Hive), SubKey); + } + return true; + } + CVLOG(LogRegistry, Error, TEXT("Failed to create registry key: {}\\{} (Error: {} -> \"{}\")"), GetHiveName(Hive), SubKey, Result, FExceptionHandler::GetResultDescription(Result)); + return false; +} + +bool8 FRegistryKey::Open(const bool8 ReadOnly) +{ + if (!IsValid()) + { + CVLOG(LogRegistry, Error, TEXT("Cannot open invalid registry key")); + return false; + } + if (IsOpen) + { + return true; + } + const REGSAM Access = ReadOnly ? KEY_READ : (KEY_READ | KEY_WRITE); + const LONG Result = RegOpenKeyEx(GetHiveHandle(), SubKey.c_str(), 0, Access, &KeyHandle); + if (Result == ERROR_SUCCESS) + { + IsOpen = true; + return true; + } + CVLOG(LogRegistry, Error, TEXT("Failed to open registry key: {}\\{} (Error: {})"), GetHiveName(Hive), SubKey, Result); + return false; +} + +void FRegistryKey::Close() +{ + if (IsOpen && KeyHandle) + { + RegCloseKey(KeyHandle); + KeyHandle = nullptr; + IsOpen = false; + } +} + +bool8 FRegistryKey::Delete() +{ + if (!IsValid()) + { + return false; + } + Close(); + const LONG Result = RegDeleteKey(GetHiveHandle(), SubKey.c_str()); + return Result == ERROR_SUCCESS; +} + +bool8 FRegistryKey::HasValue(const FString& ValueName) const +{ + if (!IsOpen) + { + return false; + } + const LONG Result = RegQueryValueEx(KeyHandle, ValueName.c_str(), nullptr, nullptr, nullptr, nullptr); + return Result == ERROR_SUCCESS; +} + +FRegistryValue FRegistryKey::ReadValue(const FString& ValueName) const +{ + FRegistryValue Value; + Value.Name = ValueName; + if (!IsOpen) + { + CVLOG(LogRegistry, Warning, TEXT("Attempting to read from closed registry key")); + return Value; + } + DWORD Type = 0; + DWORD DataSize = 0; + LONG Result = RegQueryValueEx(KeyHandle, ValueName.c_str(), nullptr, &Type, nullptr, &DataSize); + if (Result != ERROR_SUCCESS) + { + return Value; + } + Value.Type = static_cast(Type); + Value.Data.Resize(DataSize); + Result = RegQueryValueEx(KeyHandle, ValueName.c_str(), nullptr, &Type, Value.Data.GetData(), &DataSize); + if (Result != ERROR_SUCCESS) + { + Value.Data.Clear(); + Value.Type = ERegistryValueType::None; + } + return Value; +} + +bool8 FRegistryKey::WriteValue(const FString& ValueName, const FRegistryValue& Value) const +{ + if (!IsOpen) + { + CVLOG(LogRegistry, Warning, TEXT("Attempting to write to closed registry key")); + return false; + } + const LONG Result = RegSetValueEx(KeyHandle, ValueName.c_str(), 0, static_cast(Value.Type), Value.Data.GetData(), static_cast(Value.Data.Num())); + if (Result != ERROR_SUCCESS) + { + CVLOG(LogRegistry, Error, TEXT("Failed to write registry value: {} (Error: {})"), ValueName, Result); + return false; + } + return true; +} + +bool8 FRegistryKey::DeleteValue(const FString& ValueName) const +{ + if (!IsOpen) + { + return false; + } + const LONG Result = RegDeleteValue(KeyHandle, ValueName.c_str()); + return Result == ERROR_SUCCESS; +} + +bool8 FRegistryKey::WriteString(const FString& ValueName, const FString& Value) const +{ + FRegistryValue RegValue; + RegValue.Name = ValueName; + RegValue.Type = ERegistryValueType::String; + const size64 ByteSize = (Value.length() + 1) * sizeof(FChar); + RegValue.Data.Resize(ByteSize); + FMemory::Copy(Value.c_str(), RegValue.Data.GetData(), ByteSize); + return WriteValue(ValueName, RegValue); +} + +bool8 FRegistryKey::WriteDWord(const FString& ValueName, const uint32 Value) const +{ + FRegistryValue RegValue; + RegValue.Name = ValueName; + RegValue.Type = ERegistryValueType::DWord; + RegValue.Data.Resize(sizeof(uint32)); + FMemory::Copy(&Value, RegValue.Data.GetData(), sizeof(uint32)); + return WriteValue(ValueName, RegValue); +} + +bool8 FRegistryKey::WriteQWord(const FString& ValueName, const uint64 Value) const +{ + FRegistryValue RegValue; + RegValue.Name = ValueName; + RegValue.Type = ERegistryValueType::QWord; + RegValue.Data.Resize(sizeof(uint64)); + FMemory::Copy(&Value, RegValue.Data.GetData(), sizeof(uint64)); + return WriteValue(ValueName, RegValue); +} + +bool8 FRegistryKey::WriteBinary(const FString& ValueName, const TArray& Value) const +{ + FRegistryValue RegValue; + RegValue.Name = ValueName; + RegValue.Type = ERegistryValueType::Binary; + RegValue.Data = Value; + return WriteValue(ValueName, RegValue); +} + +bool8 FRegistryKey::WriteMultiString(const FString& ValueName, const TArray& Values) const +{ + FRegistryValue RegValue; + RegValue.Name = ValueName; + RegValue.Type = ERegistryValueType::MultiString; + size64 TotalSize = sizeof(FChar); + for (const auto& Str : Values) + { + TotalSize += (Str.length() + 1) * sizeof(FChar); + } + RegValue.Data.Resize(TotalSize); + uint8* Current = RegValue.Data.GetData(); + for (const auto& Str : Values) + { + const size64 ByteSize = (Str.length() + 1) * sizeof(FChar); + FMemory::Copy(Str.c_str(), Current, ByteSize); + Current += ByteSize; + } + *reinterpret_cast(Current) = 0; + return WriteValue(ValueName, RegValue); +} + +FString FRegistryKey::ReadString(const FString& ValueName, const FString& DefaultValue) const +{ + const FRegistryValue Value = ReadValue(ValueName); + if (Value.Type == ERegistryValueType::String || Value.Type == ERegistryValueType::ExpandString) + { + return Value.AsString(); + } + return DefaultValue; +} + +uint32 FRegistryKey::ReadDWord(const FString& ValueName, const uint32 DefaultValue) const +{ + const FRegistryValue Value = ReadValue(ValueName); + if (Value.Type == ERegistryValueType::DWord) + { + return Value.AsDWord(); + } + return DefaultValue; +} + +uint64 FRegistryKey::ReadQWord(const FString& ValueName, const uint64 DefaultValue) const +{ + const FRegistryValue Value = ReadValue(ValueName); + if (Value.Type == ERegistryValueType::QWord) + { + return Value.AsQWord(); + } + return DefaultValue; +} + +TArray FRegistryKey::ReadBinary(const FString& ValueName) const +{ + const FRegistryValue Value = ReadValue(ValueName); + if (Value.Type == ERegistryValueType::Binary) + { + return Value.AsBinary(); + } + return {}; +} + +TArray FRegistryKey::ReadMultiString(const FString& ValueName) const +{ + const FRegistryValue Value = ReadValue(ValueName); + if (Value.Type == ERegistryValueType::MultiString) + { + return Value.AsMultiString(); + } + return {}; +} + +TArray FRegistryKey::GetSubKeyNames() const +{ + TArray SubKeys; + if (!IsOpen) + { + return SubKeys; + } + FChar KeyName[256]; + DWORD Index = 0; + while (true) + { + DWORD NameSize = 256; + const LONG Result = RegEnumKeyEx(KeyHandle, Index++, KeyName, &NameSize, nullptr, nullptr, nullptr, nullptr); + if (Result == ERROR_NO_MORE_ITEMS) + { + break; + } + if (Result == ERROR_SUCCESS) + { + SubKeys.PushBack(FString(KeyName)); + } + } + return SubKeys; +} + +TArray FRegistryKey::GetValueNames() const +{ + TArray ValueNames; + if (!IsOpen) + { + return ValueNames; + } + FChar ValueName[16384]; + DWORD Index = 0; + while (true) + { + DWORD NameSize = 16384; + const LONG Result = RegEnumValue(KeyHandle, Index++, ValueName, &NameSize, nullptr, nullptr, nullptr, nullptr); + if (Result == ERROR_NO_MORE_ITEMS) + { + break; + } + if (Result == ERROR_SUCCESS) + { + ValueNames.PushBack(FString(ValueName)); + } + } + return ValueNames; +} + +TArray FRegistryKey::GetValues() const +{ + TArray Values; + const TArray ValueNames = GetValueNames(); + for (const auto& Name : ValueNames) + { + FRegistryValue Value = ReadValue(Name); + if (Value.Type != ERegistryValueType::None) + { + Values.PushBack(std::move(Value)); + } + } + return Values; +} + +FString FRegistryKey::GetFullPath() const +{ + return GetHiveName(Hive) + TEXT("\\") + SubKey; +} + +HKEY FRegistryKey::GetHiveHandle() const +{ + return ::GetHiveHandle(Hive); +} + +bool8 FRegistry::KeyExists(const ERegistryHive Hive, const FString& SubKey) +{ + const FRegistryKey Key(Hive, SubKey); + return Key.Exists(); +} + +bool8 FRegistry::CreateKey(const ERegistryHive Hive, const FString& SubKey) +{ + FRegistryKey Key(Hive, SubKey); + return Key.Create(); +} + +bool8 FRegistry::DeleteKey(const ERegistryHive Hive, const FString& SubKey) +{ + FRegistryKey Key(Hive, SubKey); + return Key.Delete(); +} + +bool8 FRegistry::DeleteKeyTree(const ERegistryHive Hive, const FString& SubKey) +{ + const LONG Result = RegDeleteTree(GetHiveHandle(Hive), SubKey.c_str()); + return Result == ERROR_SUCCESS; +} + +FString FRegistry::ReadString(const ERegistryHive Hive, const FString& SubKey, const FString& ValueName, const FString& DefaultValue) +{ + FRegistryKey Key(Hive, SubKey); + if (Key.Open(true)) + { + return Key.ReadString(ValueName, DefaultValue); + } + return DefaultValue; +} + +uint32 FRegistry::ReadDWord(const ERegistryHive Hive, const FString& SubKey, const FString& ValueName, const uint32 DefaultValue) +{ + FRegistryKey Key(Hive, SubKey); + if (Key.Open(true)) + { + return Key.ReadDWord(ValueName, DefaultValue); + } + return DefaultValue; +} + +uint64 FRegistry::ReadQWord(const ERegistryHive Hive, const FString& SubKey, const FString& ValueName, const uint64 DefaultValue) +{ + FRegistryKey Key(Hive, SubKey); + if (Key.Open(true)) + { + return Key.ReadQWord(ValueName, DefaultValue); + } + return DefaultValue; +} + +bool8 FRegistry::WriteString(const ERegistryHive Hive, const FString& SubKey, const FString& ValueName, const FString& Value) +{ + FRegistryKey Key(Hive, SubKey); + if (Key.Create() || Key.Open(false)) + { + return Key.WriteString(ValueName, Value); + } + return false; +} + +bool8 FRegistry::WriteDWord(const ERegistryHive Hive, const FString& SubKey, const FString& ValueName, const uint32 Value) +{ + FRegistryKey Key(Hive, SubKey); + if (Key.Create() || Key.Open(false)) + { + return Key.WriteDWord(ValueName, Value); + } + return false; +} + +bool8 FRegistry::WriteQWord(const ERegistryHive Hive, const FString& SubKey, const FString& ValueName, const uint64 Value) +{ + FRegistryKey Key(Hive, SubKey); + if (Key.Create() || Key.Open(false)) + { + return Key.WriteQWord(ValueName, Value); + } + return false; +} diff --git a/Engine/Source/Runtime/Platform/Public/Platform/CPUDetection.hpp b/Engine/Source/Runtime/Platform/Public/Platform/CPUDetection.hpp new file mode 100644 index 0000000..1591f0a --- /dev/null +++ b/Engine/Source/Runtime/Platform/Public/Platform/CPUDetection.hpp @@ -0,0 +1,41 @@ +// RavenStorm Copyright @ 2025-2025 + +#pragma once + +#include "Core/Containers/String.hpp" + +struct PLATFORM_API FCPUInfo +{ + FAnsiString VendorString; + FAnsiString BrandString; + int32 Family; + int32 Model; + int32 Stepping; + int32 PhysicalCores; + int32 LogicalCores; + int32 CacheLine; + int32 L1CacheSize; + int32 L2CacheSize; + int32 L3CacheSize; + bool8 IsIntel; + bool8 IsAMD; + bool8 IsHyperThreaded; +}; + +class PLATFORM_API FCPUDetection +{ +private: + static void Initialize(); + + [[nodiscard]] static FCPUInfo GetCPUInfo() noexcept; + +private: + static void DetectVendor(); + static void DetectBrand(); + static void DetectCPUTopology(); + +private: + static FCPUInfo CPUInfo; + + friend class PLATFORM_API FPlatform; +}; diff --git a/Engine/Source/Runtime/Platform/Public/Platform/Platform.hpp b/Engine/Source/Runtime/Platform/Public/Platform/Platform.hpp new file mode 100644 index 0000000..fec9f7c --- /dev/null +++ b/Engine/Source/Runtime/Platform/Public/Platform/Platform.hpp @@ -0,0 +1,46 @@ +// RavenStorm Copyright @ 2025-2025 + +#pragma once + +#include "CPUDetection.hpp" + +enum class PLATFORM_API EWindowsVersion : uint8 +{ + Unknown = 0, + Windows10, + Windows11, +}; + +struct PLATFORM_API FSystemInfo +{ + FString OSName; + FString OSVersion; + FString OSBuildNumber; + EWindowsVersion WindowsVersion; + FString ComputerName; +}; + +class PLATFORM_API FPlatform +{ +public: + static void Initialize(); + static void Shutdown(); + + [[nodiscard]] static bool8 HasEnvVariable(const FString& VariableName) noexcept; + [[nodiscard]] static FString GetEnvVariable(const FString& VariableName) noexcept; + [[nodiscard]] static FString GetEnvVariable(const FString& VariableName, const FString& DefaultValue) noexcept; + static bool8 SetEnvVariable(const FString& VariableName, const FString& Value) noexcept; + + [[nodiscard]] static FCPUInfo GetCPUInfo() noexcept; + [[nodiscard]] static FSystemInfo GetSystemInfo() noexcept; + +private: + static void DetectOperatingSystem(); + static void DetectComputerInfo(); + + static EWindowsVersion DetermineWindowsVersion(uint32 MajorVersion, uint32 BuildNumber); + static FString GetWindowsVersionString(uint32 MajorVersion, uint32 BuildNumber); + +private: + static FSystemInfo SystemInfo; +}; diff --git a/Engine/Source/Runtime/Platform/Public/Platform/Registry.hpp b/Engine/Source/Runtime/Platform/Public/Platform/Registry.hpp new file mode 100644 index 0000000..5f68d2d --- /dev/null +++ b/Engine/Source/Runtime/Platform/Public/Platform/Registry.hpp @@ -0,0 +1,119 @@ +// RavenStorm Copyright @ 2025-2025 + +#pragma once + +#include + +#include "Core/CoreDefinitions.hpp" +#include "Core/Containers/Array.hpp" +#include "Core/Containers/String.hpp" + +enum class PLATFORM_API ERegistryHive : uint32 +{ + Unknown = 0, + ClassesRoot = 0x80000000, + CurrentUser = 0x80000001, + CurrentConfig = 0x80000005, + LocalMachine = 0x80000002, + Users = 0x80000003, +}; + +enum class PLATFORM_API ERegistryValueType : uint8 +{ + None = REG_NONE, + String = REG_SZ, + ExpandString = REG_EXPAND_SZ, + Binary = REG_BINARY, + DWord = REG_DWORD, + QWord = REG_QWORD, + MultiString = REG_MULTI_SZ, +}; + +struct PLATFORM_API FRegistryValue +{ +public: + FString Name; + ERegistryValueType Type = ERegistryValueType::None; + TArray Data; + +public: + [[nodiscard]] FString AsString() const; + [[nodiscard]] uint32 AsDWord() const; + [[nodiscard]] uint64 AsQWord() const; + [[nodiscard]] TArray AsMultiString() const; + [[nodiscard]] TArray AsBinary() const; +}; + +struct PLATFORM_API FRegistryKey +{ +public: + FRegistryKey() = default; + FRegistryKey(ERegistryHive InHive, FString InSubKey); + ~FRegistryKey(); + + NON_COPYABLE(FRegistryKey) + DEFAULT_MOVEABLE(FRegistryKey) + +public: + [[nodiscard]] bool8 IsValid() const noexcept; + [[nodiscard]] bool8 Exists() const noexcept; + + bool8 Create(); + bool8 Open(bool8 ReadOnly = false); + void Close(); + bool8 Delete(); + + [[nodiscard]] bool8 HasValue(const FString& ValueName) const; + [[nodiscard]] FRegistryValue ReadValue(const FString& ValueName) const; + bool8 WriteValue(const FString& ValueName, const FRegistryValue& Value) const; + bool8 DeleteValue(const FString& ValueName) const; + + bool8 WriteString(const FString& ValueName, const FString& Value) const; + bool8 WriteDWord(const FString& ValueName, uint32 Value) const; + bool8 WriteQWord(const FString& ValueName, uint64 Value) const; + bool8 WriteBinary(const FString& ValueName, const TArray& Value) const; + bool8 WriteMultiString(const FString& ValueName, const TArray& Values) const; + + [[nodiscard]] FString ReadString(const FString& ValueName, const FString& DefaultValue = {}) const; + [[nodiscard]] uint32 ReadDWord(const FString& ValueName, uint32 DefaultValue = 0) const; + [[nodiscard]] uint64 ReadQWord(const FString& ValueName, uint64 DefaultValue = 0) const; + [[nodiscard]] TArray ReadBinary(const FString& ValueName) const; + [[nodiscard]] TArray ReadMultiString(const FString& ValueName) const; + + [[nodiscard]] TArray GetSubKeyNames() const; + [[nodiscard]] TArray GetValueNames() const; + [[nodiscard]] TArray GetValues() const; + + [[nodiscard]] FString GetFullPath() const; + +public: + [[nodiscard]] ERegistryHive GetHive() const { return Hive; } + [[nodiscard]] FString GetSubKey() const { return SubKey; } + +private: + [[nodiscard]] HKEY GetHiveHandle() const; + +private: + ERegistryHive Hive = ERegistryHive::Unknown; + FString SubKey; + HKEY KeyHandle = nullptr; + bool8 IsOpen = false; +}; + +class PLATFORM_API FRegistry +{ +public: + [[nodiscard]] static bool8 KeyExists(ERegistryHive Hive, const FString& SubKey); + + static bool8 CreateKey(ERegistryHive Hive, const FString& SubKey); + static bool8 DeleteKey(ERegistryHive Hive, const FString& SubKey); + static bool8 DeleteKeyTree(ERegistryHive Hive, const FString& SubKey); + + [[nodiscard]] static FString ReadString(ERegistryHive Hive, const FString& SubKey, const FString& ValueName, const FString& DefaultValue = {}); + [[nodiscard]] static uint32 ReadDWord(ERegistryHive Hive, const FString& SubKey, const FString& ValueName, uint32 DefaultValue = 0); + [[nodiscard]] static uint64 ReadQWord(ERegistryHive Hive, const FString& SubKey, const FString& ValueName, uint64 DefaultValue = 0); + + static bool8 WriteString(ERegistryHive Hive, const FString& SubKey, const FString& ValueName, const FString& Value); + static bool8 WriteDWord(ERegistryHive Hive, const FString& SubKey, const FString& ValueName, uint32 Value); + static bool8 WriteQWord(ERegistryHive Hive, const FString& SubKey, const FString& ValueName, uint64 Value); +}; diff --git a/Engine/Source/Runtime/Platform/Public/PlatformAPI.hpp b/Engine/Source/Runtime/Platform/Public/PlatformAPI.hpp new file mode 100644 index 0000000..bfb291b --- /dev/null +++ b/Engine/Source/Runtime/Platform/Public/PlatformAPI.hpp @@ -0,0 +1,13 @@ +// RavenStorm Copyright @ 2025-2025 + +#pragma once + +#ifdef CORVUS_BUILD_MODULAR +# ifdef CORVUS_BUILD_PLATFORM +# define PLATFORM_API __declspec(dllexport) +# else +# define PLATFORM_API __declspec(dllimport) +# endif +#else +# define PLATFORM_API +#endif diff --git a/Engine/Source/Runtime/Platform/xmake.lua b/Engine/Source/Runtime/Platform/xmake.lua new file mode 100644 index 0000000..dd548d3 --- /dev/null +++ b/Engine/Source/Runtime/Platform/xmake.lua @@ -0,0 +1,11 @@ +local module_name = 'Platform' + +corvus_engine_target(module_name) + add_deps('Core') +corvus_target_end() + +-- Tests +-- corvus_test_target(module_name) +-- add_deps('Platform') +-- corvus_target_end() +