diff --git a/Bloxstrap/App.xaml b/Bloxstrap/App.xaml
index ae4bfac8..592254c4 100644
--- a/Bloxstrap/App.xaml
+++ b/Bloxstrap/App.xaml
@@ -34,6 +34,7 @@
+
diff --git a/Bloxstrap/App.xaml.cs b/Bloxstrap/App.xaml.cs
index 373c7eec..881d3837 100644
--- a/Bloxstrap/App.xaml.cs
+++ b/Bloxstrap/App.xaml.cs
@@ -236,6 +236,36 @@ protected override void OnStartup(StartupEventArgs e)
State.Load();
FastFlags.Load();
+ //List tests = new()
+ //{
+ // "UI.FullscreenTitlebarDelay == null || true",
+ // "UI.FullscreenTitlebarDelay == null && true",
+ // "UI.FullscreenTitlebarDelay == null || false",
+ // "UI.FullscreenTitlebarDelay == null && false",
+ // "UI.FullscreenTitlebarDelay == null && false || true",
+ // "UI.FullscreenTitlebarDelay == null && true || false",
+ // "false && false || true",
+ // "(false && false) || true",
+ // "false && (false || true)",
+ // "FFlagDisableNewIGMinDUA != True",
+ // "FStringDebugFlagState == 'hi'",
+ // "FStringDebugFlagState == 'wassup'",
+ // "FStringDebugFlagState == FStringDebugFlagState",
+ // "FStringDebugFlagState != FStringDebugFlagState",
+ // "FLogNetwork == 7",
+ // "FLogNetwork > 9",
+ // "FLogNetwork < 9"
+ //};
+
+
+ //foreach (string test in tests)
+ //{
+ // bool result = new FlexParser(test).Evaluate();
+ // App.Logger.WriteLine(LOG_IDENT, $"'{test}' evaluated to {result}");
+ //}
+
+ //Debugger.Break();
+
if (!Locale.SupportedLocales.ContainsKey(Settings.Prop.Locale))
{
Settings.Prop.Locale = "nil";
diff --git a/Bloxstrap/Enums/FlexTokenType.cs b/Bloxstrap/Enums/FlexTokenType.cs
new file mode 100644
index 00000000..822210a2
--- /dev/null
+++ b/Bloxstrap/Enums/FlexTokenType.cs
@@ -0,0 +1,28 @@
+namespace Bloxstrap.Enums
+{
+ public enum FlexTokenType
+ {
+ // data types
+ NULL,
+ NUMBER,
+ BOOL,
+ STRING,
+ FLAG,
+
+ // comparison operators
+ COMPARE_EQ,
+ COMPARE_NEQ,
+ COMPARE_GT,
+ COMPARE_LT,
+ COMPARE_GEQ,
+ COMPARE_LEQ,
+
+ // boolean logic operators
+ LOGIC_AND,
+ LOGIC_OR,
+
+ // yes, "bracket" is technically not the correct term, don't care
+ BRACKET_OPEN,
+ BRACKET_CLOSE
+ }
+}
diff --git a/Bloxstrap/Exceptions/FlexParseException.cs b/Bloxstrap/Exceptions/FlexParseException.cs
new file mode 100644
index 00000000..7776f141
--- /dev/null
+++ b/Bloxstrap/Exceptions/FlexParseException.cs
@@ -0,0 +1,14 @@
+namespace Bloxstrap.Exceptions
+{
+ internal class FlexParseException : Exception
+ {
+ public FlexParseException(string expression, string message, string? position = null)
+ : base($"Invalid syntax encountered when parsing '{expression}' at position {position ?? "EOF"} ({message})") { }
+
+ public FlexParseException(string expression, string message, int position)
+ : this(expression, message, position.ToString()) { }
+
+ public FlexParseException(string expression, string message, FlexToken? token)
+ : this(expression, message, token?.Position.ToString()) { }
+ }
+}
diff --git a/Bloxstrap/FastFlagManager.cs b/Bloxstrap/FastFlagManager.cs
index a444b80f..1be885a9 100644
--- a/Bloxstrap/FastFlagManager.cs
+++ b/Bloxstrap/FastFlagManager.cs
@@ -1,6 +1,4 @@
-using Bloxstrap.Enums.FlagPresets;
-
-namespace Bloxstrap
+namespace Bloxstrap
{
public class FastFlagManager : JsonManager>
{
@@ -12,146 +10,13 @@ public class FastFlagManager : JsonManager>
public bool Changed => !OriginalProp.SequenceEqual(Prop);
- public static IReadOnlyDictionary PresetFlags = new Dictionary
- {
- { "Network.Log", "FLogNetwork" },
-
-#if DEBUG
- { "HTTP.Log", "DFLogHttpTraceLight" },
-
- { "HTTP.Proxy.Enable", "DFFlagDebugEnableHttpProxy" },
- { "HTTP.Proxy.Address.1", "DFStringDebugPlayerHttpProxyUrl" },
- { "HTTP.Proxy.Address.2", "DFStringHttpCurlProxyHostAndPort" },
- { "HTTP.Proxy.Address.3", "DFStringHttpCurlProxyHostAndPortForExternalUrl" },
-#endif
-
- { "Rendering.Framerate", "DFIntTaskSchedulerTargetFps" },
- { "Rendering.ManualFullscreen", "FFlagHandleAltEnterFullscreenManually" },
- { "Rendering.DisableScaling", "DFFlagDisableDPIScale" },
- { "Rendering.MSAA", "FIntDebugForceMSAASamples" },
- { "Rendering.DisablePostFX", "FFlagDisablePostFx" },
- { "Rendering.ShadowIntensity", "FIntRenderShadowIntensity" },
-
- { "Rendering.Mode.D3D11", "FFlagDebugGraphicsPreferD3D11" },
- { "Rendering.Mode.D3D10", "FFlagDebugGraphicsPreferD3D11FL10" },
-
- { "Rendering.Lighting.Voxel", "DFFlagDebugRenderForceTechnologyVoxel" },
- { "Rendering.Lighting.ShadowMap", "FFlagDebugForceFutureIsBrightPhase2" },
- { "Rendering.Lighting.Future", "FFlagDebugForceFutureIsBrightPhase3" },
-
- { "Rendering.TextureQuality.OverrideEnabled", "DFFlagTextureQualityOverrideEnabled" },
- { "Rendering.TextureQuality.Level", "DFIntTextureQualityOverride" },
- { "Rendering.TerrainTextureQuality", "FIntTerrainArraySliceSize" },
-
- { "UI.Hide", "DFIntCanHideGuiGroupId" },
- { "UI.FontSize", "FIntFontSizePadding" },
-#if DEBUG
- { "UI.FlagState", "FStringDebugShowFlagState" },
-#endif
-
- { "UI.FullscreenTitlebarDelay", "FIntFullscreenTitleBarTriggerDelayMillis" },
-
- { "UI.Menu.Style.V2Rollout", "FIntNewInGameMenuPercentRollout3" },
- { "UI.Menu.Style.EnableV4.1", "FFlagEnableInGameMenuControls" },
- { "UI.Menu.Style.EnableV4.2", "FFlagEnableInGameMenuModernization" },
- { "UI.Menu.Style.EnableV4Chrome", "FFlagEnableInGameMenuChrome" },
-
- { "UI.Menu.Style.ABTest.1", "FFlagEnableMenuControlsABTest" },
- { "UI.Menu.Style.ABTest.2", "FFlagEnableV3MenuABTest3" },
- { "UI.Menu.Style.ABTest.3", "FFlagEnableInGameMenuChromeABTest3" }
- };
-
- public static IReadOnlyDictionary RenderingModes => new Dictionary
- {
- { RenderingMode.Default, "None" },
- { RenderingMode.D3D11, "D3D11" },
- { RenderingMode.D3D10, "D3D10" },
- };
-
- public static IReadOnlyDictionary LightingModes => new Dictionary
- {
- { LightingMode.Default, "None" },
- { LightingMode.Voxel, "Voxel" },
- { LightingMode.ShadowMap, "ShadowMap" },
- { LightingMode.Future, "Future" }
- };
-
- public static IReadOnlyDictionary MSAAModes => new Dictionary
- {
- { MSAAMode.Default, null },
- { MSAAMode.x1, "1" },
- { MSAAMode.x2, "2" },
- { MSAAMode.x4, "4" }
- };
+ public readonly FFlagPresets PresetConfig;
- public static IReadOnlyDictionary TextureQualityLevels => new Dictionary
- {
- { TextureQuality.Default, null },
- { TextureQuality.Level0, "0" },
- { TextureQuality.Level1, "1" },
- { TextureQuality.Level2, "2" },
- { TextureQuality.Level3, "3" },
- };
-
- // this is one hell of a dictionary definition lmao
- // since these all set the same flags, wouldn't making this use bitwise operators be better?
- public static IReadOnlyDictionary> IGMenuVersions => new Dictionary>
+ public FastFlagManager()
{
- {
- InGameMenuVersion.Default,
- new Dictionary
- {
- { "V2Rollout", null },
- { "EnableV4", null },
- { "EnableV4Chrome", null },
- { "ABTest", null }
- }
- },
-
- {
- InGameMenuVersion.V1,
- new Dictionary
- {
- { "V2Rollout", "0" },
- { "EnableV4", "False" },
- { "EnableV4Chrome", "False" },
- { "ABTest", "False" }
- }
- },
-
- {
- InGameMenuVersion.V2,
- new Dictionary
- {
- { "V2Rollout", "100" },
- { "EnableV4", "False" },
- { "EnableV4Chrome", "False" },
- { "ABTest", "False" }
- }
- },
-
- {
- InGameMenuVersion.V4,
- new Dictionary
- {
- { "V2Rollout", "0" },
- { "EnableV4", "True" },
- { "EnableV4Chrome", "False" },
- { "ABTest", "False" }
- }
- },
-
- {
- InGameMenuVersion.V4Chrome,
- new Dictionary
- {
- { "V2Rollout", "0" },
- { "EnableV4", "True" },
- { "EnableV4Chrome", "True" },
- { "ABTest", "False" }
- }
- }
- };
+ PresetConfig = JsonSerializer.Deserialize(File.ReadAllText("C:\\Users\\pizzaboxer\\Documents\\Projects\\Bloxstrap\\PrototypeSchema.json"));
+ Debug.WriteLine(PresetConfig);
+ }
// all fflags are stored as strings
// to delete a flag, set the value as null
@@ -170,7 +35,7 @@ public void SetValue(string key, object? value)
{
if (Prop.ContainsKey(key))
{
- if (key == Prop[key].ToString())
+ if (value.ToString() == Prop[key].ToString())
return;
App.Logger.WriteLine(LOG_IDENT, $"Changing of '{key}' from '{Prop[key]}' to '{value}' is pending");
@@ -184,10 +49,16 @@ public void SetValue(string key, object? value)
}
}
- // this returns null if the fflag doesn't exist
+ ///
+ /// Returns null if the flag has not been set
+ ///
+ ///
+ ///
public string? GetValue(string key)
{
- // check if we have an updated change for it pushed first
+ if (PresetConfig.Flags.ContainsKey(key))
+ key = PresetConfig.Flags[key];
+
if (Prop.TryGetValue(key, out object? value) && value is not null)
return value.ToString();
@@ -196,37 +67,25 @@ public void SetValue(string key, object? value)
public void SetPreset(string prefix, object? value)
{
- foreach (var pair in PresetFlags.Where(x => x.Key.StartsWith(prefix)))
+ foreach (var pair in PresetConfig.Flags.Where(x => x.Key.StartsWith(prefix)))
SetValue(pair.Value, value);
}
- public void SetPresetEnum(string prefix, string target, object? value)
+ public bool CheckPresetValue(string prefix, string value)
{
- foreach (var pair in PresetFlags.Where(x => x.Key.StartsWith(prefix)))
- {
- if (pair.Key.StartsWith($"{prefix}.{target}"))
- SetValue(pair.Value, value);
- else
- SetValue(pair.Value, null);
- }
- }
+ var presets = PresetConfig.Flags.Where(x => x.Key.StartsWith(prefix));
- public string? GetPreset(string name) => GetValue(PresetFlags[name]);
-
- public T GetPresetEnum(IReadOnlyDictionary mapping, string prefix, string value) where T : Enum
- {
- foreach (var pair in mapping)
+ foreach (var preset in presets)
{
- if (pair.Value == "None")
- continue;
-
- if (GetPreset($"{prefix}.{pair.Value}") == value)
- return pair.Key;
+ if (GetValue(preset.Value) != value)
+ return false;
}
- return mapping.First().Key;
+ return true;
}
+ public bool CheckPresetValue(KeyValuePair entry) => CheckPresetValue(entry.Key, entry.Value);
+
public override void Save()
{
// convert all flag values to strings before saving
@@ -248,7 +107,7 @@ public override void Load(bool alertFailure = true)
OriginalProp = new(Prop);
// TODO - remove when activity tracking has been revamped
- if (GetPreset("Network.Log") != "7")
+ if (GetValue("Network.Log") != "7")
SetPreset("Network.Log", "7");
}
}
diff --git a/Bloxstrap/FlexParser.cs b/Bloxstrap/FlexParser.cs
new file mode 100644
index 00000000..3e340edc
--- /dev/null
+++ b/Bloxstrap/FlexParser.cs
@@ -0,0 +1,249 @@
+namespace Bloxstrap
+{
+ ///
+ /// FastFlag Expression Parser
+ ///
+ public class FlexParser
+ {
+ private readonly string _expression;
+
+ private readonly List _tokens = new();
+
+ private int _tokenPos = 0;
+
+ private static readonly Dictionary _staticTokenMap = new()
+ {
+ { "null", FlexTokenType.NULL },
+
+ { "true", FlexTokenType.BOOL },
+ { "false", FlexTokenType.BOOL },
+
+ { "==", FlexTokenType.COMPARE_EQ },
+ { "!=", FlexTokenType.COMPARE_NEQ },
+
+ { ">", FlexTokenType.COMPARE_GT },
+ { "<", FlexTokenType.COMPARE_LT },
+ { ">=", FlexTokenType.COMPARE_GEQ },
+ { "<=", FlexTokenType.COMPARE_LEQ },
+
+ { "&&", FlexTokenType.LOGIC_AND },
+ { "||", FlexTokenType.LOGIC_OR },
+
+ { "(", FlexTokenType.BRACKET_OPEN },
+ { ")", FlexTokenType.BRACKET_CLOSE }
+ };
+
+ private static readonly Dictionary _regexTokenMap = new()
+ {
+ { @"^\d+", FlexTokenType.NUMBER },
+ { @"^('[^']+')", FlexTokenType.STRING },
+ { @"^([a-zA-Z0-9_\.]+)", FlexTokenType.FLAG }
+ };
+
+ public FlexParser(string expression)
+ {
+ _expression = expression;
+
+ Tokenize();
+ }
+
+ public bool Evaluate() => EvaluateExpression();
+
+ private void Tokenize()
+ {
+ int position = 0;
+
+ while (position < _expression.Length)
+ {
+ string exprSlice = _expression.Substring(position);
+
+ if (exprSlice[0] == ' ')
+ {
+ position++;
+ continue;
+ }
+
+ string exprSliceLower = exprSlice.ToLowerInvariant();
+
+ var mapMatch = _staticTokenMap.FirstOrDefault(x => exprSliceLower.StartsWith(x.Key));
+
+ if (mapMatch.Key is null)
+ {
+ bool matched = false;
+
+ foreach (var entry in _regexTokenMap)
+ {
+ var match = Regex.Match(exprSlice, entry.Key);
+
+ if (match.Success)
+ {
+ matched = true;
+
+ string phrase = match.Groups[match.Groups.Count > 1 ? 1 : 0].Value;
+ _tokens.Add(new(entry.Value, phrase, position));
+ position += phrase.Length;
+
+ break;
+ }
+ }
+
+ if (!matched)
+ throw new FlexParseException(_expression, "unknown identifier", position);
+ }
+ else
+ {
+ _tokens.Add(new(mapMatch.Value, mapMatch.Key, position));
+ position += mapMatch.Key.Length;
+ }
+ }
+ }
+
+ ///
+ /// The brackets in this example expression are instances of subexpressions: "[FLogNetwork == 7] || [false]"
+ ///
+ ///
+ ///
+ private bool EvaluateSubExpression()
+ {
+ var token = _tokens.ElementAtOrDefault(_tokenPos++);
+
+ if (token?.Type == FlexTokenType.FLAG)
+ {
+ var compToken = _tokens.ElementAtOrDefault(_tokenPos++);
+
+ if (compToken is null || compToken.Value is null || !compToken.IsComparisonOperator)
+ throw new FlexParseException(_expression, "expected comparison operator", compToken);
+
+ var valueToken = _tokens.ElementAtOrDefault(_tokenPos++);
+
+ if (valueToken is null || !valueToken.IsDataType)
+ throw new FlexParseException(_expression, "expected data", valueToken);
+
+ string? flagValue = token.GetActualValue();
+
+ if (compToken.IsInequalityOperator)
+ {
+ if (flagValue is null || valueToken.Value is null)
+ return false;
+
+ if (valueToken.Type != FlexTokenType.NUMBER)
+ throw new FlexParseException(_expression, "expected integer", valueToken);
+
+ if (!long.TryParse(flagValue, out long intFlagValue))
+ return false;
+
+ long intValue = long.Parse(valueToken.Value);
+
+ switch (compToken.Type)
+ {
+ case FlexTokenType.COMPARE_GT:
+ return intFlagValue > intValue;
+ case FlexTokenType.COMPARE_LT:
+ return intFlagValue < intValue;
+ case FlexTokenType.COMPARE_GEQ:
+ return intFlagValue >= intValue;
+ case FlexTokenType.COMPARE_LEQ:
+ return intFlagValue <= intValue;
+ }
+ }
+ else
+ {
+ if (valueToken.Type == FlexTokenType.NULL)
+ return flagValue is null;
+
+ bool result = string.Compare(flagValue, valueToken.GetActualValue(), StringComparison.InvariantCultureIgnoreCase) == 0;
+
+ if (compToken.Type == FlexTokenType.COMPARE_EQ)
+ return result;
+ else
+ return !result;
+ }
+ }
+ else if (token?.Type == FlexTokenType.BOOL)
+ {
+ return token.BoolValue;
+ }
+
+ return false;
+ }
+
+ private bool EvaluateExpression(int finalPos = 0)
+ {
+ bool result = false;
+
+ if (finalPos == 0)
+ finalPos = _tokens.Count;
+
+ while (_tokenPos < finalPos)
+ {
+ var token = _tokens.ElementAtOrDefault(_tokenPos);
+
+ if (token is null)
+ break;
+
+ if (token.Type == FlexTokenType.FLAG || token.Type == FlexTokenType.BOOL)
+ {
+ result = EvaluateSubExpression();
+ }
+ else if (token.Type == FlexTokenType.BRACKET_OPEN)
+ {
+ var closeBracketToken = _tokens.Find(x => x.Type == FlexTokenType.BRACKET_CLOSE);
+
+ if (closeBracketToken is null)
+ throw new FlexParseException(_expression, "expected closing bracket");
+
+ _tokenPos++;
+
+ result = EvaluateExpression(_tokens.IndexOf(closeBracketToken));
+
+ _tokenPos++;
+ }
+ else
+ {
+ throw new FlexParseException(_expression, "identifier was unexpected here", token);
+ }
+
+ var nextToken = _tokens.ElementAtOrDefault(_tokenPos++);
+
+ if (nextToken is null)
+ break;
+
+ if (!nextToken.IsLogicToken)
+ throw new FlexParseException(_expression, "expected boolean operator", nextToken);
+
+ if (nextToken.Type == FlexTokenType.LOGIC_AND)
+ {
+ if (result)
+ {
+ continue;
+ }
+ else
+ {
+ int bracketNesting = 0;
+
+ while (_tokenPos < finalPos)
+ {
+ token = _tokens[_tokenPos++];
+
+ if (token.Type == FlexTokenType.BRACKET_OPEN)
+ bracketNesting++;
+ else if (token.Type == FlexTokenType.BRACKET_CLOSE)
+ bracketNesting--;
+ else if (bracketNesting == 0 && token.Type == FlexTokenType.LOGIC_OR)
+ break;
+ }
+
+ if (bracketNesting != 0)
+ throw new FlexParseException(_expression, "unclosed bracket");
+ }
+ }
+ else if (nextToken.Type == FlexTokenType.LOGIC_OR && result)
+ {
+ break;
+ }
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/Bloxstrap/Installer.cs b/Bloxstrap/Installer.cs
index 9e2f6f29..19e0ad81 100644
--- a/Bloxstrap/Installer.cs
+++ b/Bloxstrap/Installer.cs
@@ -483,14 +483,14 @@ public static void HandleUpgrade()
{
App.FastFlags.SetValue("FIntDebugForceMSAASamples", null);
- if (App.FastFlags.GetPreset("UI.Menu.Style.DisableV2") is not null)
+ if (App.FastFlags.GetValue("UI.Menu.Style.DisableV2") is not null)
App.FastFlags.SetPreset("UI.Menu.Style.ABTest", false);
}
if (Utilities.CompareVersions(existingVer, "2.5.3") == VersionComparison.LessThan)
{
- string? val = App.FastFlags.GetPreset("UI.Menu.Style.EnableV4.1");
- if (App.FastFlags.GetPreset("UI.Menu.Style.EnableV4.2") != val)
+ string? val = App.FastFlags.GetValue("UI.Menu.Style.EnableV4.1");
+ if (App.FastFlags.GetValue("UI.Menu.Style.EnableV4.2") != val)
App.FastFlags.SetPreset("UI.Menu.Style.EnableV4.2", val);
}
@@ -513,7 +513,7 @@ public static void HandleUpgrade()
if (App.Settings.Prop.BootstrapperStyle == BootstrapperStyle.ClassicFluentDialog)
App.Settings.Prop.BootstrapperStyle = BootstrapperStyle.FluentDialog;
- _ = int.TryParse(App.FastFlags.GetPreset("Rendering.Framerate"), out int x);
+ _ = int.TryParse(App.FastFlags.GetValue("Rendering.Framerate"), out int x);
if (x == 0)
App.FastFlags.SetPreset("Rendering.Framerate", null);
}
diff --git a/Bloxstrap/Models/APIs/Config/FFlagPreset.cs b/Bloxstrap/Models/APIs/Config/FFlagPreset.cs
new file mode 100644
index 00000000..b5d669bc
--- /dev/null
+++ b/Bloxstrap/Models/APIs/Config/FFlagPreset.cs
@@ -0,0 +1,139 @@
+namespace Bloxstrap.Models.APIs.Config
+{
+ // technically an entity, whatever
+ public class FFlagPreset
+ {
+ public string Title { get; set; } = null!;
+
+ public string? Description { get; set; }
+
+ public string? HelpLink { get; set; }
+
+ public string Type { get; set; } = null!;
+
+ ///
+ /// Specific to TextBox and Toggle
+ ///
+ public Dictionary? Apply { get; set; }
+
+ #region ComboBox
+ // Data
+ public Dictionary>? Options { get; set; }
+
+ // Frontend
+ public List? ComboBoxEntries => Options?.Keys.Prepend("Common.Default").ToList();
+
+ public string ComboBoxSelection
+ {
+ get
+ {
+ if (Options is null || ComboBoxEntries is null)
+ return "";
+
+ foreach (var optionEntry in Options)
+ {
+ bool matches = true;
+
+ foreach (var flagEntry in optionEntry.Value)
+ {
+ if (matches && !App.FastFlags.CheckPresetValue(flagEntry))
+ matches = false;
+ }
+
+ if (matches)
+ return optionEntry.Key;
+ }
+
+ return ComboBoxEntries[0];
+ }
+
+ set
+ {
+ if (Options is null || ComboBoxEntries is null)
+ throw new InvalidOperationException();
+
+ if (value == ComboBoxEntries[0])
+ {
+ // get all flags that this preset sets
+ var flags = Options.Values.SelectMany(x => x.Keys).Distinct().ToList();
+
+ foreach (string flag in flags)
+ App.FastFlags.SetPreset(flag, null);
+ }
+ else
+ {
+ foreach (var entry in Options[value])
+ App.FastFlags.SetPreset(entry.Key, entry.Value);
+ }
+ }
+ }
+ #endregion
+
+ #region TextBox
+ // TODO: filtering (i dont know how tf thats gonna work)
+
+ // Data
+ public string? InputFilter { get; set; }
+
+ public string? Subject { get; set; }
+
+ public string? DefaultValue { get; set; }
+
+ // Frontend
+ public string TextBoxValue
+ {
+ get
+ {
+ if (Subject is null || DefaultValue is null)
+ return "";
+
+ return App.FastFlags.GetValue(Subject) ?? DefaultValue;
+ }
+
+ set
+ {
+ if (Apply is null || DefaultValue is null)
+ throw new InvalidOperationException();
+
+ if (InputFilter is not null && !Regex.IsMatch(value, InputFilter))
+ {
+ value = TextBoxValue;
+ return;
+ }
+
+ foreach (var entry in Apply)
+ {
+ if (value == DefaultValue)
+ App.FastFlags.SetPreset(entry.Key, null);
+ else
+ App.FastFlags.SetPreset(entry.Key, String.Format(entry.Value, value));
+ }
+ }
+ }
+ #endregion
+
+ #region Toggle
+ public string? EnabledIf { get; set; }
+
+ public bool ToggleEnabled
+ {
+ get
+ {
+ if (EnabledIf is null)
+ return false;
+
+ return new FlexParser(EnabledIf).Evaluate();
+ }
+
+ set
+ {
+ if (Apply is null)
+ throw new InvalidOperationException();
+
+ foreach (var entry in Apply)
+ App.FastFlags.SetPreset(entry.Key, value ? entry.Value : null);
+ }
+ }
+ #endregion
+ }
+}
diff --git a/Bloxstrap/Models/APIs/Config/FFlagPresets.cs b/Bloxstrap/Models/APIs/Config/FFlagPresets.cs
new file mode 100644
index 00000000..0f708598
--- /dev/null
+++ b/Bloxstrap/Models/APIs/Config/FFlagPresets.cs
@@ -0,0 +1,11 @@
+namespace Bloxstrap.Models.APIs.Config
+{
+ public class FFlagPresets
+ {
+ public string Version { get; set; } = null!;
+
+ public Dictionary Flags { get; set; } = null!;
+
+ public Dictionary> Presets { get; set; } = null!;
+ }
+}
diff --git a/Bloxstrap/Models/APIs/Config/TranslationPatch.cs b/Bloxstrap/Models/APIs/Config/TranslationPatch.cs
new file mode 100644
index 00000000..33434599
--- /dev/null
+++ b/Bloxstrap/Models/APIs/Config/TranslationPatch.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Bloxstrap.Models.APIs.Config
+{
+ internal class TranslationPatch
+ {
+ }
+}
diff --git a/Bloxstrap/Models/FlexToken.cs b/Bloxstrap/Models/FlexToken.cs
new file mode 100644
index 00000000..2d36fe31
--- /dev/null
+++ b/Bloxstrap/Models/FlexToken.cs
@@ -0,0 +1,56 @@
+namespace Bloxstrap.Models
+{
+ public class FlexToken
+ {
+ public FlexTokenType Type;
+ public string? Value;
+ public int Position;
+
+ public bool IsDataType =>
+ Type == FlexTokenType.FLAG
+ || Type == FlexTokenType.NUMBER
+ || Type == FlexTokenType.BOOL
+ || Type == FlexTokenType.STRING
+ || Type == FlexTokenType.NULL;
+
+ public bool IsLogicToken =>
+ Type == FlexTokenType.LOGIC_AND
+ || Type == FlexTokenType.LOGIC_OR;
+
+ public bool IsInequalityOperator =>
+ Type == FlexTokenType.COMPARE_GT
+ || Type == FlexTokenType.COMPARE_LT
+ || Type == FlexTokenType.COMPARE_GEQ
+ || Type == FlexTokenType.COMPARE_LEQ;
+
+ public bool IsComparisonOperator =>
+ IsInequalityOperator
+ || Type == FlexTokenType.COMPARE_EQ
+ || Type == FlexTokenType.COMPARE_NEQ;
+
+ public bool BoolValue => Type == FlexTokenType.BOOL && Value == "true";
+
+ public FlexToken(FlexTokenType type, string? value, int position)
+ {
+ Type = type;
+ Value = value;
+ Position = position;
+ }
+
+ public string? GetActualValue()
+ {
+ if (Value is null)
+ return null;
+
+ if (Type == FlexTokenType.STRING)
+ return Value.Substring(1, Value.Length - 2);
+
+ if (Type == FlexTokenType.FLAG)
+ return App.FastFlags.GetValue(Value);
+
+ return Value;
+ }
+
+ public override string ToString() => $"{Type}: {Value}";
+ }
+}
diff --git a/Bloxstrap/UI/Converters/StringResourceConverter.cs b/Bloxstrap/UI/Converters/StringResourceConverter.cs
new file mode 100644
index 00000000..df3c0a1c
--- /dev/null
+++ b/Bloxstrap/UI/Converters/StringResourceConverter.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Data;
+using System.Xml.Linq;
+
+namespace Bloxstrap.UI.Converters
+{
+ class StringResourceConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ return Strings.ResourceManager.GetStringSafe((string)value);
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotSupportedException();
+ }
+ }
+}
diff --git a/Bloxstrap/UI/Elements/Settings/Pages/FastFlagEditorPage.xaml.cs b/Bloxstrap/UI/Elements/Settings/Pages/FastFlagEditorPage.xaml.cs
index cad94ba8..e0b2ccb6 100644
--- a/Bloxstrap/UI/Elements/Settings/Pages/FastFlagEditorPage.xaml.cs
+++ b/Bloxstrap/UI/Elements/Settings/Pages/FastFlagEditorPage.xaml.cs
@@ -45,7 +45,7 @@ private void ReloadList()
_fastFlagList.Clear();
- var presetFlags = FastFlagManager.PresetFlags.Values;
+ var presetFlags = App.FastFlags.PresetConfig.Flags.Values;
foreach (var pair in App.FastFlags.Prop.OrderBy(x => x.Key))
{
@@ -141,7 +141,7 @@ private void AddSingle(string name, string value)
bool refresh = false;
- if (!_showPresets && FastFlagManager.PresetFlags.Values.Contains(name))
+ if (!_showPresets && App.FastFlags.PresetConfig.Flags.Values.Contains(name))
{
TogglePresetsButton.IsChecked = true;
_showPresets = true;
diff --git a/Bloxstrap/UI/Elements/Settings/Pages/FastFlagsPage.xaml b/Bloxstrap/UI/Elements/Settings/Pages/FastFlagsPage.xaml
index 9af9ff0b..14d2606e 100644
--- a/Bloxstrap/UI/Elements/Settings/Pages/FastFlagsPage.xaml
+++ b/Bloxstrap/UI/Elements/Settings/Pages/FastFlagsPage.xaml
@@ -21,149 +21,87 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Bloxstrap/UI/ViewModels/Settings/FastFlagsViewModel.cs b/Bloxstrap/UI/ViewModels/Settings/FastFlagsViewModel.cs
index 1fc04e0c..9e095b83 100644
--- a/Bloxstrap/UI/ViewModels/Settings/FastFlagsViewModel.cs
+++ b/Bloxstrap/UI/ViewModels/Settings/FastFlagsViewModel.cs
@@ -7,7 +7,6 @@
using CommunityToolkit.Mvvm.Input;
using Bloxstrap.UI.Elements.Settings.Pages;
-using Bloxstrap.Enums.FlagPresets;
namespace Bloxstrap.UI.ViewModels.Settings
{
@@ -15,182 +14,30 @@ public class FastFlagsViewModel : NotifyPropertyChangedViewModel
{
private readonly Page _page;
- public FastFlagsViewModel(Page page)
- {
- _page = page;
- }
-
- private void OpenFastFlagEditor()
- {
- if (Window.GetWindow(_page) is INavigationWindow window)
- {
- if (App.State.Prop.ShowFFlagEditorWarning)
- window.Navigate(typeof(FastFlagEditorWarningPage));
- else
- window.Navigate(typeof(FastFlagEditorPage));
- }
- }
-
public ICommand OpenFastFlagEditorCommand => new RelayCommand(OpenFastFlagEditor);
-#if DEBUG
- public Visibility ShowDebugFlags => Visibility.Visible;
-#else
- public Visibility ShowDebugFlags => Visibility.Collapsed;
-#endif
-
public bool UseFastFlagManager
{
get => App.Settings.Prop.UseFastFlagManager;
set => App.Settings.Prop.UseFastFlagManager = value;
}
- public bool HttpRequestLogging
- {
- get => App.FastFlags.GetPreset("HTTP.Log") is not null;
- set => App.FastFlags.SetPreset("HTTP.Log", value ? 12 : null);
- }
-
- public string HttpRequestProxy
- {
- get => App.FastFlags.GetPreset("HTTP.Proxy.Address.1") ?? "";
-
- set
- {
- App.FastFlags.SetPreset("HTTP.Proxy.Enable", String.IsNullOrEmpty(value) ? null : true);
- App.FastFlags.SetPreset("HTTP.Proxy.Address", String.IsNullOrEmpty(value) ? null : value);
- }
- }
-
- public string StateOverlayFlags
- {
- get => App.FastFlags.GetPreset("UI.FlagState") ?? "";
- set => App.FastFlags.SetPreset("UI.FlagState", String.IsNullOrEmpty(value) ? null : value);
- }
-
- public int FramerateLimit
- {
- get => int.TryParse(App.FastFlags.GetPreset("Rendering.Framerate"), out int x) ? x : 0;
- set => App.FastFlags.SetPreset("Rendering.Framerate", value == 0 ? null : value);
- }
-
- public IReadOnlyDictionary MSAALevels => FastFlagManager.MSAAModes;
-
- public MSAAMode SelectedMSAALevel
- {
- get => MSAALevels.FirstOrDefault(x => x.Value == App.FastFlags.GetPreset("Rendering.MSAA")).Key;
- set => App.FastFlags.SetPreset("Rendering.MSAA", MSAALevels[value]);
- }
-
- public IReadOnlyDictionary RenderingModes => FastFlagManager.RenderingModes;
-
- public RenderingMode SelectedRenderingMode
- {
- get => App.FastFlags.GetPresetEnum(RenderingModes, "Rendering.Mode", "True");
- set => App.FastFlags.SetPresetEnum("Rendering.Mode", RenderingModes[value], "True");
- }
-
- public bool FixDisplayScaling
- {
- get => App.FastFlags.GetPreset("Rendering.DisableScaling") == "True";
- set => App.FastFlags.SetPreset("Rendering.DisableScaling", value ? "True" : null);
- }
+ public Dictionary> Presets => App.FastFlags.PresetConfig.Presets;
- public IReadOnlyDictionary> IGMenuVersions => FastFlagManager.IGMenuVersions;
-
- public InGameMenuVersion SelectedIGMenuVersion
- {
- get
- {
- // yeah this kinda sucks
- foreach (var version in IGMenuVersions)
- {
- bool flagsMatch = true;
-
- foreach (var flag in version.Value)
- {
- foreach (var presetFlag in FastFlagManager.PresetFlags.Where(x => x.Key.StartsWith($"UI.Menu.Style.{flag.Key}")))
- {
- if (App.FastFlags.GetValue(presetFlag.Value) != flag.Value)
- flagsMatch = false;
- }
- }
-
- if (flagsMatch)
- return version.Key;
- }
-
- return IGMenuVersions.First().Key;
- }
-
- set
- {
- foreach (var flag in IGMenuVersions[value])
- App.FastFlags.SetPreset($"UI.Menu.Style.{flag.Key}", flag.Value);
- }
- }
-
- public IReadOnlyDictionary LightingModes => FastFlagManager.LightingModes;
-
- public LightingMode SelectedLightingMode
- {
- get => App.FastFlags.GetPresetEnum(LightingModes, "Rendering.Lighting", "True");
- set => App.FastFlags.SetPresetEnum("Rendering.Lighting", LightingModes[value], "True");
- }
-
- public bool FullscreenTitlebarDisabled
- {
- get => int.TryParse(App.FastFlags.GetPreset("UI.FullscreenTitlebarDelay"), out int x) && x > 5000;
- set => App.FastFlags.SetPreset("UI.FullscreenTitlebarDelay", value ? "3600000" : null);
- }
-
- public bool GuiHidingEnabled
- {
- get => App.FastFlags.GetPreset("UI.Hide") == "32380007";
- set => App.FastFlags.SetPreset("UI.Hide", value ? "32380007" : null);
+ public FastFlagsViewModel(Page page)
+ {
+ _page = page;
}
- public IReadOnlyDictionary TextureQualities => FastFlagManager.TextureQualityLevels;
-
- public TextureQuality SelectedTextureQuality
+ private void OpenFastFlagEditor()
{
- get => TextureQualities.Where(x => x.Value == App.FastFlags.GetPreset("Rendering.TextureQuality.Level")).FirstOrDefault().Key;
- set
+ if (Window.GetWindow(_page) is INavigationWindow window)
{
- if (value == TextureQuality.Default)
- {
- App.FastFlags.SetPreset("Rendering.TextureQuality", null);
- }
+ if (App.State.Prop.ShowFFlagEditorWarning)
+ window.Navigate(typeof(FastFlagEditorWarningPage));
else
- {
- App.FastFlags.SetPreset("Rendering.TextureQuality.OverrideEnabled", "True");
- App.FastFlags.SetPreset("Rendering.TextureQuality.Level", TextureQualities[value]);
- }
+ window.Navigate(typeof(FastFlagEditorPage));
}
}
-
- public bool DisablePostFX
- {
- get => App.FastFlags.GetPreset("Rendering.DisablePostFX") == "True";
- set => App.FastFlags.SetPreset("Rendering.DisablePostFX", value ? "True" : null);
- }
-
- public bool DisablePlayerShadows
- {
- get => App.FastFlags.GetPreset("Rendering.ShadowIntensity") == "0";
- set => App.FastFlags.SetPreset("Rendering.ShadowIntensity", value ? "0" : null);
- }
-
- public int? FontSize
- {
- get => int.TryParse(App.FastFlags.GetPreset("UI.FontSize"), out int x) ? x : 1;
- set => App.FastFlags.SetPreset("UI.FontSize", value == 1 ? null : value);
- }
-
- public bool DisableTerrainTextures
- {
- get => App.FastFlags.GetPreset("Rendering.TerrainTextureQuality") == "0";
- set => App.FastFlags.SetPreset("Rendering.TerrainTextureQuality", value ? "0" : null);
- }
}
}