mirror of
https://github.com/optiscaler/OptiScaler.git
synced 2026-11-04 21:13:33 +00:00
Merge pull request #1238 from LorenzoKy/fps-overlay-appearance
FPS overlay: appearance and layout customization
This commit is contained in:
4 files changed
+1858
-272
No files matched your search
+86
-7
@@ -892,18 +892,26 @@ DisableSplash=auto
|
||||
; true or false - Default (auto) is false
|
||||
ShowFps=auto
|
||||
|
||||
; In-game FPS Overlay scale
|
||||
; 0.5 to 2.0 - Default (auto) is using the same scale as menu
|
||||
; In-game FPS Overlay scale, independent from the menu scale when set
|
||||
; 0.5 to 3.0 - Default (auto) is using the same scale as menu
|
||||
FpsScale=auto
|
||||
|
||||
; FPS overlay position
|
||||
; 0 = Top Left | 1 = Top Right | 2 = Bottom Left | 3 Bottom Right
|
||||
; 0 to 3 - Default (auto) is 0
|
||||
; 4 = Top Center | 5 = Bottom Center | 6 = Custom (FpsOverlayPosX/Y)
|
||||
; 0 to 6 - Default (auto) is 0
|
||||
FpsOverlayPos=auto
|
||||
|
||||
; FPS overlay position when FpsOverlayPos is 6, 0.0 is left/top and 1.0 right/bottom of the screen
|
||||
; 0.0 to 1.0 - Default (auto) is 0.0
|
||||
FpsOverlayPosX=auto
|
||||
FpsOverlayPosY=auto
|
||||
|
||||
; FPS overlay type
|
||||
; 0 = Just FPS | 1 = Simple | 2 = Detailed | 3 = Detailed + Graph | 4 Full | 5 Full + Graph | 6 Reflex timings
|
||||
; 0 to 6 - Default (auto) is 0
|
||||
; Each type before Custom is a preset of the rows, Custom shows FpsOverlayRows
|
||||
; 0 = Just FPS | 1 = Simple | 2 = Detailed | 3 = Detailed + Graph | 4 Full | 5 Full + Graph | 6 Low latency timings
|
||||
; 7 = Custom
|
||||
; 0 to 7 - Default (auto) is 5
|
||||
FpsOverlayType=auto
|
||||
|
||||
; Shortcut key for FPS overlay
|
||||
@@ -922,21 +930,92 @@ FpsCycleShortcutKey=auto
|
||||
; true or false - Default (auto) is false
|
||||
FpsOverlayHorizontal=auto
|
||||
|
||||
; Shows the current upscale ratio (and render -> output resolution) in the FPS overlay
|
||||
; Updated in real time, so dynamic resolution changes are visible
|
||||
; Shows the current upscale ratio (and render -> output resolution) in the FPS overlay types from Simple to
|
||||
; Low latency timings, Custom has the upscale row for it. Updated in real time, so dynamic resolution changes are visible
|
||||
; true or false - Default (auto) is false
|
||||
FpsOverlayShowRatio=auto
|
||||
|
||||
; Shorter labels in the FPS overlay (UPS, FT, UT...), for a narrower overlay
|
||||
; true or false - Default (auto) is false
|
||||
FpsOverlayCompactLabels=auto
|
||||
|
||||
; Rows of the Custom FPS overlay type in their order, comma separated, a row starting with - is hidden:
|
||||
; fps, upscale, frametime, frametimegraph, upscaler, upscalergraph, lowlatency
|
||||
; Rows left out are hidden. Default (auto) is the rows of Full + Graph
|
||||
FpsOverlayRows=auto
|
||||
|
||||
; What the FPS row of the Custom FPS overlay type shows with the FPS
|
||||
; 0 = Just FPS | 1 = Frame time | 2 = Average FPS - Default (auto) is 2
|
||||
; The API, frame generation and upscaler are shown with 1 and 2
|
||||
FpsOverlayFpsRow=auto
|
||||
|
||||
; Space between the FPS overlay rows in pixels, before FpsScale
|
||||
; 0.0 to 32.0 - Default (auto) is twice the item spacing
|
||||
FpsOverlayRowSpacing=auto
|
||||
|
||||
; How often the FPS overlay's FPS, frame time and upscaler time change in milliseconds, averaged in between so they
|
||||
; can be read. 0 updates them every frame
|
||||
; 0.0 to 1000.0 - Default (auto) is 0.0
|
||||
FpsOverlayUpdateInterval=auto
|
||||
|
||||
; FPS Overlay background alpha
|
||||
; 0.0 to 1.0 - Default (auto) is 0.4
|
||||
FpsOverlayAlpha=auto
|
||||
|
||||
; FPS Overlay graph height multiplier
|
||||
; 0.5 to 3.0 - Default (auto) is 1.0
|
||||
FpsOverlayGraphHeight=auto
|
||||
|
||||
; FPS Overlay font, a file name in C:\Windows\Fonts (e.g. segoeui.ttf) or a full path
|
||||
; Default (auto) is the menu font
|
||||
FpsOverlayFont=auto
|
||||
|
||||
; FPS Overlay font size in pixels, before FpsScale
|
||||
; 8.0 to 48.0 - Default (auto) is the menu font size
|
||||
FpsOverlayFontSize=auto
|
||||
|
||||
; FPS Overlay extra horizontal spacing between characters in pixels
|
||||
; -2.0 to 8.0 - Default (auto) is 0.0
|
||||
FpsOverlayLetterSpacing=auto
|
||||
|
||||
; FPS Overlay size of the labels (names like FPS:) and values (numbers), multiplies the font size
|
||||
; 0.5 to 2.0 - Default (auto) is 1.0
|
||||
FpsOverlayLabelScale=auto
|
||||
FpsOverlayValueScale=auto
|
||||
|
||||
; FPS Overlay colors as 0xRRGGBB, used when OverlaysUseTheme is false
|
||||
; Default (auto) is 0xd9d9d9 for labels, 0xffffff for values, 0x00ff00 for graph and 0x000000 for background
|
||||
FpsOverlayLabelColor=auto
|
||||
FpsOverlayValueColor=auto
|
||||
FpsOverlayGraphColor=auto
|
||||
FpsOverlayBGColor=auto
|
||||
|
||||
; FPS Overlay text shadow
|
||||
; 0 = Off | 1 = Shadow | 2 = Outline - Default (auto) is 1
|
||||
FpsOverlayShadow=auto
|
||||
|
||||
; FPS Overlay text shadow thickness and darkness, outline thickness
|
||||
; 0.0 to 1.0 - Default (auto) is 0.5
|
||||
FpsOverlayShadowStrength=auto
|
||||
|
||||
; FPS Overlay text shadow or outline color as 0xRRGGBB
|
||||
; Default (auto) is 0x000000
|
||||
FpsOverlayShadowColor=auto
|
||||
|
||||
; FPS Overlay corner radius in pixels, 0 is square
|
||||
; 0.0 to 16.0 - Default (auto) is 4.0
|
||||
FpsOverlayRounding=auto
|
||||
|
||||
; Shortcut key for FG enabled/disabled
|
||||
; https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
|
||||
; Integer value - Default (auto) is 0x23 -> VK_END/End key
|
||||
; -1 -> No shortcut key
|
||||
FGShortcutKey=auto
|
||||
|
||||
; Menu corner radius in pixels, 0 is square
|
||||
; 0.0 to 16.0 - Default (auto) is 2.0
|
||||
Rounding=auto
|
||||
|
||||
; Overlays uses the theme colors
|
||||
; true or false - Default (auto) is false
|
||||
OverlaysUseTheme=auto
|
||||
|
||||
+391
-27
@@ -468,27 +468,13 @@ bool Config::Reload(std::filesystem::path iniPath)
|
||||
UseHQFont.set_from_config(readBool("Menu", "UseHQFont"));
|
||||
DisableSplash.set_from_config(readBool("Menu", "DisableSplash"));
|
||||
|
||||
if (auto setting = readUInt("Menu", "FpsOverlayPos"); setting.has_value())
|
||||
{
|
||||
FpsOverlayPosition.set_from_config(
|
||||
(FpsOverlayPos) std::clamp(setting.value(), 0U, FpsOverlayPos_COUNT - 1));
|
||||
}
|
||||
|
||||
if (auto setting = readUInt("Menu", "FpsOverlayType"); setting.has_value())
|
||||
{
|
||||
FpsOverlayType.set_from_config((FpsOverlay) std::clamp(setting.value(), 0U, FpsOverlay_COUNT - 1));
|
||||
}
|
||||
ReadOverlaySettings(false);
|
||||
|
||||
FpsShortcutKey.set_from_config(readInt("Menu", "FpsShortcutKey"));
|
||||
FpsCycleShortcutKey.set_from_config(readInt("Menu", "FpsCycleShortcutKey"));
|
||||
FpsOverlayHorizontal.set_from_config(readBool("Menu", "FpsOverlayHorizontal"));
|
||||
FpsOverlayShowRatio.set_from_config(readBool("Menu", "FpsOverlayShowRatio"));
|
||||
|
||||
if (auto setting = readFloat("Menu", "FpsOverlayAlpha"); setting.has_value())
|
||||
FpsOverlayAlpha.set_from_config(std::clamp(setting.value(), 0.0f, 1.0f));
|
||||
|
||||
if (auto setting = readFloat("Menu", "FpsScale"); setting.has_value())
|
||||
FpsScale.set_from_config(std::clamp(setting.value(), 0.5f, 2.0f));
|
||||
if (auto setting = readFloat("Menu", "Rounding"); setting.has_value())
|
||||
MenuRounding.set_from_config(std::clamp(setting.value(), 0.0f, 16.0f));
|
||||
|
||||
FontSize.set_from_config(readFloat("Menu", "FontSize"));
|
||||
TTFFontPath.set_from_config(readWString("Menu", "TTFFontPath"));
|
||||
@@ -496,7 +482,6 @@ bool Config::Reload(std::filesystem::path iniPath)
|
||||
FGShortcutKey.set_from_config(readInt("Menu", "FGShortcutKey"));
|
||||
|
||||
LightTheme.set_from_config(readBool("Menu", "LightTheme"));
|
||||
OverlaysUseTheme.set_from_config(readBool("Menu", "OverlaysUseTheme"));
|
||||
MenuAccentColorR.set_from_config(readFloat("Menu", "AccentColorR"));
|
||||
MenuAccentColorG.set_from_config(readFloat("Menu", "AccentColorG"));
|
||||
MenuAccentColorB.set_from_config(readFloat("Menu", "AccentColorB"));
|
||||
@@ -893,6 +878,391 @@ std::string GetFloatValue(std::optional<float> value)
|
||||
return std::to_string(value.value());
|
||||
}
|
||||
|
||||
// How a key's value is packed in a settings code
|
||||
enum class OverlayValueType
|
||||
{
|
||||
Bool,
|
||||
Index,
|
||||
Float,
|
||||
Color,
|
||||
Text,
|
||||
};
|
||||
|
||||
struct OverlayIniKey
|
||||
{
|
||||
const char* name;
|
||||
OverlayValueType type;
|
||||
};
|
||||
|
||||
// FPS overlay settings, in the Menu section. Codes refer to a key by its place here, so new keys go at the end.
|
||||
static const OverlayIniKey overlayIniKeys[] = {
|
||||
{ "OverlaysUseTheme", OverlayValueType::Bool }, { "FpsOverlayPos", OverlayValueType::Index },
|
||||
{ "FpsOverlayType", OverlayValueType::Index }, { "FpsOverlayHorizontal", OverlayValueType::Bool },
|
||||
{ "FpsOverlayShowRatio", OverlayValueType::Bool }, { "FpsOverlayCompactLabels", OverlayValueType::Bool },
|
||||
{ "FpsOverlayRows", OverlayValueType::Text }, { "FpsOverlayFpsRow", OverlayValueType::Index },
|
||||
{ "FpsOverlayRowSpacing", OverlayValueType::Float }, { "FpsOverlayUpdateInterval", OverlayValueType::Float },
|
||||
{ "FpsOverlayAlpha", OverlayValueType::Float }, { "FpsScale", OverlayValueType::Float },
|
||||
{ "FpsOverlayGraphHeight", OverlayValueType::Float }, { "FpsOverlayPosX", OverlayValueType::Float },
|
||||
{ "FpsOverlayPosY", OverlayValueType::Float }, { "FpsOverlayFont", OverlayValueType::Text },
|
||||
{ "FpsOverlayFontSize", OverlayValueType::Float }, { "FpsOverlayLetterSpacing", OverlayValueType::Float },
|
||||
{ "FpsOverlayLabelScale", OverlayValueType::Float }, { "FpsOverlayValueScale", OverlayValueType::Float },
|
||||
{ "FpsOverlayLabelColor", OverlayValueType::Color }, { "FpsOverlayValueColor", OverlayValueType::Color },
|
||||
{ "FpsOverlayGraphColor", OverlayValueType::Color }, { "FpsOverlayBGColor", OverlayValueType::Color },
|
||||
{ "FpsOverlayShadow", OverlayValueType::Index }, { "FpsOverlayShadowStrength", OverlayValueType::Float },
|
||||
{ "FpsOverlayShadowColor", OverlayValueType::Color }, { "FpsOverlayRounding", OverlayValueType::Float },
|
||||
};
|
||||
|
||||
void Config::ReadOverlaySettings(bool assign)
|
||||
{
|
||||
// Loading the ini fills only what isn't set yet, applying a code replaces the values
|
||||
auto apply = [assign](auto& setting, const auto& value)
|
||||
{
|
||||
if (assign)
|
||||
setting = value;
|
||||
else
|
||||
setting.set_from_config(value);
|
||||
};
|
||||
|
||||
auto readClamped = [&](const char* key, float low, float high)
|
||||
{ return readFloat("Menu", key).transform([=](float value) { return std::clamp(value, low, high); }); };
|
||||
|
||||
auto readColor = [&](const char* key)
|
||||
{ return readUInt("Menu", key).transform([](uint32_t value) { return value & 0xFFFFFF; }); };
|
||||
|
||||
auto readIndex = [&](const char* key, uint32_t count)
|
||||
{ return readUInt("Menu", key).transform([=](uint32_t value) { return std::min(value, count - 1); }); };
|
||||
|
||||
apply(OverlaysUseTheme, readBool("Menu", "OverlaysUseTheme"));
|
||||
apply(FpsOverlayPosition, readIndex("FpsOverlayPos", FpsOverlayPos_COUNT)
|
||||
.transform([](uint32_t value) { return (FpsOverlayPos) value; }));
|
||||
apply(FpsOverlayType,
|
||||
readIndex("FpsOverlayType", FpsOverlay_COUNT).transform([](uint32_t value) { return (FpsOverlay) value; }));
|
||||
apply(FpsOverlayHorizontal, readBool("Menu", "FpsOverlayHorizontal"));
|
||||
apply(FpsOverlayShowRatio, readBool("Menu", "FpsOverlayShowRatio"));
|
||||
apply(FpsOverlayCompactLabels, readBool("Menu", "FpsOverlayCompactLabels"));
|
||||
apply(FpsOverlayRows, readString("Menu", "FpsOverlayRows", true));
|
||||
apply(FpsOverlayFpsRowType, readIndex("FpsOverlayFpsRow", FpsOverlayFpsRow_COUNT)
|
||||
.transform([](uint32_t value) { return (FpsOverlayFpsRow) value; }));
|
||||
apply(FpsOverlayRowSpacing, readClamped("FpsOverlayRowSpacing", 0.0f, 32.0f));
|
||||
apply(FpsOverlayUpdateInterval, readClamped("FpsOverlayUpdateInterval", 0.0f, 1000.0f));
|
||||
apply(FpsOverlayAlpha, readClamped("FpsOverlayAlpha", 0.0f, 1.0f));
|
||||
apply(FpsScale, readClamped("FpsScale", 0.5f, 3.0f));
|
||||
apply(FpsOverlayGraphHeight, readClamped("FpsOverlayGraphHeight", 0.5f, 3.0f));
|
||||
apply(FpsOverlayPosX, readClamped("FpsOverlayPosX", 0.0f, 1.0f));
|
||||
apply(FpsOverlayPosY, readClamped("FpsOverlayPosY", 0.0f, 1.0f));
|
||||
apply(FpsOverlayFont, readString("Menu", "FpsOverlayFont"));
|
||||
apply(FpsOverlayFontSize, readClamped("FpsOverlayFontSize", 8.0f, 48.0f));
|
||||
apply(FpsOverlayLetterSpacing, readClamped("FpsOverlayLetterSpacing", -2.0f, 8.0f));
|
||||
apply(FpsOverlayLabelScale, readClamped("FpsOverlayLabelScale", 0.5f, 2.0f));
|
||||
apply(FpsOverlayValueScale, readClamped("FpsOverlayValueScale", 0.5f, 2.0f));
|
||||
apply(FpsOverlayLabelColor, readColor("FpsOverlayLabelColor"));
|
||||
apply(FpsOverlayValueColor, readColor("FpsOverlayValueColor"));
|
||||
apply(FpsOverlayGraphColor, readColor("FpsOverlayGraphColor"));
|
||||
apply(FpsOverlayBGColor, readColor("FpsOverlayBGColor"));
|
||||
apply(FpsOverlayShadowType, readIndex("FpsOverlayShadow", FpsOverlayShadow_COUNT)
|
||||
.transform([](uint32_t value) { return (FpsOverlayShadow) value; }));
|
||||
apply(FpsOverlayShadowStrength, readClamped("FpsOverlayShadowStrength", 0.0f, 1.0f));
|
||||
apply(FpsOverlayShadowColor, readColor("FpsOverlayShadowColor"));
|
||||
apply(FpsOverlayRounding, readClamped("FpsOverlayRounding", 0.0f, 16.0f));
|
||||
}
|
||||
|
||||
void Config::WriteOverlaySettings()
|
||||
{
|
||||
ini.SetValue("Menu", "OverlaysUseTheme", GetBoolValue(OverlaysUseTheme.value_for_config()).c_str());
|
||||
ini.SetValue("Menu", "FpsOverlayPos", GetIntValue(FpsOverlayPosition.value_for_config()).c_str());
|
||||
ini.SetValue("Menu", "FpsOverlayType", GetIntValue(FpsOverlayType.value_for_config()).c_str());
|
||||
ini.SetValue("Menu", "FpsOverlayHorizontal", GetBoolValue(FpsOverlayHorizontal.value_for_config()).c_str());
|
||||
ini.SetValue("Menu", "FpsOverlayShowRatio", GetBoolValue(FpsOverlayShowRatio.value_for_config()).c_str());
|
||||
ini.SetValue("Menu", "FpsOverlayCompactLabels", GetBoolValue(FpsOverlayCompactLabels.value_for_config()).c_str());
|
||||
ini.SetValue("Menu", "FpsOverlayRows", FpsOverlayRows.value_for_config_or("auto").c_str());
|
||||
ini.SetValue("Menu", "FpsOverlayFpsRow", GetIntValue(FpsOverlayFpsRowType.value_for_config()).c_str());
|
||||
ini.SetValue("Menu", "FpsOverlayRowSpacing", GetFloatValue(FpsOverlayRowSpacing.value_for_config()).c_str());
|
||||
ini.SetValue("Menu", "FpsOverlayUpdateInterval",
|
||||
GetFloatValue(FpsOverlayUpdateInterval.value_for_config()).c_str());
|
||||
ini.SetValue("Menu", "FpsOverlayAlpha", GetFloatValue(FpsOverlayAlpha.value_for_config()).c_str());
|
||||
ini.SetValue("Menu", "FpsScale", GetFloatValue(FpsScale.value_for_config()).c_str());
|
||||
ini.SetValue("Menu", "FpsOverlayGraphHeight", GetFloatValue(FpsOverlayGraphHeight.value_for_config()).c_str());
|
||||
ini.SetValue("Menu", "FpsOverlayPosX", GetFloatValue(FpsOverlayPosX.value_for_config()).c_str());
|
||||
ini.SetValue("Menu", "FpsOverlayPosY", GetFloatValue(FpsOverlayPosY.value_for_config()).c_str());
|
||||
ini.SetValue("Menu", "FpsOverlayFont", FpsOverlayFont.value_for_config_or("auto").c_str());
|
||||
ini.SetValue("Menu", "FpsOverlayFontSize", GetFloatValue(FpsOverlayFontSize.value_for_config()).c_str());
|
||||
ini.SetValue("Menu", "FpsOverlayLetterSpacing", GetFloatValue(FpsOverlayLetterSpacing.value_for_config()).c_str());
|
||||
ini.SetValue("Menu", "FpsOverlayLabelScale", GetFloatValue(FpsOverlayLabelScale.value_for_config()).c_str());
|
||||
ini.SetValue("Menu", "FpsOverlayValueScale", GetFloatValue(FpsOverlayValueScale.value_for_config()).c_str());
|
||||
ini.SetValue("Menu", "FpsOverlayLabelColor", GetIntValue(FpsOverlayLabelColor.value_for_config(), true).c_str());
|
||||
ini.SetValue("Menu", "FpsOverlayValueColor", GetIntValue(FpsOverlayValueColor.value_for_config(), true).c_str());
|
||||
ini.SetValue("Menu", "FpsOverlayGraphColor", GetIntValue(FpsOverlayGraphColor.value_for_config(), true).c_str());
|
||||
ini.SetValue("Menu", "FpsOverlayBGColor", GetIntValue(FpsOverlayBGColor.value_for_config(), true).c_str());
|
||||
ini.SetValue("Menu", "FpsOverlayShadow", GetIntValue(FpsOverlayShadowType.value_for_config()).c_str());
|
||||
ini.SetValue("Menu", "FpsOverlayShadowStrength",
|
||||
GetFloatValue(FpsOverlayShadowStrength.value_for_config()).c_str());
|
||||
ini.SetValue("Menu", "FpsOverlayShadowColor", GetIntValue(FpsOverlayShadowColor.value_for_config(), true).c_str());
|
||||
ini.SetValue("Menu", "FpsOverlayRounding", GetFloatValue(FpsOverlayRounding.value_for_config()).c_str());
|
||||
}
|
||||
|
||||
// The code: a versioned prefix and, as base64, the keys that differ from their defaults, each as its place in
|
||||
// overlayIniKeys and its value packed
|
||||
static constexpr std::string_view overlayCodePrefix = "OptiOverlay1:";
|
||||
static constexpr std::string_view overlayKeyPrefix = "FpsOverlay";
|
||||
|
||||
static std::string_view OverlayCodeKey(std::string_view key)
|
||||
{
|
||||
if (key.starts_with(overlayKeyPrefix))
|
||||
key.remove_prefix(overlayKeyPrefix.size());
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
static constexpr std::string_view base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
static std::string Base64Encode(std::string_view data)
|
||||
{
|
||||
std::string out;
|
||||
uint32_t bits = 0;
|
||||
int count = 0;
|
||||
|
||||
for (unsigned char c : data)
|
||||
{
|
||||
bits = (bits << 8) | c;
|
||||
count += 8;
|
||||
|
||||
for (; count >= 6; count -= 6)
|
||||
out += base64Chars[(bits >> (count - 6)) & 0x3F];
|
||||
}
|
||||
|
||||
// Without the padding, the decoder doesn't need it
|
||||
if (count > 0)
|
||||
out += base64Chars[(bits << (6 - count)) & 0x3F];
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
// Skips whitespace, so a code a chat wrapped over lines still decodes, and the padding
|
||||
static std::optional<std::string> Base64Decode(std::string_view data)
|
||||
{
|
||||
std::string out;
|
||||
uint32_t bits = 0;
|
||||
int count = 0;
|
||||
|
||||
for (char c : data)
|
||||
{
|
||||
if (c == '=' || std::isspace((unsigned char) c))
|
||||
continue;
|
||||
|
||||
const auto value = base64Chars.find(c);
|
||||
if (value == std::string_view::npos)
|
||||
return std::nullopt;
|
||||
|
||||
bits = (bits << 6) | static_cast<uint32_t>(value);
|
||||
count += 6;
|
||||
|
||||
if (count >= 8)
|
||||
{
|
||||
count -= 8;
|
||||
out += static_cast<char>((bits >> count) & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
// A value as the ini has it, packed by its type: a byte for a bool or an index, three for a color, four for a float,
|
||||
// a text as its length and characters
|
||||
static void PackOverlayValue(std::string& data, OverlayValueType type, const char* value)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case OverlayValueType::Bool:
|
||||
data += static_cast<char>(lstrcmpiA(value, "true") == 0);
|
||||
break;
|
||||
|
||||
case OverlayValueType::Index:
|
||||
data += static_cast<char>(std::strtoul(value, nullptr, 10));
|
||||
break;
|
||||
|
||||
case OverlayValueType::Color:
|
||||
{
|
||||
const auto rgb = std::strtoul(value, nullptr, 16);
|
||||
data += static_cast<char>(rgb >> 16);
|
||||
data += static_cast<char>(rgb >> 8);
|
||||
data += static_cast<char>(rgb);
|
||||
break;
|
||||
}
|
||||
|
||||
case OverlayValueType::Float:
|
||||
{
|
||||
const float number = std::strtof(value, nullptr);
|
||||
data.append(reinterpret_cast<const char*>(&number), sizeof(number));
|
||||
break;
|
||||
}
|
||||
|
||||
case OverlayValueType::Text:
|
||||
{
|
||||
const size_t length = std::min<size_t>(std::strlen(value), 255);
|
||||
data += static_cast<char>(length);
|
||||
data.append(value, length);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Packed values back as the Key=Value text, nothing for an unknown key or data that ends inside a value
|
||||
static std::optional<std::string> UnpackOverlayValues(std::string_view data)
|
||||
{
|
||||
std::string text;
|
||||
|
||||
while (!data.empty())
|
||||
{
|
||||
const auto index = static_cast<uint8_t>(data.front());
|
||||
data.remove_prefix(1);
|
||||
|
||||
if (index >= std::size(overlayIniKeys))
|
||||
return std::nullopt;
|
||||
|
||||
const auto& key = overlayIniKeys[index];
|
||||
|
||||
size_t size = 1;
|
||||
if (key.type == OverlayValueType::Color)
|
||||
size = 3;
|
||||
else if (key.type == OverlayValueType::Float)
|
||||
size = sizeof(float);
|
||||
else if (key.type == OverlayValueType::Text && !data.empty())
|
||||
size += static_cast<uint8_t>(data.front());
|
||||
|
||||
if (data.size() < size)
|
||||
return std::nullopt;
|
||||
|
||||
const auto bytes = data.substr(0, size);
|
||||
data.remove_prefix(size);
|
||||
|
||||
auto byte = [&](size_t i) { return static_cast<uint32_t>(static_cast<uint8_t>(bytes[i])); };
|
||||
|
||||
std::string value;
|
||||
|
||||
switch (key.type)
|
||||
{
|
||||
case OverlayValueType::Bool:
|
||||
value = byte(0) != 0 ? "true" : "false";
|
||||
break;
|
||||
|
||||
case OverlayValueType::Index:
|
||||
value = std::to_string(byte(0));
|
||||
break;
|
||||
|
||||
case OverlayValueType::Color:
|
||||
value = std::format("{:#08x}", (byte(0) << 16) | (byte(1) << 8) | byte(2));
|
||||
break;
|
||||
|
||||
case OverlayValueType::Float:
|
||||
{
|
||||
float number;
|
||||
std::memcpy(&number, bytes.data(), sizeof(number));
|
||||
value = std::format("{}", number);
|
||||
break;
|
||||
}
|
||||
|
||||
case OverlayValueType::Text:
|
||||
value = bytes.substr(1);
|
||||
break;
|
||||
}
|
||||
|
||||
text += std::format("{}={};", OverlayCodeKey(key.name), value);
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
std::string Config::ExportOverlaySettings()
|
||||
{
|
||||
WriteOverlaySettings();
|
||||
|
||||
std::string data;
|
||||
|
||||
for (size_t i = 0; i < std::size(overlayIniKeys); i++)
|
||||
{
|
||||
const char* value = ini.GetValue("Menu", overlayIniKeys[i].name, "auto");
|
||||
|
||||
if (value == nullptr || lstrcmpiA(value, "auto") == 0)
|
||||
continue;
|
||||
|
||||
data += static_cast<char>(i);
|
||||
PackOverlayValue(data, overlayIniKeys[i].type, value);
|
||||
}
|
||||
|
||||
return std::string(overlayCodePrefix) + Base64Encode(data);
|
||||
}
|
||||
|
||||
bool Config::ImportOverlaySettings(std::string_view code)
|
||||
{
|
||||
while (!code.empty() && std::isspace((unsigned char) code.front()))
|
||||
code.remove_prefix(1);
|
||||
|
||||
while (!code.empty() && std::isspace((unsigned char) code.back()))
|
||||
code.remove_suffix(1);
|
||||
|
||||
if (!code.starts_with(overlayCodePrefix))
|
||||
return false;
|
||||
|
||||
code.remove_prefix(overlayCodePrefix.size());
|
||||
|
||||
auto data = Base64Decode(code);
|
||||
if (!data.has_value())
|
||||
return false;
|
||||
|
||||
auto text = UnpackOverlayValues(data.value());
|
||||
if (!text.has_value())
|
||||
return false;
|
||||
|
||||
return ApplyOverlaySettings(text.value());
|
||||
}
|
||||
|
||||
bool Config::ApplyOverlaySettings(std::string_view text)
|
||||
{
|
||||
// Known keys only, their values are checked as the ini's are
|
||||
std::vector<std::pair<const char*, std::string>> values;
|
||||
std::istringstream entries { std::string(text) };
|
||||
std::string entry;
|
||||
|
||||
while (std::getline(entries, entry, ';'))
|
||||
{
|
||||
const auto separator = entry.find('=');
|
||||
if (separator == std::string::npos)
|
||||
continue;
|
||||
|
||||
const std::string_view name = std::string_view(entry).substr(0, separator);
|
||||
std::string value = entry.substr(separator + 1);
|
||||
|
||||
// A font by its name in the Windows fonts folder, not a path on the computer it was copied from
|
||||
if (name == OverlayCodeKey("FpsOverlayFont") && value.find_first_of("\\/:") != std::string::npos)
|
||||
continue;
|
||||
|
||||
for (const auto& key : overlayIniKeys)
|
||||
{
|
||||
if (OverlayCodeKey(key.name) == name)
|
||||
{
|
||||
values.emplace_back(key.name, std::move(value));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A code with text but no known key is broken or from a later version
|
||||
if (values.empty() && !text.empty())
|
||||
return false;
|
||||
|
||||
// What the code doesn't list goes back to its default
|
||||
for (const auto& key : overlayIniKeys)
|
||||
ini.SetValue("Menu", key.name, "auto");
|
||||
|
||||
for (const auto& [key, value] : values)
|
||||
ini.SetValue("Menu", key, value.c_str());
|
||||
|
||||
ReadOverlaySettings(true);
|
||||
LOG_INFO("Applied {} FPS overlay settings", values.size());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Config::SaveIni()
|
||||
{
|
||||
// Upscalers
|
||||
@@ -1326,20 +1696,14 @@ bool Config::SaveIni()
|
||||
ini.SetValue("Menu", "FpsCycleShortcutKey",
|
||||
GetIntValue(Instance()->FpsCycleShortcutKey.value_for_config(), setting > 0).c_str());
|
||||
|
||||
ini.SetValue("Menu", "FpsOverlayPos", GetIntValue(Instance()->FpsOverlayPosition.value_for_config()).c_str());
|
||||
ini.SetValue("Menu", "FpsOverlayType", GetIntValue(Instance()->FpsOverlayType.value_for_config()).c_str());
|
||||
ini.SetValue("Menu", "FpsOverlayHorizontal",
|
||||
GetBoolValue(Instance()->FpsOverlayHorizontal.value_for_config()).c_str());
|
||||
ini.SetValue("Menu", "FpsOverlayShowRatio",
|
||||
GetBoolValue(Instance()->FpsOverlayShowRatio.value_for_config()).c_str());
|
||||
ini.SetValue("Menu", "FpsOverlayAlpha", GetFloatValue(Instance()->FpsOverlayAlpha.value_for_config()).c_str());
|
||||
ini.SetValue("Menu", "FpsScale", GetFloatValue(Instance()->FpsScale.value_for_config()).c_str());
|
||||
WriteOverlaySettings();
|
||||
|
||||
ini.SetValue("Menu", "Rounding", GetFloatValue(Instance()->MenuRounding.value_for_config()).c_str());
|
||||
ini.SetValue("Menu", "FontSize", GetFloatValue(Instance()->FontSize.value_for_config()).c_str());
|
||||
ini.SetValue("Menu", "TTFFontPath",
|
||||
wstring_to_string(Instance()->TTFFontPath.value_for_config_or(L"auto")).c_str());
|
||||
|
||||
ini.SetValue("Menu", "LightTheme", GetBoolValue(Instance()->LightTheme.value_for_config()).c_str());
|
||||
ini.SetValue("Menu", "OverlaysUseTheme", GetBoolValue(Instance()->OverlaysUseTheme.value_for_config()).c_str());
|
||||
ini.SetValue("Menu", "AccentColorR", GetFloatValue(Instance()->MenuAccentColorR.value_for_config()).c_str());
|
||||
ini.SetValue("Menu", "AccentColorG", GetFloatValue(Instance()->MenuAccentColorG.value_for_config()).c_str());
|
||||
ini.SetValue("Menu", "AccentColorB", GetFloatValue(Instance()->MenuAccentColorB.value_for_config()).c_str());
|
||||
|
||||
+66
-3
@@ -154,9 +154,20 @@ enum FpsOverlayPos : uint32_t
|
||||
FpsOverlayPos_TopRight,
|
||||
FpsOverlayPos_BottomLeft,
|
||||
FpsOverlayPos_BottomRight,
|
||||
FpsOverlayPos_TopCenter,
|
||||
FpsOverlayPos_BottomCenter,
|
||||
FpsOverlayPos_Custom, // FpsOverlayPosX/Y
|
||||
FpsOverlayPos_COUNT,
|
||||
};
|
||||
|
||||
enum FpsOverlayShadow : uint32_t
|
||||
{
|
||||
FpsOverlayShadow_Off,
|
||||
FpsOverlayShadow_Shadow,
|
||||
FpsOverlayShadow_Outline,
|
||||
FpsOverlayShadow_COUNT,
|
||||
};
|
||||
|
||||
enum FpsOverlay : uint32_t
|
||||
{
|
||||
FpsOverlay_JustFPS,
|
||||
@@ -165,10 +176,20 @@ enum FpsOverlay : uint32_t
|
||||
FpsOverlay_DetailedGraph,
|
||||
FpsOverlay_Full,
|
||||
FpsOverlay_FullGraph,
|
||||
FpsOverlay_ReflexTimings,
|
||||
FpsOverlay_LowLatencyTimings,
|
||||
FpsOverlay_Custom, // FpsOverlayRows and FpsOverlayFpsRow
|
||||
FpsOverlay_COUNT,
|
||||
};
|
||||
|
||||
// What the FPS row shows with the FPS
|
||||
enum FpsOverlayFpsRow : uint32_t
|
||||
{
|
||||
FpsOverlayFpsRow_JustFps, // Only the number
|
||||
FpsOverlayFpsRow_FrameTime, // The API, frame time, frame generation and upscaler
|
||||
FpsOverlayFpsRow_Average, // The API, average FPS, frame generation and upscaler
|
||||
FpsOverlayFpsRow_COUNT,
|
||||
};
|
||||
|
||||
// Output scaling downscaler
|
||||
enum class Scaler : uint32_t
|
||||
{
|
||||
@@ -363,14 +384,43 @@ class Config
|
||||
CustomOptional<FpsOverlayPos> FpsOverlayPosition { FpsOverlayPos_TopLeft };
|
||||
/// 0 Only FPS, 1 +Avg FPS & Upscaler info 2 +Frame Time,
|
||||
/// 3 +Upscaler Time, 4 +Frame Time Graph, 5 +Upscaler Time Graph
|
||||
/// 6 +Reflex timings
|
||||
CustomOptional<FpsOverlay> FpsOverlayType { FpsOverlay_JustFPS };
|
||||
/// 6 +Low latency timings, 7 Custom rows
|
||||
CustomOptional<FpsOverlay> FpsOverlayType { FpsOverlay_FullGraph };
|
||||
CustomOptional<int> FpsShortcutKey { VK_PRIOR };
|
||||
CustomOptional<int> FpsCycleShortcutKey { VK_NEXT };
|
||||
CustomOptional<bool> FpsOverlayHorizontal { false };
|
||||
CustomOptional<bool> FpsOverlayShowRatio { false };
|
||||
CustomOptional<bool> FpsOverlayCompactLabels { false };
|
||||
// Rows of the Custom overlay type in their order, comma separated keys, hidden ones start with -.
|
||||
// No value means the default overlay type's rows
|
||||
CustomOptional<std::string, NoDefault> FpsOverlayRows;
|
||||
CustomOptional<FpsOverlayFpsRow> FpsOverlayFpsRowType { FpsOverlayFpsRow_Average }; // Of the Custom type
|
||||
CustomOptional<float, NoDefault> FpsOverlayRowSpacing; // No value means the default spacing
|
||||
// Milliseconds the values that change every frame are kept, 0 updates them every frame
|
||||
CustomOptional<float> FpsOverlayUpdateInterval { 0.0f };
|
||||
CustomOptional<float> FpsOverlayAlpha { 0.4f };
|
||||
CustomOptional<float, NoDefault> FpsScale; // No value means same as MenuScale
|
||||
CustomOptional<float> FpsOverlayGraphHeight { 1.0f };
|
||||
// Position for FpsOverlayPos_Custom, 0.0 left/top to 1.0 right/bottom
|
||||
CustomOptional<float> FpsOverlayPosX { 0.0f };
|
||||
CustomOptional<float> FpsOverlayPosY { 0.0f };
|
||||
// Font file in the Windows fonts folder or a full path, no value means the menu font
|
||||
CustomOptional<std::string, NoDefault> FpsOverlayFont;
|
||||
CustomOptional<float, NoDefault> FpsOverlayFontSize; // No value means the menu font size
|
||||
CustomOptional<float> FpsOverlayLetterSpacing { 0.0f };
|
||||
// Labels are the names like FPS:, values their numbers
|
||||
CustomOptional<float> FpsOverlayLabelScale { 1.0f };
|
||||
CustomOptional<float> FpsOverlayValueScale { 1.0f };
|
||||
// 0xRRGGBB, used when the overlays don't use the theme colors
|
||||
CustomOptional<uint32_t> FpsOverlayLabelColor { 0xD9D9D9 };
|
||||
CustomOptional<uint32_t> FpsOverlayValueColor { 0xFFFFFF };
|
||||
CustomOptional<uint32_t> FpsOverlayGraphColor { 0x00FF00 };
|
||||
CustomOptional<uint32_t> FpsOverlayBGColor { 0x000000 };
|
||||
CustomOptional<FpsOverlayShadow> FpsOverlayShadowType { FpsOverlayShadow_Shadow };
|
||||
CustomOptional<float> FpsOverlayShadowStrength { 0.5f };
|
||||
CustomOptional<uint32_t> FpsOverlayShadowColor { 0x000000 };
|
||||
CustomOptional<float> FpsOverlayRounding { 4.0f };
|
||||
CustomOptional<float> MenuRounding { 2.0f };
|
||||
CustomOptional<bool> UseHQFont { true };
|
||||
CustomOptional<bool> DisableSplash { false };
|
||||
CustomOptional<float> FontSize { 14.0f };
|
||||
@@ -728,6 +778,15 @@ class Config
|
||||
|
||||
std::vector<std::string> GetConfigLog();
|
||||
|
||||
// The FPS overlay's settings that differ from the defaults as a code, to keep them outside the ini and share
|
||||
// them without a file
|
||||
std::string ExportOverlaySettings();
|
||||
bool ImportOverlaySettings(std::string_view code);
|
||||
|
||||
// A code's settings as text, also a preset: Key=Value separated by ';', keys without the FpsOverlay prefix.
|
||||
// What it doesn't list goes back to the default.
|
||||
bool ApplyOverlaySettings(std::string_view text);
|
||||
|
||||
static Config* Instance();
|
||||
|
||||
private:
|
||||
@@ -739,6 +798,10 @@ class Config
|
||||
|
||||
bool Reload(std::filesystem::path iniPath);
|
||||
|
||||
// The FPS overlay's keys of the Menu section: read when loading the ini, or assigned for a pasted code
|
||||
void ReadOverlaySettings(bool assign);
|
||||
void WriteOverlaySettings();
|
||||
|
||||
std::optional<std::string> readString(std::string section, std::string key, bool lowercase = false);
|
||||
std::optional<std::wstring> readWString(std::string section, std::string key, bool lowercase = false);
|
||||
std::optional<float> readFloat(std::string section, std::string key);
|
||||
|
||||
+1315
-235
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user