From 5c9a3256d67e66f5c1fd6a2538453d51bac1d0c4 Mon Sep 17 00:00:00 2001 From: Jordon Brooks Date: Wed, 15 May 2024 16:20:05 +0100 Subject: [PATCH] Working loading, Saving and default config functions --- Source/HarmonyLink/HarmonyLink.Build.cs | 2 + .../Private/Objects/HarmonyLinkGraphics.cpp | 271 +++++++++++++----- Source/HarmonyLink/Public/Enums/Profile.h | 18 ++ .../Public/Objects/HarmonyLinkGraphics.h | 44 ++- .../Public/Structs/HLConfigValue.h | 52 ++++ .../Public/Structs/SettingsProfile.h | 45 +++ 6 files changed, 335 insertions(+), 97 deletions(-) create mode 100644 Source/HarmonyLink/Public/Enums/Profile.h create mode 100644 Source/HarmonyLink/Public/Structs/SettingsProfile.h diff --git a/Source/HarmonyLink/HarmonyLink.Build.cs b/Source/HarmonyLink/HarmonyLink.Build.cs index 75e3165..725ae7e 100644 --- a/Source/HarmonyLink/HarmonyLink.Build.cs +++ b/Source/HarmonyLink/HarmonyLink.Build.cs @@ -8,6 +8,8 @@ public class HarmonyLink : ModuleRules public HarmonyLink(ReadOnlyTargetRules Target) : base(Target) { PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; + + IWYUSupport = IWYUSupport.Full; PublicIncludePaths.AddRange( new string[] { diff --git a/Source/HarmonyLink/Private/Objects/HarmonyLinkGraphics.cpp b/Source/HarmonyLink/Private/Objects/HarmonyLinkGraphics.cpp index 7268be3..42fb8d1 100644 --- a/Source/HarmonyLink/Private/Objects/HarmonyLinkGraphics.cpp +++ b/Source/HarmonyLink/Private/Objects/HarmonyLinkGraphics.cpp @@ -7,108 +7,152 @@ UHarmonyLinkGraphics* UHarmonyLinkGraphics::Instance = nullptr; FString UHarmonyLinkGraphics::IniLocation = "HarmonyLink"; -FName UHarmonyLinkGraphics::BatteryProfile = "Battery"; -FName UHarmonyLinkGraphics::ChargingProfile = "Charging"; -FName UHarmonyLinkGraphics::DockedProfile = "Docked"; -TMap UHarmonyLinkGraphics::DefaultSettingsMap = {{"Test", 0}}; +TMap> UHarmonyLinkGraphics::DefaultSettings = { + { "Battery", { + { TEXT("r.ReflectionMethod"), FHLConfigValue(0) }, + { TEXT("r.DynamicGlobalIlluminationMethod"), FHLConfigValue(2) }, + { TEXT("r.Shadow.Virtual.Enable"), FHLConfigValue(0) } + }}, + { "Charging", { + { TEXT("r.ReflectionMethod"), FHLConfigValue(2) }, + { TEXT("r.DynamicGlobalIlluminationMethod"), FHLConfigValue(2) }, + { TEXT("r.Shadow.Virtual.Enable"), FHLConfigValue(1) } + }}, + { "Docked", { + { TEXT("r.ReflectionMethod"), FHLConfigValue(2) }, + { TEXT("r.DynamicGlobalIlluminationMethod"), FHLConfigValue(2) }, + { TEXT("r.Shadow.Virtual.Enable"), FHLConfigValue(1) } + }} +}; UHarmonyLinkGraphics::UHarmonyLinkGraphics() { UE_LOG(LogHarmonyLink, Warning, TEXT("HarmonyLinkGraphics initialized.")); - - - // Initialize settings for Low Graphics Profile - BatterySettings.Add(TEXT("LowResolutionX"), FHLConfigValue(1280)); - BatterySettings.Add(TEXT("LowResolutionY"), FHLConfigValue(720)); - BatterySettings.Add(TEXT("LowTextureQuality"), FHLConfigValue(0.5f)); - - // Initialize settings for Medium Graphics Profile - ChargingSettings.Add(TEXT("MediumResolutionX"), FHLConfigValue(1920)); - ChargingSettings.Add(TEXT("MediumResolutionY"), FHLConfigValue(1080)); - ChargingSettings.Add(TEXT("MediumTextureQuality"), FHLConfigValue(0.75f)); - - // Initialize settings for High Graphics Profile - DockedSettings.Add(TEXT("HighResolutionX"), FHLConfigValue(3840)); - DockedSettings.Add(TEXT("HighResolutionY"), FHLConfigValue(2160)); - DockedSettings.Add(TEXT("HighTextureQuality"), FHLConfigValue(1.0f)); } -void UHarmonyLinkGraphics::LoadProfile(const FName& ProfileName, const bool bForceReload) +void UHarmonyLinkGraphics::LoadConfig(const bool bForceReload) { QUICK_SCOPE_CYCLE_COUNTER(HarmonyLinkGraphics_LoadSettings); - // Construct the section name - FString SectionName = FString::Printf(TEXT("GraphicsProfile.%s"), *ProfileName.ToString()); - - // Clear the previous settings - SettingsMap.Empty(); - // Load the settings into the map - if (!LoadSettingsFromConfig(SectionName)) + if (!LoadSettingsFromConfig()) { // Retry 2nd time - LoadSettingsFromConfig(SectionName); + LoadSettingsFromConfig(); } + + DebugPrintProfiles(); } -bool UHarmonyLinkGraphics::LoadSettingsFromConfig(const FString& SectionName) +bool UHarmonyLinkGraphics::LoadSettingsFromConfig() { - // Load the configuration for the specified profile + // Load the configuration from the INI file FConfigFile ConfigFile; - // Normalize the INI file path - const FString IniFilePath = FPaths::Combine(FPaths::ProjectConfigDir(), IniLocation + TEXT(".ini")); - const FString NormalizedIniFilePath = FConfigCacheIni::NormalizeConfigIniPath(IniFilePath); + const FString Filename = GetDefaultConfigFilename(); - if (FConfigCacheIni::LoadLocalIniFile(ConfigFile, *IniLocation, true, nullptr, false)) + if (FConfigCacheIni::LoadLocalIniFile(ConfigFile, *Filename, true, nullptr, false)) { - if (const FConfigSection* Section = ConfigFile.Find(*SectionName)) - { - for (const auto& ValueIt : *Section) - { - int32 Value = FCString::Atoi(*ValueIt.Value.GetValue()); - SettingsMap.Add(*ValueIt.Key.ToString(), Value); - } + // Load each profile section + bool bLoaded = true; + for (const TPair& Profile : ProfileNames) + { + if (!LoadSection(ConfigFile, Profile)) + { + UE_LOG(LogHarmonyLink, Error, TEXT("Failed to load section: '%s'"), *Profile.Value.ToString()); + bLoaded = false; + } + } + + // Check if all profiles were loaded successfully + if (bLoaded) + { + UE_LOG(LogHarmonyLink, Log, TEXT("Successfully loaded config file: %s"), *Filename); return true; } } + UE_LOG(LogHarmonyLink, Error, TEXT("Failed to load config file: %s"), *Filename); CreateDefaultConfigFile(); return false; } -void UHarmonyLinkGraphics::SaveProfile(const FName& ProfileName) +bool UHarmonyLinkGraphics::LoadSection(const FConfigFile& ConfigFile, const TPair Profile) { - QUICK_SCOPE_CYCLE_COUNTER(HarmonyLinkGraphics_SaveSettings); + const FName& SectionName = Profile.Value; + const EProfile ProfileKey = Profile.Key; - // Normalize the INI file path - const FString IniFilePath = FPaths::Combine(FPaths::ProjectConfigDir(), IniLocation + TEXT(".ini")); - const FString NormalizedIniFilePath = FConfigCacheIni::NormalizeConfigIniPath(IniFilePath); - - FConfigFile ConfigFile; - ConfigFile.Write(NormalizedIniFilePath); + if (const FConfigSection* Section = ConfigFile.FindSection(*SectionName.ToString())) + { + FSettingsProfile& SettingsProfile = Profiles.FindOrAdd(ProfileKey); + SettingsProfile.SectionName = SectionName; + SettingsProfile.Settings.Empty(); // Clear previous settings + + for (const auto& ValueIt : *Section) + { + FString ValueString = ValueIt.Value.GetValue(); + FString Value, Type; + + // Parse the Value and Type from the string + if (ValueString.Split(TEXT(", Type="), &Value, &Type)) + { + Value = Value.RightChop(7); // Remove "(Value=" prefix + Type = Type.LeftChop(1); // Remove ")" suffix + + if (Type == "int") + { + const int32 IntValue = FCString::Atoi(*Value); + SettingsProfile.Settings.Add(ValueIt.Key, FHLConfigValue(IntValue)); + } + else if (Type == "float") + { + const float FloatValue = FCString::Atof(*Value); + SettingsProfile.Settings.Add(ValueIt.Key, FHLConfigValue(FloatValue)); + } + else if (Type == "bool") + { + const bool BoolValue = Value == "true"; + SettingsProfile.Settings.Add(ValueIt.Key, FHLConfigValue(BoolValue)); + } + else if (Type == "string") + { + SettingsProfile.Settings.Add(ValueIt.Key, FHLConfigValue(Value)); + } + } + } + + return true; + } + + return false; +} + +void UHarmonyLinkGraphics::SaveConfig() const +{ + QUICK_SCOPE_CYCLE_COUNTER(HarmonyLinkGraphics_SaveConfig); + + for (TPair Profile : Profiles) + { + SaveSection(Profile.Value); + } + + const FString Filename = GetDefaultConfigFilename(); + UE_LOG(LogHarmonyLink, Log, TEXT("Flushing file: '%s'"), *Filename); + GConfig->Flush(false, Filename); } void UHarmonyLinkGraphics::ApplySettings(const bool bCheckForCommandLineOverrides) { { FGlobalComponentRecreateRenderStateContext Context; - ApplyResolutionSettings(bCheckForCommandLineOverrides); - ApplyNonResolutionSettings(); + //ApplyResolutionSettings(bCheckForCommandLineOverrides); + //ApplyNonResolutionSettings(); } - - SaveProfile(_ProfileName); } -void UHarmonyLinkGraphics::ApplyNonResolutionSettings() -{ -} -void UHarmonyLinkGraphics::ApplyResolutionSettings(bool bCheckForCommandLineOverrides) -{ -} UHarmonyLinkGraphics* UHarmonyLinkGraphics::GetSettings() { @@ -116,7 +160,7 @@ UHarmonyLinkGraphics* UHarmonyLinkGraphics::GetSettings() { Instance = NewObject(); Instance->AddToRoot(); - Instance->LoadProfile(ChargingProfile); + Instance->LoadConfig(); } return Instance; @@ -134,39 +178,120 @@ void UHarmonyLinkGraphics::DestroySettings() void UHarmonyLinkGraphics::CreateDefaultConfigFile() { - UE_LOG(LogHarmonyLink, Log, TEXT("CreateDefaultConfigFile started.")); + UE_LOG(LogHarmonyLink, Log, TEXT("Creating default config file.")); + + LoadDefaults(); + SaveConfig(); - SaveSection(BatteryProfile, BatterySettings); - SaveSection(ChargingProfile, ChargingSettings); - SaveSection(DockedProfile, DockedSettings); + UE_LOG(LogHarmonyLink, Log, TEXT("Default config file created.")); } -void UHarmonyLinkGraphics::SaveSection(const FName& SectionName, const TMap& Settings) const +void UHarmonyLinkGraphics::SaveSection(FSettingsProfile& SettingsProfile, const bool bFlush) const { if (GConfig) { const FString Filename = GetDefaultConfigFilename(); - for (const auto& Setting : Settings) + for (const auto& Setting : SettingsProfile.Settings) { + FString TypeString; + FString ValueString; + switch (Setting.Value.GetType()) { case EConfigValueType::Int: - GConfig->SetInt(*SectionName.ToString(), *Setting.Key.ToString(), Setting.Value.GetValue(), Filename); + ValueString = FString::FromInt(Setting.Value.GetValue()); + TypeString = TEXT("int"); break; case EConfigValueType::Float: - GConfig->SetFloat(*SectionName.ToString(), *Setting.Key.ToString(), Setting.Value.GetValue(), Filename); + ValueString = FString::SanitizeFloat(Setting.Value.GetValue()); + TypeString = TEXT("float"); break; case EConfigValueType::Bool: - GConfig->SetBool(*SectionName.ToString(), *Setting.Key.ToString(), Setting.Value.GetValue(), Filename); + ValueString = Setting.Value.GetValue() ? TEXT("true") : TEXT("false"); + TypeString = TEXT("bool"); break; case EConfigValueType::String: - GConfig->SetString(*SectionName.ToString(), *Setting.Key.ToString(), *Setting.Value.GetValue(), Filename); + ValueString = Setting.Value.GetValue(); + TypeString = TEXT("string"); break; } + + const FString ConfigValue = FString::Printf(TEXT("(Value=%s, Type=%s)"), *ValueString, *TypeString); + GConfig->SetString(*SettingsProfile.SectionName.ToString(), *Setting.Key.ToString(), *ConfigValue, Filename); } - UE_LOG(LogHarmonyLink, Log, TEXT("Saving config file: '%s'"), *Filename) - GConfig->Flush(false, Filename); + UE_LOG(LogHarmonyLink, Log, TEXT("Saving config file: '%s'"), *Filename); + if (bFlush) + { + UE_LOG(LogHarmonyLink, Log, TEXT("Flushing file: '%s'"), *Filename); + GConfig->Flush(false, Filename); + } + } +} + +void UHarmonyLinkGraphics::LoadDefaults() +{ + UE_LOG(LogHarmonyLink, Log, TEXT("LoadDefaults started.")); + + Profiles.Reset(); + + // Iterate over ProfileNames to create default settings + for (const TPair& Profile : ProfileNames) + { + FSettingsProfile NewProfileSettings; + NewProfileSettings.SectionName = Profile.Value; + + if (const TMap* Settings = DefaultSettings.Find(Profile.Value)) + { + NewProfileSettings.Settings = *Settings; + } + + Profiles.Add(Profile.Key, NewProfileSettings); + } +} + +void UHarmonyLinkGraphics::DebugPrintProfiles() const +{ + UE_LOG(LogHarmonyLink, Log, TEXT("DebugPrintProfiles started.")); + + for (TPair Profile : Profiles) + { + PrintDebugSection(Profile.Value); + } + + UE_LOG(LogHarmonyLink, Log, TEXT("DebugPrintProfiles completed.")); +} + +void UHarmonyLinkGraphics::PrintDebugSection(FSettingsProfile& SettingsProfile) +{ + UE_LOG(LogHarmonyLink, Warning, TEXT("[%s]"), *SettingsProfile.SectionName.ToString()); + + for (const auto& Setting : SettingsProfile.Settings) + { + FString ValueString; + FString TypeString; + + switch (Setting.Value.GetType()) + { + case EConfigValueType::Int: + ValueString = FString::FromInt(Setting.Value.GetValue()); + TypeString = TEXT("int"); + break; + case EConfigValueType::Float: + ValueString = FString::SanitizeFloat(Setting.Value.GetValue()); + TypeString = TEXT("float"); + break; + case EConfigValueType::Bool: + ValueString = Setting.Value.GetValue() ? TEXT("true") : TEXT("false"); + TypeString = TEXT("bool"); + break; + case EConfigValueType::String: + ValueString = Setting.Value.GetValue(); + TypeString = TEXT("string"); + break; + } + + UE_LOG(LogHarmonyLink, Warning, TEXT("Key: %s = V=%s, T=%s "), *Setting.Key.ToString(), *ValueString, *TypeString); } } diff --git a/Source/HarmonyLink/Public/Enums/Profile.h b/Source/HarmonyLink/Public/Enums/Profile.h new file mode 100644 index 0000000..6985b45 --- /dev/null +++ b/Source/HarmonyLink/Public/Enums/Profile.h @@ -0,0 +1,18 @@ +// Copyright (C) 2024 Jordon Brooks + +#pragma once + +#include "CoreMinimal.h" +#include "Profile.generated.h" + +/* + * Enum representing different operating system platforms. + */ +UENUM(BlueprintType) +enum class EProfile : uint8 +{ + NONE UMETA(DisplayName = "NONE"), + BATTERY UMETA(DisplayName = "BATTERY"), + CHARGING UMETA(DisplayName = "CHARGING"), + DOCKED UMETA(DisplayName = "DOCKED"), +}; diff --git a/Source/HarmonyLink/Public/Objects/HarmonyLinkGraphics.h b/Source/HarmonyLink/Public/Objects/HarmonyLinkGraphics.h index 545bbfb..ad76529 100644 --- a/Source/HarmonyLink/Public/Objects/HarmonyLinkGraphics.h +++ b/Source/HarmonyLink/Public/Objects/HarmonyLinkGraphics.h @@ -3,7 +3,8 @@ #pragma once #include "CoreMinimal.h" -#include "Structs/HLConfigValue.h" +#include "Enums/Profile.h" +#include "Structs/SettingsProfile.h" #include "UObject/Object.h" #include "HarmonyLinkGraphics.generated.h" @@ -20,20 +21,14 @@ public: UHarmonyLinkGraphics(); UFUNCTION(BlueprintCallable, Category="HarmonyLink Settings") - void LoadProfile(const FName& ProfileName, const bool bForceReload = false); + void LoadConfig(const bool bForceReload = false); UFUNCTION(BlueprintCallable, Category="HarmonyLink Settings") - void SaveProfile(const FName& ProfileName); + void SaveConfig() const; UFUNCTION(BlueprintCallable, Category="HarmonyLink Settings", meta=(bCheckForCommandLineOverrides=true)) void ApplySettings(bool bCheckForCommandLineOverrides = true); - UFUNCTION(BlueprintCallable, Category="HarmonyLink Settings") - void ApplyNonResolutionSettings(); - - UFUNCTION(BlueprintCallable, Category="HarmonyLink Settings") - void ApplyResolutionSettings(bool bCheckForCommandLineOverrides); - /** Returns the game local machine settings (resolution, windowing mode, scalability settings, etc...) */ UFUNCTION(BlueprintCallable, Category="HarmonyLink Settings") static UHarmonyLinkGraphics* GetSettings(); @@ -42,29 +37,30 @@ public: private: void CreateDefaultConfigFile(); - bool LoadSettingsFromConfig(const FString& SectionName); + bool LoadSettingsFromConfig(); - void SaveSection(const FName& SectionName, const TMap& Settings) const; + bool LoadSection(const FConfigFile& ConfigFile, const TPair Profile); + + void SaveSection(FSettingsProfile& SettingsProfile, const bool bFlush = false) const; + + void LoadDefaults(); + + void DebugPrintProfiles() const; + static void PrintDebugSection(FSettingsProfile& SettingsProfile); static void ResetInstance(); - UPROPERTY(Config) - FName _ProfileName = NAME_None; - static FString IniLocation; - - static FName BatteryProfile; - static FName ChargingProfile; - static FName DockedProfile; - TMap SettingsMap; + TMap ProfileNames = { + {EProfile::BATTERY, "Battery"}, + {EProfile::CHARGING, "Charging"}, + {EProfile::DOCKED, "Docked"}, + }; - // Maps to store configuration settings for each profile - TMap BatterySettings; - TMap ChargingSettings; - TMap DockedSettings; + TMap Profiles; - static TMap DefaultSettingsMap; + static TMap> DefaultSettings; static UHarmonyLinkGraphics* Instance; }; diff --git a/Source/HarmonyLink/Public/Structs/HLConfigValue.h b/Source/HarmonyLink/Public/Structs/HLConfigValue.h index 1a0cd2f..daf99d2 100644 --- a/Source/HarmonyLink/Public/Structs/HLConfigValue.h +++ b/Source/HarmonyLink/Public/Structs/HLConfigValue.h @@ -53,6 +53,34 @@ public: template T GetValue() const; + + // Equality operators + bool operator==(const FHLConfigValue& Other) const + { + if (Type != Other.Type) + { + return false; + } + + switch (Type) + { + case EConfigValueType::Int: + return IntValue == Other.IntValue; + case EConfigValueType::Float: + return FloatValue == Other.FloatValue; + case EConfigValueType::Bool: + return BoolValue == Other.BoolValue; + case EConfigValueType::String: + return StringValue == Other.StringValue; + default: + return false; + } + } + + bool operator!=(const FHLConfigValue& Other) const + { + return !(*this == Other); + } }; // Specializations of the templated getter @@ -83,3 +111,27 @@ inline FString FHLConfigValue::GetValue() const ensure(Type == EConfigValueType::String); return StringValue; } + +// Hash function +FORCEINLINE uint32 GetTypeHash(const FHLConfigValue& Value) +{ + uint32 Hash = GetTypeHash(Value.GetType()); + + switch (Value.GetType()) + { + case EConfigValueType::Int: + Hash = HashCombine(Hash, GetTypeHash(Value.GetValue())); + break; + case EConfigValueType::Float: + Hash = HashCombine(Hash, GetTypeHash(Value.GetValue())); + break; + case EConfigValueType::Bool: + Hash = HashCombine(Hash, GetTypeHash(Value.GetValue())); + break; + case EConfigValueType::String: + Hash = HashCombine(Hash, GetTypeHash(Value.GetValue())); + break; + } + + return Hash; +} diff --git a/Source/HarmonyLink/Public/Structs/SettingsProfile.h b/Source/HarmonyLink/Public/Structs/SettingsProfile.h new file mode 100644 index 0000000..d38d146 --- /dev/null +++ b/Source/HarmonyLink/Public/Structs/SettingsProfile.h @@ -0,0 +1,45 @@ +// Copyright (C) 2024 Jordon Brooks + +#pragma once + +#include "CoreMinimal.h" +#include "HLConfigValue.h" + +#include "SettingsProfile.generated.h" + +USTRUCT(BlueprintType) +struct FSettingsProfile +{ + GENERATED_BODY() + + UPROPERTY() + FName SectionName; + + UPROPERTY() + TMap Settings; + + // Equality operators + bool operator==(const FSettingsProfile& Other) const + { + return SectionName == Other.SectionName && Settings.OrderIndependentCompareEqual(Other.Settings); + } + + bool operator!=(const FSettingsProfile& Other) const + { + return !(*this == Other); + } +}; + +// Hash function +FORCEINLINE uint32 GetTypeHash(const FSettingsProfile& Profile) +{ + uint32 Hash = GetTypeHash(Profile.SectionName); + + for (const auto& Pair : Profile.Settings) + { + Hash = HashCombine(Hash, GetTypeHash(Pair.Key)); + Hash = HashCombine(Hash, GetTypeHash(Pair.Value)); + } + + return Hash; +}