diff --git a/Source/HarmonyLink/Private/HarmonyLink.cpp b/Source/HarmonyLink/Private/HarmonyLink.cpp index 88e7005..b508013 100644 --- a/Source/HarmonyLink/Private/HarmonyLink.cpp +++ b/Source/HarmonyLink/Private/HarmonyLink.cpp @@ -2,15 +2,20 @@ #include "HarmonyLink.h" #include "Modules/ModuleManager.h" +#include "Objects/HarmonyLinkGraphics.h" #define LOCTEXT_NAMESPACE "FHarmonyLinkModule" +DEFINE_LOG_CATEGORY(LogHarmonyLink); + void FHarmonyLinkModule::StartupModule() { + UHarmonyLinkGraphics::GetSettings(); } void FHarmonyLinkModule::ShutdownModule() { + UHarmonyLinkGraphics::DestroySettings(); } #undef LOCTEXT_NAMESPACE diff --git a/Source/HarmonyLink/Private/Objects/HarmonyLinkGraphics.cpp b/Source/HarmonyLink/Private/Objects/HarmonyLinkGraphics.cpp new file mode 100644 index 0000000..7268be3 --- /dev/null +++ b/Source/HarmonyLink/Private/Objects/HarmonyLinkGraphics.cpp @@ -0,0 +1,177 @@ +// Copyright (C) 2024 Jordon Brooks + + +#include "Objects/HarmonyLinkGraphics.h" +#include "ComponentRecreateRenderStateContext.h" +#include "HarmonyLink.h" + +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}}; + +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) +{ + 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)) + { + // Retry 2nd time + LoadSettingsFromConfig(SectionName); + } +} + +bool UHarmonyLinkGraphics::LoadSettingsFromConfig(const FString& SectionName) +{ + // Load the configuration for the specified profile + FConfigFile ConfigFile; + + // Normalize the INI file path + const FString IniFilePath = FPaths::Combine(FPaths::ProjectConfigDir(), IniLocation + TEXT(".ini")); + const FString NormalizedIniFilePath = FConfigCacheIni::NormalizeConfigIniPath(IniFilePath); + + if (FConfigCacheIni::LoadLocalIniFile(ConfigFile, *IniLocation, 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); + } + + return true; + } + } + + CreateDefaultConfigFile(); + return false; +} + +void UHarmonyLinkGraphics::SaveProfile(const FName& ProfileName) +{ + QUICK_SCOPE_CYCLE_COUNTER(HarmonyLinkGraphics_SaveSettings); + + // 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); +} + +void UHarmonyLinkGraphics::ApplySettings(const bool bCheckForCommandLineOverrides) +{ + { + FGlobalComponentRecreateRenderStateContext Context; + ApplyResolutionSettings(bCheckForCommandLineOverrides); + ApplyNonResolutionSettings(); + } + + SaveProfile(_ProfileName); +} + +void UHarmonyLinkGraphics::ApplyNonResolutionSettings() +{ +} + +void UHarmonyLinkGraphics::ApplyResolutionSettings(bool bCheckForCommandLineOverrides) +{ +} + +UHarmonyLinkGraphics* UHarmonyLinkGraphics::GetSettings() +{ + if (!Instance) + { + Instance = NewObject(); + Instance->AddToRoot(); + Instance->LoadProfile(ChargingProfile); + } + + return Instance; +} + +void UHarmonyLinkGraphics::DestroySettings() +{ + if (Instance) + { + Instance->RemoveFromRoot(); + Instance->MarkAsGarbage(); + Instance = nullptr; + } +} + +void UHarmonyLinkGraphics::CreateDefaultConfigFile() +{ + UE_LOG(LogHarmonyLink, Log, TEXT("CreateDefaultConfigFile started.")); + + SaveSection(BatteryProfile, BatterySettings); + SaveSection(ChargingProfile, ChargingSettings); + SaveSection(DockedProfile, DockedSettings); +} + +void UHarmonyLinkGraphics::SaveSection(const FName& SectionName, const TMap& Settings) const +{ + if (GConfig) + { + const FString Filename = GetDefaultConfigFilename(); + for (const auto& Setting : Settings) + { + switch (Setting.Value.GetType()) + { + case EConfigValueType::Int: + GConfig->SetInt(*SectionName.ToString(), *Setting.Key.ToString(), Setting.Value.GetValue(), Filename); + break; + case EConfigValueType::Float: + GConfig->SetFloat(*SectionName.ToString(), *Setting.Key.ToString(), Setting.Value.GetValue(), Filename); + break; + case EConfigValueType::Bool: + GConfig->SetBool(*SectionName.ToString(), *Setting.Key.ToString(), Setting.Value.GetValue(), Filename); + break; + case EConfigValueType::String: + GConfig->SetString(*SectionName.ToString(), *Setting.Key.ToString(), *Setting.Value.GetValue(), Filename); + break; + } + } + + UE_LOG(LogHarmonyLink, Log, TEXT("Saving config file: '%s'"), *Filename) + GConfig->Flush(false, Filename); + } +} + +void UHarmonyLinkGraphics::ResetInstance() +{ + Instance->DestroySettings(); + GetSettings(); +} diff --git a/Source/HarmonyLink/Private/Structs/HLConfigValue.cpp b/Source/HarmonyLink/Private/Structs/HLConfigValue.cpp new file mode 100644 index 0000000..16f5699 --- /dev/null +++ b/Source/HarmonyLink/Private/Structs/HLConfigValue.cpp @@ -0,0 +1,3 @@ +// Copyright (C) 2024 Jordon Brooks + +#include "Structs/HLConfigValue.h" diff --git a/Source/HarmonyLink/Public/HarmonyLink.h b/Source/HarmonyLink/Public/HarmonyLink.h index 9ee5be3..6cb8597 100644 --- a/Source/HarmonyLink/Public/HarmonyLink.h +++ b/Source/HarmonyLink/Public/HarmonyLink.h @@ -4,6 +4,8 @@ #include "Modules/ModuleManager.h" +DECLARE_LOG_CATEGORY_EXTERN(LogHarmonyLink, Log, All); + class FHarmonyLinkModule : public IModuleInterface { public: diff --git a/Source/HarmonyLink/Public/Objects/HarmonyLinkGraphics.h b/Source/HarmonyLink/Public/Objects/HarmonyLinkGraphics.h new file mode 100644 index 0000000..545bbfb --- /dev/null +++ b/Source/HarmonyLink/Public/Objects/HarmonyLinkGraphics.h @@ -0,0 +1,70 @@ +// Copyright (C) 2024 Jordon Brooks + +#pragma once + +#include "CoreMinimal.h" +#include "Structs/HLConfigValue.h" + +#include "UObject/Object.h" +#include "HarmonyLinkGraphics.generated.h" + +/** + * + */ +UCLASS(Blueprintable, config="HarmonyLink") +class HARMONYLINK_API UHarmonyLinkGraphics : public UObject +{ + GENERATED_BODY() + +public: + UHarmonyLinkGraphics(); + + UFUNCTION(BlueprintCallable, Category="HarmonyLink Settings") + void LoadProfile(const FName& ProfileName, const bool bForceReload = false); + + UFUNCTION(BlueprintCallable, Category="HarmonyLink Settings") + void SaveProfile(const FName& ProfileName); + + 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(); + + static void DestroySettings(); + +private: + void CreateDefaultConfigFile(); + bool LoadSettingsFromConfig(const FString& SectionName); + + void SaveSection(const FName& SectionName, const TMap& Settings) const; + + static void ResetInstance(); + + UPROPERTY(Config) + FName _ProfileName = NAME_None; + + static FString IniLocation; + + static FName BatteryProfile; + static FName ChargingProfile; + static FName DockedProfile; + + TMap SettingsMap; + + // Maps to store configuration settings for each profile + TMap BatterySettings; + TMap ChargingSettings; + TMap DockedSettings; + + static TMap DefaultSettingsMap; + + static UHarmonyLinkGraphics* Instance; +}; diff --git a/Source/HarmonyLink/Public/Structs/HLConfigValue.h b/Source/HarmonyLink/Public/Structs/HLConfigValue.h new file mode 100644 index 0000000..1a0cd2f --- /dev/null +++ b/Source/HarmonyLink/Public/Structs/HLConfigValue.h @@ -0,0 +1,85 @@ +// Copyright (C) 2024 Jordon Brooks + +#pragma once + +#include "CoreMinimal.h" +#include "HLConfigValue.generated.h" + +UENUM(BlueprintType) +enum class EConfigValueType : uint8 +{ + Int, + Float, + Bool, + String +}; + +USTRUCT(BlueprintType) +struct FHLConfigValue +{ + GENERATED_BODY() + +private: + UPROPERTY(EditAnywhere, Category="ConfigValue") + EConfigValueType Type; + + UPROPERTY(EditAnywhere, Category="ConfigValue") + int32 IntValue; + + UPROPERTY(EditAnywhere, Category="ConfigValue") + float FloatValue; + + UPROPERTY(EditAnywhere, Category="ConfigValue") + bool BoolValue; + + UPROPERTY(EditAnywhere, Category="ConfigValue") + FString StringValue; + +public: + FHLConfigValue() : Type(EConfigValueType::String), IntValue(0), FloatValue(0.0f), BoolValue(false), StringValue(TEXT("")) {} + + FHLConfigValue(int32 Value) : Type(EConfigValueType::Int), IntValue(Value), FloatValue(0.0f), BoolValue(false), StringValue(TEXT("")) {} + + FHLConfigValue(float Value) : Type(EConfigValueType::Float), IntValue(0), FloatValue(Value), BoolValue(false), StringValue(TEXT("")) {} + + FHLConfigValue(bool Value) : Type(EConfigValueType::Bool), IntValue(0), FloatValue(0.0f), BoolValue(Value), StringValue(TEXT("")) {} + + FHLConfigValue(const FString& Value) : Type(EConfigValueType::String), IntValue(0), FloatValue(0.0f), BoolValue(false), StringValue(Value) {} + + EConfigValueType GetType() const + { + return Type; + } + + template + T GetValue() const; +}; + +// Specializations of the templated getter +template<> +inline int32 FHLConfigValue::GetValue() const +{ + ensure(Type == EConfigValueType::Int); + return IntValue; +} + +template<> +inline float FHLConfigValue::GetValue() const +{ + ensure(Type == EConfigValueType::Float); + return FloatValue; +} + +template<> +inline bool FHLConfigValue::GetValue() const +{ + ensure(Type == EConfigValueType::Bool); + return BoolValue; +} + +template<> +inline FString FHLConfigValue::GetValue() const +{ + ensure(Type == EConfigValueType::String); + return StringValue; +}