diff --git a/UnleashedRecomp/config.cpp b/UnleashedRecomp/config.cpp index b366a2f..67234fd 100644 --- a/UnleashedRecomp/config.cpp +++ b/UnleashedRecomp/config.cpp @@ -2,7 +2,7 @@ void Config::Load() { try { - auto toml = toml::parse_file((GetUserPath() / TOML_FILE).string()); + auto toml = toml::parse_file(GetConfigPath().string()); TOML_BEGIN_SECTION("System") { @@ -61,10 +61,38 @@ void Config::Load() printf("Failed to parse configuration: %s\n", err.what()); } - ResolutionScale = std::clamp(ResolutionScale, 0.25f, 2.0f); + ResolutionScale = std::clamp(ResolutionScale.Value, 0.25f, 2.0f); } void Config::Save() { - // TODO + std::string result; + std::string section; + + for (auto def : Definitions) + { + auto isFirstSection = section.empty(); + auto isDefWithSection = section != def->GetSection(); + auto tomlDef = def->GetDefinition(isDefWithSection); + + section = def->GetSection(); + + // Don't output prefix space for first section. + if (!isFirstSection && isDefWithSection) + result += '\n'; + + result += tomlDef + '\n'; + } + + std::ofstream out(GetConfigPath()); + + if (out.is_open()) + { + out << result; + out.close(); + } + else + { + printf("Failed to write configuration.\n"); + } } diff --git a/UnleashedRecomp/config.h b/UnleashedRecomp/config.h index 6866862..8082fb9 100644 --- a/UnleashedRecomp/config.h +++ b/UnleashedRecomp/config.h @@ -1,116 +1,47 @@ #pragma once -#define USER_DIRECTORY "SWA" - -#define TOML_FILE "config.toml" - -#define TOML_BEGIN_SECTION(name) if (auto pSection = toml[name].as_table()) { const auto& section = *pSection; -#define TOML_END_SECTION() } - -#define TOML_READ_STRING(var) var = section[#var].value_or(var); -#define TOML_READ_BOOLEAN(var) var = section[#var].value_or(var); -#define TOML_READ_FLOAT(var) var = section[#var].value_or(var); -#define TOML_READ_INTEGER(var) var = section[#var].value_or(var); -#define TOML_READ_DOUBLE(var) var = section[#var].value_or(var); -#define TOML_READ_ENUM(type, var) var = (type)section[#var].value_or(var); - -#define CONFIG_DEFINE(type, name, defaultValue) \ - static const type name##_Default = defaultValue; \ - inline static type name = name##_Default; - -#define CONFIG_GET_DEFAULT(name) Config::name##_Default -#define CONFIG_SET_DEFAULT(name) Config::name = CONFIG_GET_DEFAULT(name) - -enum ELanguage -{ - ELanguage_English = 1, - ELanguage_Japanese, - ELanguage_German, - ELanguage_French, - ELanguage_Spanish, - ELanguage_Italian -}; - -enum EScoreBehaviour -{ - EScoreBehaviour_CheckpointReset, - EScoreBehaviour_CheckpointRetain -}; - -enum EVoiceLanguage -{ - EVoiceLanguage_English, - EVoiceLanguage_Japanese -}; - -enum EGraphicsAPI -{ - EGraphicsAPI_D3D12, - EGraphicsAPI_Vulkan -}; - -enum EGITextureFiltering -{ - EGITextureFiltering_Linear, - EGITextureFiltering_Bicubic -}; - -enum EMovieScaleMode -{ - EMovieScaleMode_Stretch, - EMovieScaleMode_Fit, - EMovieScaleMode_Fill -}; - -enum EUIScaleMode -{ - EUIScaleMode_Stretch, - EUIScaleMode_Edge, - EUIScaleMode_Centre -}; +#include "config_detail.h" class Config { public: - // System - CONFIG_DEFINE(ELanguage, Language, ELanguage_English); - CONFIG_DEFINE(bool, Hints, true); - CONFIG_DEFINE(EScoreBehaviour, ScoreBehaviour, EScoreBehaviour_CheckpointReset); - CONFIG_DEFINE(bool, UnleashOutOfControlDrain, true); - CONFIG_DEFINE(bool, WerehogHubTransformVideo, true); - CONFIG_DEFINE(bool, LogoSkip, false); + inline static std::vector> Definitions{}; - // Controls - CONFIG_DEFINE(bool, CameraXInvert, false); - CONFIG_DEFINE(bool, CameraYInvert, false); - CONFIG_DEFINE(bool, XButtonHoming, true); - CONFIG_DEFINE(bool, UnleashCancel, false); + CONFIG_DEFINE("System", ELanguage, Language, ELanguage_English); + CONFIG_DEFINE("System", bool, Hints, true); + CONFIG_DEFINE("System", EScoreBehaviour, ScoreBehaviour, EScoreBehaviour_CheckpointReset); + CONFIG_DEFINE("System", bool, UnleashOutOfControlDrain, true); + CONFIG_DEFINE("System", bool, WerehogHubTransformVideo, true); + CONFIG_DEFINE("System", bool, LogoSkip, false); - // Audio - CONFIG_DEFINE(float, MusicVolume, 1.0f); - CONFIG_DEFINE(float, SEVolume, 1.0f); - CONFIG_DEFINE(EVoiceLanguage, VoiceLanguage, EVoiceLanguage_English); - CONFIG_DEFINE(bool, Subtitles, true); - CONFIG_DEFINE(bool, WerehogBattleMusic, true); + CONFIG_DEFINE("Controls", bool, CameraXInvert, false); + CONFIG_DEFINE("Controls", bool, CameraYInvert, false); + CONFIG_DEFINE("Controls", bool, XButtonHoming, true); + CONFIG_DEFINE("Controls", bool, UnleashCancel, false); - // Video - CONFIG_DEFINE(EGraphicsAPI, GraphicsAPI, EGraphicsAPI_D3D12); - CONFIG_DEFINE(size_t, WindowWidth, 1280); - CONFIG_DEFINE(size_t, WindowHeight, 720); - CONFIG_DEFINE(float, ResolutionScale, 1.0f); - CONFIG_DEFINE(bool, Fullscreen, false); - CONFIG_DEFINE(bool, VSync, true); - CONFIG_DEFINE(size_t, BufferCount, 3); - CONFIG_DEFINE(size_t, FPS, 60); - CONFIG_DEFINE(float, Brightness, 0.5f); - CONFIG_DEFINE(size_t, MSAA, 4); - CONFIG_DEFINE(size_t, AnisotropicFiltering, 16); - CONFIG_DEFINE(int32_t, ShadowResolution, 4096); - CONFIG_DEFINE(EGITextureFiltering, GITextureFiltering, EGITextureFiltering_Bicubic); - CONFIG_DEFINE(bool, AlphaToCoverage, false); - CONFIG_DEFINE(bool, Xbox360ColorCorrection, false); - CONFIG_DEFINE(EMovieScaleMode, MovieScaleMode, EMovieScaleMode_Fit); - CONFIG_DEFINE(EUIScaleMode, UIScaleMode, EUIScaleMode_Centre); + CONFIG_DEFINE("Audio", float, MusicVolume, 1.0f); + CONFIG_DEFINE("Audio", float, SEVolume, 1.0f); + CONFIG_DEFINE("Audio", EVoiceLanguage, VoiceLanguage, EVoiceLanguage_English); + CONFIG_DEFINE("Audio", bool, Subtitles, true); + CONFIG_DEFINE("Audio", bool, WerehogBattleMusic, true); + + CONFIG_DEFINE("Video", EGraphicsAPI, GraphicsAPI, EGraphicsAPI_D3D12); + CONFIG_DEFINE("Video", size_t, WindowWidth, 1280); + CONFIG_DEFINE("Video", size_t, WindowHeight, 720); + CONFIG_DEFINE("Video", float, ResolutionScale, 1.0f); + CONFIG_DEFINE("Video", bool, Fullscreen, false); + CONFIG_DEFINE("Video", bool, VSync, true); + CONFIG_DEFINE("Video", size_t, BufferCount, 3); + CONFIG_DEFINE("Video", size_t, FPS, 60); + CONFIG_DEFINE("Video", float, Brightness, 0.5f); + CONFIG_DEFINE("Video", size_t, MSAA, 4); + CONFIG_DEFINE("Video", size_t, AnisotropicFiltering, 16); + CONFIG_DEFINE("Video", int32_t, ShadowResolution, 4096); + CONFIG_DEFINE("Video", EGITextureFiltering, GITextureFiltering, EGITextureFiltering_Bicubic); + CONFIG_DEFINE("Video", bool, AlphaToCoverage, false); + CONFIG_DEFINE("Video", bool, Xbox360ColorCorrection, false); + CONFIG_DEFINE("Video", EMovieScaleMode, MovieScaleMode, EMovieScaleMode_Fit); + CONFIG_DEFINE("Video", EUIScaleMode, UIScaleMode, EUIScaleMode_Centre); static std::filesystem::path GetUserPath() { @@ -129,6 +60,11 @@ public: return userPath; } + static std::filesystem::path GetConfigPath() + { + return GetUserPath() / TOML_FILE; + } + static void Load(); static void Save(); }; diff --git a/UnleashedRecomp/config_detail.h b/UnleashedRecomp/config_detail.h new file mode 100644 index 0000000..f68457d --- /dev/null +++ b/UnleashedRecomp/config_detail.h @@ -0,0 +1,181 @@ +#pragma once + +#define USER_DIRECTORY "SWA" + +#define TOML_FILE "config.toml" + +#define TOML_BEGIN_SECTION(name) if (auto pSection = toml[name].as_table()) { const auto& section = *pSection; +#define TOML_END_SECTION() } + +#define TOML_READ_STRING(var) var.Value = section[#var].value_or(var.DefaultValue); +#define TOML_READ_BOOLEAN(var) var.Value = section[#var].value_or(var.DefaultValue); +#define TOML_READ_FLOAT(var) var.Value = section[#var].value_or(var.DefaultValue); +#define TOML_READ_INTEGER(var) var.Value = section[#var].value_or(var.DefaultValue); +#define TOML_READ_DOUBLE(var) var.Value = section[#var].value_or(var.DefaultValue); +#define TOML_READ_ENUM(type, var) var.Value = (type)section[#var].value_or(var.DefaultValue); + +#define CONFIG_DEFINE(section, type, name, defaultValue) inline static ConfigDef name{section, #name, defaultValue}; +#define CONFIG_VALUE(name) Config::name.Value + +#define CONFIG_GET_DEFAULT(name) Config::name.DefaultValue +#define CONFIG_SET_DEFAULT(name) Config::name.MakeDefault(); + +class ConfigDefBase +{ +public: + virtual ~ConfigDefBase() = default; + virtual void MakeDefault() = 0; + virtual std::string GetSection() const = 0; + virtual std::string GetName() const = 0; + virtual std::string GetDefinition(bool withSection = false) const = 0; + virtual std::string ToString() const = 0; +}; + +template +class ConfigDef : public ConfigDefBase +{ +public: + std::string Section; + std::string Name; + T DefaultValue{}; + T Value{ DefaultValue }; + + ConfigDef(std::string section, std::string name, T defaultValue) : Section(section), Name(name), DefaultValue(defaultValue) + { + Config::Definitions.emplace_back(this); + } + + void MakeDefault() override + { + Value = DefaultValue; + } + + std::string GetSection() const override + { + return Section; + } + + std::string GetName() const override + { + return Name; + } + + std::string GetDefinition(bool withSection = false) const override + { + std::string result; + + if (withSection) + result += "[" + Section + "]\n"; + + result += Name + " = " + ToString(); + + return result; + } + + std::string ToString() const override + { + if constexpr (std::is_same::value) + { + return Value; + } + else if constexpr (std::is_same::value) + { + return Value ? "true" : "false"; + } + + std::ostringstream oss; + oss << Value; + return oss.str(); + } + + ConfigDef& operator=(const ConfigDef& other) + { + if (this != &other) + Value = other.Value; + + return *this; + } + + void operator=(const T& other) + { + Value = other; + } + + bool operator==(const T& other) const + { + return Value == other; + } + + bool operator!=(const T& other) const + { + return Value != other; + } + + bool operator<(const T& other) const + { + return Value < other; + } + + bool operator>(const T& other) const + { + return Value > other; + } + + bool operator<=(const T& other) const + { + return Value <= other; + } + + bool operator>=(const T& other) const + { + return Value >= other; + } +}; + +enum ELanguage +{ + ELanguage_English = 1, + ELanguage_Japanese, + ELanguage_German, + ELanguage_French, + ELanguage_Spanish, + ELanguage_Italian +}; + +enum EScoreBehaviour +{ + EScoreBehaviour_CheckpointReset, + EScoreBehaviour_CheckpointRetain +}; + +enum EVoiceLanguage +{ + EVoiceLanguage_English, + EVoiceLanguage_Japanese +}; + +enum EGraphicsAPI +{ + EGraphicsAPI_D3D12, + EGraphicsAPI_Vulkan +}; + +enum EGITextureFiltering +{ + EGITextureFiltering_Linear, + EGITextureFiltering_Bicubic +}; + +enum EMovieScaleMode +{ + EMovieScaleMode_Stretch, + EMovieScaleMode_Fit, + EMovieScaleMode_Fill +}; + +enum EUIScaleMode +{ + EUIScaleMode_Stretch, + EUIScaleMode_Edge, + EUIScaleMode_Centre +}; diff --git a/UnleashedRecomp/game.cpp b/UnleashedRecomp/game.cpp index 52ca173..b68a311 100644 --- a/UnleashedRecomp/game.cpp +++ b/UnleashedRecomp/game.cpp @@ -119,7 +119,7 @@ PPC_FUNC(sub_824DCF38) { /* Force the Werehog transition ID to use a different transition. */ - if (!Config::WerehogHubTransformVideo) + if (!Config::WerehogHubTransformVideo.Value) { /* 0 - Tails Electric NOW LOADING @@ -160,12 +160,12 @@ PPC_FUNC(sub_823AF7A8) m_lastDarkGaiaEnergy = pEvilSonicContext->m_DarkGaiaEnergy; // Don't drain energy if out of control. - if (!Config::UnleashOutOfControlDrain && pEvilSonicContext->m_OutOfControlCount && ctx.f1.f64 < 0.0) + if (!Config::UnleashOutOfControlDrain.Value && pEvilSonicContext->m_OutOfControlCount && ctx.f1.f64 < 0.0) return; __imp__sub_823AF7A8(ctx, base); - if (!Config::UnleashCancel) + if (!Config::UnleashCancel.Value) return; auto pInputState = SWA::CInputState::GetInstance(); @@ -198,12 +198,12 @@ void PostUnleashMidAsmHook(PPCRegister& r30) bool DisableHintsMidAsmHook() { - return !Config::Hints; + return !Config::Hints.Value; } void SetXButtonHomingMidAsmHook(PPCRegister& r30) { - r30.u32 = Config::XButtonHoming; + r30.u32 = Config::XButtonHoming.Value; } /* Hook function that gets the game region @@ -240,7 +240,7 @@ void GetStageIDMidAsmHook(PPCRegister& r5) PPC_FUNC_IMPL(__imp__sub_82547DF0); PPC_FUNC(sub_82547DF0) { - if (Config::LogoSkip) + if (Config::LogoSkip.Value) { ctx.r4.u64 = 0; ctx.r5.u64 = 0; diff --git a/UnleashedRecomp/gpu/video.cpp b/UnleashedRecomp/gpu/video.cpp index a72b499..7c70f78 100644 --- a/UnleashedRecomp/gpu/video.cpp +++ b/UnleashedRecomp/gpu/video.cpp @@ -1171,7 +1171,7 @@ static GuestSurface* CreateSurface(uint32_t width, uint32_t height, uint32_t for desc.depth = 1; desc.mipLevels = 1; desc.arraySize = 1; - desc.multisampling.sampleCount = multiSample != 0 && Config::MSAA > 1 ? Config::MSAA : RenderSampleCount::COUNT_1; + desc.multisampling.sampleCount = multiSample != 0 && Config::MSAA > 1 ? Config::MSAA.Value : RenderSampleCount::COUNT_1; desc.format = ConvertFormat(format); desc.flags = desc.format == RenderFormat::D32_FLOAT ? RenderTextureFlag::DEPTH_TARGET : RenderTextureFlag::RENDER_TARGET; @@ -2614,7 +2614,7 @@ void IndexBufferLengthMidAsmHook(PPCRegister& r3) void SetShadowResolutionMidAsmHook(PPCRegister& r11) { if (Config::ShadowResolution > 0) - r11.u64 = Config::ShadowResolution; + r11.u64 = Config::ShadowResolution.Value; } void Primitive2DHalfPixelOffsetMidAsmHook(PPCRegister& f13) @@ -2624,8 +2624,8 @@ void Primitive2DHalfPixelOffsetMidAsmHook(PPCRegister& f13) static void SetResolution(be* device) { - uint32_t width = uint32_t(g_swapChain->getWidth() * Config::ResolutionScale); - uint32_t height = uint32_t(g_swapChain->getHeight() * Config::ResolutionScale); + uint32_t width = uint32_t(g_swapChain->getWidth() * Config::ResolutionScale.Value); + uint32_t height = uint32_t(g_swapChain->getHeight() * Config::ResolutionScale.Value); device[46] = width == 0 ? 880 : width; device[47] = height == 0 ? 720 : height; } diff --git a/UnleashedRecomp/kernel/imports.cpp b/UnleashedRecomp/kernel/imports.cpp index be0a3fe..ab110ac 100644 --- a/UnleashedRecomp/kernel/imports.cpp +++ b/UnleashedRecomp/kernel/imports.cpp @@ -191,7 +191,7 @@ void XamShowMessageBoxUIEx() uint32_t XGetLanguage() { // printf("!!! STUB !!! XGetLanguage\n"); - return Config::Language; + return Config::Language.Value; } uint32_t XGetAVPack() @@ -336,7 +336,7 @@ uint32_t ExGetXConfigSetting(uint16_t Category, uint16_t Setting, void* Buffer, // XCONFIG_USER_LANGUAGE case 0x0009: - data[0] = std::byteswap((uint32_t)Config::Language); + data[0] = std::byteswap((uint32_t)Config::Language.Value); break; // XCONFIG_USER_VIDEO_FLAGS diff --git a/UnleashedRecomp/stdafx.h b/UnleashedRecomp/stdafx.h index d8b63de..3e6445a 100644 --- a/UnleashedRecomp/stdafx.h +++ b/UnleashedRecomp/stdafx.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include diff --git a/UnleashedRecomp/ui/window.cpp b/UnleashedRecomp/ui/window.cpp index 7fb0ac9..f404a92 100644 --- a/UnleashedRecomp/ui/window.cpp +++ b/UnleashedRecomp/ui/window.cpp @@ -92,8 +92,8 @@ void Window::Init() s_window = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, - Config::WindowWidth, - Config::WindowHeight, + Config::WindowWidth.Value, + Config::WindowHeight.Value, SDL_WINDOW_RESIZABLE); if (auto icon = GetIconSurface()) @@ -103,7 +103,7 @@ void Window::Init() } SetWindowDimensions(); - SetFullscreen(Config::Fullscreen); + SetFullscreen(Config::Fullscreen.Value); SDL_SysWMinfo info; SDL_VERSION(&info.version); diff --git a/UnleashedRecomp/ui/window.h b/UnleashedRecomp/ui/window.h index 07cbaaf..b1077a2 100644 --- a/UnleashedRecomp/ui/window.h +++ b/UnleashedRecomp/ui/window.h @@ -29,8 +29,8 @@ public: static bool IsDisplayResolution(int w, int h, bool isExact = true) { - auto width = w <= 0 ? Config::WindowWidth : w; - auto height = h <= 0 ? Config::WindowHeight : h; + auto width = w <= 0 ? Config::WindowWidth.Value : w; + auto height = h <= 0 ? Config::WindowHeight.Value : h; SDL_Rect displayRect; if (SDL_GetDisplayBounds(SDL_GetWindowDisplayIndex(s_window), &displayRect) == ERROR_SUCCESS) @@ -83,8 +83,8 @@ public: static void SetWindowDimensions(int w = -1, int h = -1) { - auto width = w <= 0 ? Config::WindowWidth : w; - auto height = h <= 0 ? Config::WindowHeight : h; + auto width = w <= 0 ? Config::WindowWidth.Value : w; + auto height = h <= 0 ? Config::WindowHeight.Value : h; auto isPendingMaximise = false; if (IsDisplayResolution(width, height))