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); - } } }